/**
 * v0.8 frpc-wrap stepping-stone — PLAN_RELAY_AGENT T6. Fastest path to the café demo: wraps a
 * child `frpc` presenting the shared v0.8 `agentToken`, registering the subdomain, forwarding to
 * 127.0.0.1:3000. EXPLICITLY a stepping-stone — the native mux (T7–T11) replaces it at v0.9.
 *
 * Forwards ONLY to loopback (anti-SSRF). Retired once EnrollMode==='ed25519' (guard below).
 */
import type { AgentConfig } from '../config/agentConfig.js'
import { isLoopbackWsUrl } from '../config/agentConfig.js'
import type { EnrollMode } from '../enroll/pair.js'

export interface FrpScaffold {
  start(): Promise<void>
  stop(): Promise<void>
  onExit(cb: (code: number) => void): void
}

/** Minimal child-process seam so tests inject a fake spawn (no real frpc needed). */
export interface ChildLike {
  on(ev: 'exit', cb: (code: number | null) => void): void
  kill(): void
}
export type SpawnImpl = (cmd: string, args: readonly string[]) => ChildLike

const LOOPBACK_IP = '127.0.0.1'
const LOCAL_PORT = 3000

/** Build the frpc.toml. local_ip is ALWAYS loopback; tls is enabled. */
export function buildFrpcToml(cfg: AgentConfig): string {
  if (!isLoopbackWsUrl(cfg.localTargetUrl)) {
    throw new Error('frpScaffold refuses a non-loopback localTargetUrl (anti-SSRF)')
  }
  const subdomain = cfg.subdomain ?? ''
  return [
    '[common]',
    'tls_enable = true',
    '',
    '[web-terminal]',
    'type = "tcp"',
    `local_ip = "${LOOPBACK_IP}"`,
    `local_port = ${LOCAL_PORT}`,
    `subdomain = "${subdomain}"`,
    '',
  ].join('\n')
}

/** True once the native Ed25519 substrate is active — `run` must NOT wire frpc then. */
export function isFrpRetired(mode: EnrollMode): boolean {
  return mode === 'ed25519'
}

/** Spawn (a mockable) frpc child with a generated config. */
export function spawnFrpc(cfg: AgentConfig, frpcPath: string, spawnImpl: SpawnImpl): FrpScaffold {
  const toml = buildFrpcToml(cfg) // validates loopback before spawning
  void toml
  let child: ChildLike | null = null
  const exitCbs: Array<(code: number) => void> = []
  return {
    async start(): Promise<void> {
      child = spawnImpl(frpcPath, ['-c', 'frpc.toml'])
      child.on('exit', (code) => {
        for (const cb of exitCbs) cb(code ?? 0)
      })
    },
    async stop(): Promise<void> {
      child?.kill()
    },
    onExit(cb: (code: number) => void): void {
      exitCbs.push(cb)
    },
  }
}
