import { describe, test, expect } from 'vitest'
import {
  createFlowController,
  DEFAULT_INITIAL_WINDOW,
  WINDOW_REPLENISH_THRESHOLD,
} from '../mux/flow-control.js'

describe('credit flow control (T4, §4.1)', () => {
  test('canSend true until credit exhausted, then false; never negative', () => {
    const fc = createFlowController()
    fc.registerStream(1, 100)
    expect(fc.canSend(1, 100)).toBe(true)
    fc.consumeSendCredit(1, 100)
    expect(fc.creditFor(1)).toBe(0)
    expect(fc.canSend(1, 1)).toBe(false)
    // Over-consume never drives credit negative.
    fc.consumeSendCredit(1, 50)
    expect(fc.creditFor(1)).toBe(0)
  })

  test('grantCredit unblocks a backpressured stream', () => {
    const fc = createFlowController()
    fc.registerStream(1, 10)
    fc.consumeSendCredit(1, 10)
    expect(fc.canSend(1, 5)).toBe(false)
    fc.grantCredit(1, 20)
    expect(fc.canSend(1, 5)).toBe(true)
    expect(fc.creditFor(1)).toBe(20)
  })

  test('onDelivered returns a replenish amount only once the threshold is crossed', () => {
    const fc = createFlowController()
    fc.registerStream(1, 100) // threshold = 50 bytes
    expect(fc.onDelivered(1, 40)).toBe(0)
    const replenish = fc.onDelivered(1, 20) // cumulative 60 ≥ 50
    expect(replenish).toBe(60)
    // Counter reset after replenish.
    expect(fc.onDelivered(1, 10)).toBe(0)
  })

  test('unknown streamId is deny-by-default: canSend false, creditFor 0', () => {
    const fc = createFlowController()
    expect(fc.canSend(999, 1)).toBe(false)
    expect(fc.creditFor(999)).toBe(0)
    expect(fc.onDelivered(999, 100)).toBe(0)
  })

  test('released streams carry no credit (send-after-close is denied)', () => {
    const fc = createFlowController()
    fc.registerStream(1, 100)
    fc.releaseStream(1)
    expect(fc.canSend(1, 1)).toBe(false)
  })

  test('per-stream isolation: exhausting A does not affect B (no cross-stream starvation)', () => {
    const fc = createFlowController()
    fc.registerStream(1, 50)
    fc.registerStream(2, 50)
    fc.consumeSendCredit(1, 50)
    expect(fc.canSend(1, 1)).toBe(false)
    expect(fc.canSend(2, 50)).toBe(true) // B untouched
  })

  test('exposes the documented constants', () => {
    expect(DEFAULT_INITIAL_WINDOW).toBe(256 * 1024)
    expect(WINDOW_REPLENISH_THRESHOLD).toBe(0.5)
  })
})
