/**
 * Data-plane composition — assembles the REAL term-relay (P1) `createRelayNode` +
 * `createAgentListener` and routes every browser upgrade through the REAL `authorizeUpgrade`
 * adapter (→ our P5 `Authorizer`). The relay-node performs the OPAQUE byte splice (INV2): it never
 * decodes DATA. This is the one composition both the integration test and `main.ts` reuse.
 */
import { createRelayNode, type RelayNode } from 'term-relay/data-plane/relay-node.js'
import {
  createAgentListener,
  type AgentListener,
  type AgentTunnel,
  type MtlsVerifier,
  type RouteRegistrar,
  type TlsServerFactory,
} from 'term-relay/data-plane/agent-listener.js'
import {
  authorizeUpgrade,
  type UpgradeRequest,
  type UpgradeDecision,
} from 'term-relay/data-plane/upgrade.js'
import type { RouteResolver } from 'term-relay/data-plane/subdomain-router.js'
import type { DataPlaneConfig } from 'term-relay/data-plane/config.js'
import type { Authorizer } from 'term-relay/data-plane/authz-port.js'
import type { CapabilityRight } from 'relay-contracts'
import type { ScheduleHandle } from 'term-relay/mux/heartbeat.js'

const RELAY_NODE_ID = 'relay-run-node-0'
const REMOTE_ADDR_SALT = 'relay-run-dev-salt'

/** Build a frozen DataPlaneConfig without touching disk (cert paths are informational here — the
 *  live TLS servers are constructed by main.ts, not from these fields). */
export function makeDataPlaneConfig(overrides: Partial<DataPlaneConfig> = {}): DataPlaneConfig {
  return Object.freeze({
    baseDomain: 'term.localhost',
    bindHost: '127.0.0.1',
    bindPort: 8443,
    agentBindPort: 8444,
    tlsCertPath: '(in-memory)',
    tlsKeyPath: '(in-memory)',
    agentCaCertPath: '(in-memory)',
    agentCaChainPath: '(in-memory)',
    relayNodeId: RELAY_NODE_ID,
    maxFrameBytes: 1024 * 1024,
    initialWindowBytes: 256 * 1024,
    heartbeatIntervalMs: 15_000,
    routeTtlMs: 45_000,
    ...overrides,
  })
}

/** A no-op route registrar (Phase 0 has no Redis routing table; routes live in the listener's Map). */
export function noopRegistrar(): RouteRegistrar {
  return {
    register: async () => {},
    heartbeat: async () => {},
    deregister: async () => {},
  }
}

export interface DataPlaneDeps {
  readonly config: DataPlaneConfig
  readonly authorizer: Authorizer
  readonly resolver: RouteResolver
  readonly mtls: MtlsVerifier
  readonly now: () => number
  readonly requiredRight?: CapabilityRight
  readonly caBundle?: readonly Uint8Array[]
  readonly registrar?: RouteRegistrar
  readonly onTunnel?: (t: AgentTunnel) => void
  /** Inject a manual scheduler to keep heartbeat timers out of unit tests. */
  readonly schedule?: (fn: () => void, ms: number) => ScheduleHandle
  readonly onError?: (e: unknown) => void
  /** Real agent-facing mTLS server factory (main.ts). Omitted in tests → no TLS server started. */
  readonly tlsServerFactory?: TlsServerFactory
}

export interface DataPlane {
  readonly node: RelayNode
  readonly listener: AgentListener
  authorize(req: UpgradeRequest): Promise<UpgradeDecision>
}

export function buildDataPlane(deps: DataPlaneDeps): DataPlane {
  const listener = createAgentListener({
    config: deps.config,
    mtls: deps.mtls,
    registrar: deps.registrar ?? noopRegistrar(),
    onTunnel: deps.onTunnel ?? (() => {}),
    caBundle: deps.caBundle ?? [],
    ...(deps.tlsServerFactory ? { tlsServerFactory: deps.tlsServerFactory } : {}),
    ...(deps.schedule ? { schedule: deps.schedule } : {}),
    ...(deps.onError ? { onError: deps.onError } : {}),
  })

  const authorize = (req: UpgradeRequest): Promise<UpgradeDecision> =>
    authorizeUpgrade(req, {
      authorizer: deps.authorizer,
      resolver: deps.resolver,
      baseDomain: deps.config.baseDomain,
      now: deps.now,
      remoteAddrSalt: REMOTE_ADDR_SALT,
      requiredRight: deps.requiredRight ?? 'attach',
    })

  const node = createRelayNode({ config: deps.config, listener, authorize })
  return { node, listener, authorize }
}

/** In-memory RouteResolver: subdomain label → resolved host (P3's job in production). */
export function memoryRouteResolver(
  hosts: ReadonlyMap<string, { hostId: string; accountId: string }>,
): RouteResolver {
  return {
    resolveSubdomain: async (subdomain) => {
      const h = hosts.get(subdomain)
      return h ? { hostId: h.hostId, accountId: h.accountId, subdomain } : null
    },
  }
}
