import { describe, expect, it } from 'vitest'
import { ReplayError } from '../src/errors.js'
import { SequenceGuard } from '../src/sequence.js'

describe('T7 SequenceGuard (INV13)', () => {
  it('send: next() yields 0,1,2… strictly increasing', () => {
    const g = new SequenceGuard('send')
    expect(g.next()).toBe(0n)
    expect(g.next()).toBe(1n)
    expect(g.next()).toBe(2n)
  })

  it('two guards never share state', () => {
    const a = new SequenceGuard('send')
    const b = new SequenceGuard('send')
    a.next()
    a.next()
    expect(b.next()).toBe(0n)
  })

  it('recv: in-order 0,1,2 accepted; lastAccepted tracks', () => {
    const g = new SequenceGuard('recv')
    g.accept(0n)
    g.accept(1n)
    g.accept(2n)
    expect(g.lastAccepted).toBe(2n)
  })

  it('recv: replay of an accepted seq → ReplayError', () => {
    const g = new SequenceGuard('recv')
    g.accept(0n)
    expect(() => g.accept(0n)).toThrow(ReplayError)
  })

  it('recv: reorder / gap / rewind all → ReplayError', () => {
    const g1 = new SequenceGuard('recv')
    expect(() => g1.accept(2n)).toThrow(ReplayError) // gap from start
    const g2 = new SequenceGuard('recv')
    g2.accept(0n)
    expect(() => g2.accept(2n)).toThrow(ReplayError) // reorder/gap
    const g3 = new SequenceGuard('recv')
    g3.accept(0n)
    g3.accept(1n)
    g3.accept(2n)
    g3.accept(3n)
    g3.accept(4n)
    expect(() => g3.accept(3n)).toThrow(ReplayError) // rewind (5 then 4-style)
  })

  it('bigint boundary: accepts near-u64-max without float error', () => {
    const g = new SequenceGuard('recv')
    // seed just below the boundary by direct successor stepping is impractical; assert range guard.
    expect(() => g.accept(-1n)).toThrow(ReplayError)
    expect(() => g.accept(2n ** 64n)).toThrow(ReplayError)
  })

  it('role misuse is rejected', () => {
    expect(() => new SequenceGuard('recv').next()).toThrow(ReplayError)
    expect(() => new SequenceGuard('send').accept(0n)).toThrow(ReplayError)
  })
})
