import { describe, test, expect } from 'vitest'
import { readdirSync, readFileSync, statSync } from 'node:fs'
import { fileURLToPath } from 'node:url'
import { createMuxSession, type MuxSession } from '../mux/mux-session.js'
import type { MuxOpen } from '../mux/frame-codec.js'

/** Recursively collect .ts source files under a directory. */
function tsFiles(dir: string): string[] {
  const out: string[] = []
  for (const entry of readdirSync(dir)) {
    const full = `${dir}/${entry}`
    if (statSync(full).isDirectory()) out.push(...tsFiles(full))
    else if (entry.endsWith('.ts')) out.push(full)
  }
  return out
}

const MUX_DIR = fileURLToPath(new URL('../mux', import.meta.url))
const DP_DIR = fileURLToPath(new URL('../data-plane', import.meta.url))
const TERMINAL_PARSER_RE = /xterm|ansi|vt100|terminal-parser/i

describe('P1 invariant tripwires (T13)', () => {
  test('INV11: no mux/ or data-plane/ source imports a terminal/ANSI parser', () => {
    const files = [...tsFiles(MUX_DIR), ...tsFiles(DP_DIR)]
    expect(files.length).toBeGreaterThan(0)
    const offenders = files.filter((f) => {
      const src = readFileSync(f, 'utf8')
      const importLines = src.split('\n').filter((l) => /\bimport\b/.test(l) || /\bfrom\b/.test(l))
      return importLines.some((l) => TERMINAL_PARSER_RE.test(l))
    })
    expect(offenders).toEqual([])
  })

  test('INV2: a plaintext marker rides DATA as OPAQUE bytes; never inspected or retained', () => {
    const marker = new TextEncoder().encode('SECRET-PLAINTEXT-MARKER')
    let delivered: Uint8Array | null = null
    let agent!: MuxSession
    const relay = createMuxSession({
      role: 'relay',
      sendWire: (f) => agent.onWire(f),
      onOpen: () => {},
      onData: () => {},
      onClose: () => {},
      onDead: () => {},
      maxFrameBytes: 1024,
      initialWindowBytes: 1_000_000,
      schedule: () => ({ cancel: () => {} }),
    })
    agent = createMuxSession({
      role: 'agent',
      sendWire: (f) => relay.onWire(f),
      onOpen: () => {},
      onData: (_id, payload) => (delivered = payload),
      onClose: () => {},
      onDead: () => {},
      maxFrameBytes: 1024,
      initialWindowBytes: 1_000_000,
      schedule: () => ({ cancel: () => {} }),
    })
    const open: MuxOpen = { streamId: 0, subdomain: 'alice', requestPath: '/', originHeader: 'o', remoteAddrHash: 'h', capabilityTokenRef: 'j' }
    relay.openStream(open).writeData(marker)
    expect(delivered).not.toBeNull()
    expect([...delivered!]).toEqual([...marker]) // byte-identical, opaque
  })
})
