import { spawnSync, type SpawnSyncReturns } from "node:child_process";
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { dirname } from "node:path";
import type { UniDeskConfig } from "./config";
import { rootPath } from "./config";
import type { RenderedCliResult } from "./output";
import { applyPk01CaddySiteBlock, caddyManagedBlockMarkers } from "./pk01-caddy";
import {
  asRecord,
  booleanField,
  integerField,
  parseOpsApplyOptions,
  parseOpsCommonOptions,
  readYamlRecord,
  recordField,
  stringField,
} from "./platform-infra-ops-library";

const configPath = rootPath("config", "platform-infra", "webterm.yaml");
const serviceName = "webterm";

interface WebtermConfig {
  defaults: { targetId: string };
  runtimeDatabase: RuntimeDatabase;
  sessionStartModes: SessionStartModes;
  autoResumePrompt: AutoResumePrompt;
  browserTerminalHistory: BrowserTerminalHistory;
  browserOutputPolicy: BrowserOutputPolicy;
  browserPerformance: BrowserPerformance;
  targets: WebtermTarget[];
}

interface RuntimeDatabase {
  sourceRef: string;
  userKey: string;
  passwordKey: string;
  databaseKey: string;
  host: string;
  port: number;
}

interface SessionStartModes {
  defaultMode: string;
  options: Array<{ id: string; label: string; command: string }>;
}

interface AutoResumePrompt {
  enabled: boolean;
  prompt: string;
  sendEnter: boolean;
  enterDelayMs: number;
  readiness: { minDelayMs: number; minOutputBytes: number; quietPeriodMs: number; timeoutMs: number };
}

interface BrowserTerminalHistory {
  desktopScrollbackLines: number;
  mobileScrollbackLines: number;
  mobileMaxWidthPx: number;
}

interface BrowserOutputPolicy {
  maxFramesPerSecond: number;
  maxFrameBytes: number;
  compression: { enabled: boolean; thresholdBytes: number; level: number; memLevel: number; concurrencyLimit: number; serverContextTakeover: boolean; clientContextTakeover: boolean };
}

interface BrowserPerformance {
  animationsEnabled: boolean;
  defaultBackgroundMode: "disconnect" | "receive-only" | "low-frequency" | "live";
  defaultMobileTerminalPercent: number;
  backgroundModes: Array<{ id: "disconnect" | "receive-only" | "low-frequency" | "live"; label: string }>;
  deferredOutputMaxBytes: number;
  lowFrequencyRenderIntervalMs: number;
  lowFrequencyRenderJitterMs: number;
  dashboardSampleWindowSeconds: number;
  dashboardRefreshIntervalMs: number;
}

interface WebtermTarget {
  id: string;
  enabled: boolean;
  runtime: {
    mode: "host-docker";
    workDir: string;
    composePath: string;
    composeProject: string;
    serviceName: string;
    containerName: string;
    image: string;
    envFile: string;
    runtimeDataDomain: string;
    runtimeDatabase: RuntimeDatabase;
    hostPort: number;
    containerPort: number;
    privileged: boolean;
    pid: "host" | "container";
    sourceMounts: Array<{ source: string; target: string; readOnly: boolean }>;
    networkSpeedTest: { enabled: boolean; containerName: string; hostPort: number; containerPort: number; publicUrl: string; downloadBytes: number; uploadBytes: number; maxDurationMs: number } | null;
    autoStartSessions: Array<{ id: string; title: string; cwd: string; command: string; cols: number; rows: number; resumePrompt: boolean; fallbackShell: boolean }>;
    sessionStartModes: SessionStartModes;
    autoResumePrompt: AutoResumePrompt;
    browserTerminalHistory: BrowserTerminalHistory;
    browserOutputPolicy: BrowserOutputPolicy;
    browserPerformance: BrowserPerformance;
    environment: Record<string, string>;
  };
  publicExposure: {
    enabled: boolean;
    publicBaseUrl: string;
    management: "pk01" | "shared-public-edge";
    healthPath: string | null;
    websocket: boolean | null;
    dns: { hostname: string; expectedA: string; resolvers: string[] };
    upstream: { host: string; port: number };
    pk01: {
      route: string;
      caddyConfigPath: string;
      caddyServiceName: string;
      responseHeaderTimeoutSeconds: number;
    } | null;
  };
}

export async function runWebtermCommand(config: UniDeskConfig, args: string[]): Promise<Record<string, unknown> | RenderedCliResult> {
  const [action = "plan", ...rest] = args;
  if (action === "plan") {
    const result = plan(parseCommon(rest).targetId);
    return wantsFull(rest) ? result : renderPlan(result);
  }
  if (action === "apply") {
    const options = parseOpsApplyOptions(rest);
    const result = await apply(config, options.targetId, options.confirm);
    return options.full || options.raw ? result : renderApply(result);
  }
  if (action === "status") {
    const options = parseCommon(rest);
    const result = status(options.targetId);
    return options.full || options.raw ? result : renderStatus(result);
  }
  if (action === "validate") {
    const options = parseCommon(rest);
    const result = validate(options.targetId);
    return options.full || options.raw ? result : renderValidate(result);
  }
  if (action === "logs") {
    const options = parseOpsCommonOptions(rest, { stringOptions: ["--tail"] });
    const result = logs(options.targetId, parseLogTail(options.tail));
    return options.full || options.raw ? result : renderLogs(result);
  }
  return {
    ok: false,
    error: "unsupported-webterm-command",
    args,
    help: webtermHelp(),
  };
}

export function webtermHelp(): Record<string, unknown> {
  return {
    command: "platform-infra webterm plan|apply|status|validate|logs",
    examples: [
      "bun scripts/cli.ts platform-infra webterm plan",
      "bun scripts/cli.ts platform-infra webterm apply --dry-run",
      "bun scripts/cli.ts platform-infra webterm apply --confirm",
      "bun scripts/cli.ts platform-infra webterm status",
      "bun scripts/cli.ts platform-infra webterm validate",
      "bun scripts/cli.ts platform-infra webterm logs --target local-7682 --tail 100",
    ],
    configPath,
    description: "Deploy target-scoped host-Docker Web Terminal instances from YAML and optionally expose them through PK01 Caddy HTTPS.",
  };
}

function plan(targetId: string | null): Record<string, unknown> {
  const target = resolveTarget(targetId);
  const compose = renderCompose(target);
  return {
    ok: true,
    service: serviceName,
    configPath,
    target: targetSummary(target),
    compose: {
      path: target.runtime.composePath,
      project: target.runtime.composeProject,
      service: target.runtime.serviceName,
      container: target.runtime.containerName,
      fingerprint: fingerprint(compose),
      hostPort: target.runtime.hostPort,
      containerPort: target.runtime.containerPort,
    },
    publicExposure: exposureSummary(target),
    policy: policyChecks(target, compose),
    next: {
      dryRun: "bun scripts/cli.ts platform-infra webterm apply --dry-run",
      apply: "bun scripts/cli.ts platform-infra webterm apply --confirm",
      status: "bun scripts/cli.ts platform-infra webterm status",
      validate: "bun scripts/cli.ts platform-infra webterm validate",
    },
    valuesPrinted: false,
  };
}

async function apply(config: UniDeskConfig, targetId: string | null, confirm: boolean): Promise<Record<string, unknown>> {
  const target = resolveTarget(targetId);
  const compose = renderCompose(target);
  const policy = policyChecks(target, compose);
  const failedPolicy = policy.filter((item) => item.ok === false);
  if (!confirm) {
    return {
      ok: failedPolicy.length === 0,
      mode: "dry-run",
      target: targetSummary(target),
      compose: { path: target.runtime.composePath, fingerprint: fingerprint(compose), preview: compose },
      publicExposure: exposureSummary(target),
      policy,
      valuesPrinted: false,
    };
  }
  if (failedPolicy.length > 0) {
    return {
      ok: false,
      mode: "policy-blocked",
      target: targetSummary(target),
      policy,
      valuesPrinted: false,
    };
  }

  mkdirSync(dirname(target.runtime.composePath), { recursive: true });
  writeFileSync(target.runtime.composePath, compose, { encoding: "utf8", mode: 0o644 });
  const composeResult = spawnSync("docker", ["compose", "-f", target.runtime.composePath, "-p", target.runtime.composeProject, "up", "-d", "--force-recreate"], {
    cwd: target.runtime.workDir,
    encoding: "utf8",
    env: { ...process.env, ...runtimeDatabaseEnvironment(target.runtime.runtimeDatabase) },
  });
  const localProbe = providerHealthProbeWithRetry(target.runtime.hostPort, 10, 500);
  const caddy = target.publicExposure.enabled && target.publicExposure.pk01 !== null
    ? await applyPk01CaddySiteBlock(
        config,
        serviceName,
        caddyExposure(target),
        renderWebtermCaddySiteBlock({
          hostname: target.publicExposure.dns.hostname,
          upstream: `${target.publicExposure.upstream.host}:${target.publicExposure.upstream.port}`,
          responseHeaderTimeoutSeconds: target.publicExposure.pk01.responseHeaderTimeoutSeconds,
        }),
        caddyManagedBlockMarkers(serviceName),
      )
    : { ok: true, action: target.publicExposure.enabled ? "managed-by-shared-public-edge" : "not-enabled" };
  const publicProbe = target.publicExposure.enabled ? publicHttpProbeWithRetry(target.publicExposure.publicBaseUrl, "/login", 6, 1000) : { ok: true, skipped: true };
  return {
    ok: composeResult.status === 0 && localProbe.ok === true && caddy.ok !== false && publicProbe.ok === true,
    mode: "confirmed",
    target: targetSummary(target),
    compose: {
      path: target.runtime.composePath,
      project: target.runtime.composeProject,
      fingerprint: fingerprint(compose),
      up: commandSummary(composeResult),
    },
    localProbe,
    pk01Caddy: caddy,
    publicProbe,
    valuesPrinted: false,
  };
}

function status(targetId: string | null): Record<string, unknown> {
  const target = resolveTarget(targetId);
  const inspect = spawnSync("docker", ["inspect", target.runtime.containerName], { encoding: "utf8" });
  const ps = spawnSync("docker", ["ps", "--filter", `name=^/${target.runtime.containerName}$`, "--format", "{{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}"], { encoding: "utf8" });
  const localProbe = providerHealthProbe(target.runtime.hostPort);
  const publicProbe = target.publicExposure.enabled ? publicHttpProbe(target.publicExposure.publicBaseUrl, "/login") : { ok: true, skipped: true };
  return {
    ok: ps.status === 0 && ps.stdout.trim().length > 0 && localProbe.ok === true && publicProbe.ok === true,
    target: targetSummary(target),
    compose: {
      path: target.runtime.composePath,
      exists: existsSync(target.runtime.composePath),
      fingerprint: existsSync(target.runtime.composePath) ? fingerprint(readFileSync(target.runtime.composePath, "utf8")) : null,
    },
    container: {
      exists: inspect.status === 0,
      ps: ps.stdout.trim(),
    },
    localProbe,
    publicProbe,
    valuesPrinted: false,
  };
}

function validate(targetId: string | null): Record<string, unknown> {
  const target = resolveTarget(targetId);
  const dns = dnsProbe(target.publicExposure.dns.hostname);
  const localProbe = providerHealthProbe(target.runtime.hostPort);
  const publicProbe = target.publicExposure.enabled ? publicHttpProbe(target.publicExposure.publicBaseUrl, "/login") : { ok: true, skipped: true };
  return {
    ok: localProbe.ok === true && publicProbe.ok === true && dns.addresses.includes(target.publicExposure.dns.expectedA),
    target: targetSummary(target),
    dns,
    localProbe,
    publicProbe,
    valuesPrinted: false,
  };
}

function logs(targetId: string | null, tail: number): Record<string, unknown> {
  const target = resolveTarget(targetId);
  const result = spawnSync("docker", ["logs", "--tail", String(tail), target.runtime.containerName], { encoding: "utf8" });
  const output = `${result.stdout}${result.stderr}`.slice(-20000);
  return {
    ok: result.status === 0,
    target: targetSummary(target),
    tail,
    logs: output,
    truncated: result.stdout.length + result.stderr.length > output.length,
    exitCode: result.status,
    valuesPrinted: false,
  };
}

function parseLogTail(value: unknown): number {
  if (value === null || value === undefined) return 100;
  const tail = Number(value);
  if (!Number.isInteger(tail) || tail < 1 || tail > 1000) throw new Error("--tail must be an integer from 1 to 1000");
  return tail;
}

function readWebtermConfig(): WebtermConfig {
  const yaml = readYamlRecord(configPath, "platform-infra-webterm");
  const defaults = recordField(yaml, "defaults", "webterm");
  const sessionStartModes = parseSessionStartModes(recordField(yaml, "sessionStartModes", "webterm"), "webterm.sessionStartModes");
  const autoResumePrompt = parseAutoResumePrompt(recordField(yaml, "autoResumePrompt", "webterm"), "webterm.autoResumePrompt");
  const browserTerminalHistory = parseBrowserTerminalHistory(recordField(yaml, "browserTerminalHistory", "webterm"), "webterm.browserTerminalHistory");
  const browserOutputPolicy = parseBrowserOutputPolicy(recordField(yaml, "browserOutputPolicy", "webterm"), "webterm.browserOutputPolicy");
  const browserPerformance = parseBrowserPerformance(recordField(yaml, "browserPerformance", "webterm"), "webterm.browserPerformance");
  const runtimeDatabase = parseRuntimeDatabase(recordField(yaml, "runtimeDatabase", "webterm"), "webterm.runtimeDatabase");
  const targetsRaw = yaml.targets;
  if (!Array.isArray(targetsRaw)) throw new Error("webterm.targets must be an array");
  return {
    defaults: { targetId: stringField(defaults, "targetId", "webterm.defaults") },
    runtimeDatabase,
    sessionStartModes,
    autoResumePrompt,
    browserTerminalHistory,
    browserOutputPolicy,
    browserPerformance,
    targets: targetsRaw.map((item, index) => parseTarget(
      asRecord(item, `webterm.targets[${index}]`),
      `webterm.targets[${index}]`,
      sessionStartModes,
      autoResumePrompt,
      browserTerminalHistory,
      browserOutputPolicy,
      browserPerformance,
      runtimeDatabase,
    )),
  };
}

function parseTarget(
  record: Record<string, unknown>,
  path: string,
  sessionStartModes: SessionStartModes,
  autoResumePrompt: AutoResumePrompt,
  browserTerminalHistory: BrowserTerminalHistory,
  browserOutputPolicy: BrowserOutputPolicy,
  browserPerformance: BrowserPerformance,
  runtimeDatabase: RuntimeDatabase,
): WebtermTarget {
  const runtime = recordField(record, "runtime", path);
  const exposure = recordField(record, "publicExposure", path);
  const dns = recordField(exposure, "dns", `${path}.publicExposure`);
  const upstream = typeof exposure.upstream === "string" ? null : recordField(exposure, "upstream", `${path}.publicExposure`);
  const pk01 = exposure.pk01 === undefined ? null : recordField(exposure, "pk01", `${path}.publicExposure`);
  const sharedHostname = exposure.hostname === undefined ? null : stringField(exposure, "hostname", `${path}.publicExposure`);
  const sharedUpstream = exposure.upstream === undefined || typeof exposure.upstream !== "string"
    ? null
    : parseUpstream(stringField(exposure, "upstream", `${path}.publicExposure`), `${path}.publicExposure.upstream`);
  const hostname = sharedHostname ?? stringField(dns, "hostname", `${path}.publicExposure.dns`);
  const parsedUpstream = sharedUpstream ?? parseLegacyUpstream(upstream, `${path}.publicExposure.upstream`);
  const environment = parseEnvironment(recordField(runtime, "environment", `${path}.runtime`), `${path}.runtime.environment`);
  const sourceMounts = parseSourceMounts(runtime.sourceMounts, `${path}.runtime.sourceMounts`);
  const autoStartSessions = parseAutoStartSessions(runtime.autoStartSessions, `${path}.runtime.autoStartSessions`);
  const speedRaw = runtime.networkSpeedTest === undefined ? null : recordField(runtime, "networkSpeedTest", `${path}.runtime`);
  const networkSpeedTest = speedRaw === null ? null : {
    enabled: booleanField(speedRaw, "enabled", `${path}.runtime.networkSpeedTest`),
    containerName: stringField(speedRaw, "containerName", `${path}.runtime.networkSpeedTest`),
    hostPort: portField(speedRaw, "hostPort", `${path}.runtime.networkSpeedTest`),
    containerPort: portField(speedRaw, "containerPort", `${path}.runtime.networkSpeedTest`),
    publicUrl: stringField(speedRaw, "publicUrl", `${path}.runtime.networkSpeedTest`),
    downloadBytes: integerField(speedRaw, "downloadBytes", `${path}.runtime.networkSpeedTest`),
    uploadBytes: integerField(speedRaw, "uploadBytes", `${path}.runtime.networkSpeedTest`),
    maxDurationMs: integerField(speedRaw, "maxDurationMs", `${path}.runtime.networkSpeedTest`),
  };
  const target: WebtermTarget = {
    id: stringField(record, "id", path),
    enabled: booleanField(record, "enabled", path),
    runtime: {
      mode: parseLiteral(stringField(runtime, "mode", `${path}.runtime`), "host-docker", `${path}.runtime.mode`),
      workDir: stringField(runtime, "workDir", `${path}.runtime`),
      composePath: stringField(runtime, "composePath", `${path}.runtime`),
      composeProject: stringField(runtime, "composeProject", `${path}.runtime`),
      serviceName: stringField(runtime, "serviceName", `${path}.runtime`),
      containerName: stringField(runtime, "containerName", `${path}.runtime`),
      image: stringField(runtime, "image", `${path}.runtime`),
      envFile: stringField(runtime, "envFile", `${path}.runtime`),
      runtimeDataDomain: stringField(runtime, "runtimeDataDomain", `${path}.runtime`),
      runtimeDatabase,
      hostPort: portField(runtime, "hostPort", `${path}.runtime`),
      containerPort: portField(runtime, "containerPort", `${path}.runtime`),
      privileged: booleanField(runtime, "privileged", `${path}.runtime`),
      pid: parsePid(stringField(runtime, "pid", `${path}.runtime`), `${path}.runtime.pid`),
      sourceMounts,
      networkSpeedTest,
      autoStartSessions,
      sessionStartModes,
      autoResumePrompt,
      browserTerminalHistory,
      browserOutputPolicy,
      browserPerformance,
      environment,
    },
    publicExposure: {
      enabled: booleanField(exposure, "enabled", `${path}.publicExposure`),
      publicBaseUrl: sharedHostname === null
        ? stringField(exposure, "publicBaseUrl", `${path}.publicExposure`)
        : `https://${sharedHostname}`,
      management: sharedHostname === null ? "pk01" : "shared-public-edge",
      healthPath: sharedHostname === null ? null : stringField(exposure, "healthPath", `${path}.publicExposure`),
      websocket: sharedHostname === null ? null : booleanField(exposure, "websocket", `${path}.publicExposure`),
      dns: {
        hostname,
        expectedA: stringField(dns, "expectedA", `${path}.publicExposure.dns`),
        resolvers: stringArray(dns.resolvers, `${path}.publicExposure.dns.resolvers`),
      },
      upstream: parsedUpstream,
      pk01: pk01 === null ? null : {
        route: stringField(pk01, "route", `${path}.publicExposure.pk01`),
        caddyConfigPath: stringField(pk01, "caddyConfigPath", `${path}.publicExposure.pk01`),
        caddyServiceName: stringField(pk01, "caddyServiceName", `${path}.publicExposure.pk01`),
        responseHeaderTimeoutSeconds: integerField(pk01, "responseHeaderTimeoutSeconds", `${path}.publicExposure.pk01`),
      },
    },
  };
  if (!target.publicExposure.publicBaseUrl.startsWith("https://")) throw new Error(`${path}.publicExposure.publicBaseUrl must be https`);
  if (target.publicExposure.management === "shared-public-edge" && target.publicExposure.pk01 !== null) {
    throw new Error(`${path}.publicExposure cannot declare shared public edge and pk01 together`);
  }
  return target;
}

function parseUpstream(value: string, path: string): { host: string; port: number } {
  const separator = value.lastIndexOf(":");
  if (separator <= 0) throw new Error(`${path} must use host:port`);
  const host = value.slice(0, separator);
  const port = Number(value.slice(separator + 1));
  if (!Number.isInteger(port) || port < 1 || port > 65535) throw new Error(`${path} must use a valid port`);
  return { host, port };
}

function parseLegacyUpstream(value: Record<string, unknown> | null, path: string): { host: string; port: number } {
  if (value === null) throw new Error(`${path} must use one supported shape`);
  return {
    host: stringField(value, "host", path),
    port: portField(value, "port", path),
  };
}

function resolveTarget(targetId: string | null): WebtermTarget {
  const config = readWebtermConfig();
  const id = targetId ?? config.defaults.targetId;
  const target = config.targets.find((item) => item.id === id);
  if (target === undefined) throw new Error(`unknown webterm target: ${id}`);
  return target;
}

function renderCompose(target: WebtermTarget): string {
  const environment = {
    ...target.runtime.environment,
    WEBTERM_DATA_DOMAIN: target.runtime.runtimeDataDomain,
    WEBTERM_DATABASE_URL: "${WEBTERM_DATABASE_URL}",
    AUTO_RESUME_CLI_PROMPT: target.runtime.autoResumePrompt.prompt,
    AUTO_START_SESSIONS_JSON: JSON.stringify(target.runtime.autoStartSessions.map((session) => ({
      ...session,
      command: resumeCommandWithPrompt(session, target.runtime.autoResumePrompt),
    }))),
    SESSION_START_MODES_JSON: JSON.stringify(target.runtime.sessionStartModes.options),
    SESSION_START_DEFAULT_MODE: target.runtime.sessionStartModes.defaultMode,
    AUTO_RESUME_PROMPT_JSON: JSON.stringify(target.runtime.autoResumePrompt),
    BROWSER_TERMINAL_HISTORY_JSON: JSON.stringify(target.runtime.browserTerminalHistory),
    BROWSER_OUTPUT_POLICY_JSON: JSON.stringify(target.runtime.browserOutputPolicy),
    BROWSER_PERFORMANCE_JSON: JSON.stringify(target.runtime.browserPerformance),
    ...(target.runtime.networkSpeedTest?.enabled ? {
      NETWORK_SPEED_TEST_PUBLIC_URL: target.runtime.networkSpeedTest.publicUrl,
      NETWORK_SPEED_TEST_DOWNLOAD_BYTES: String(target.runtime.networkSpeedTest.downloadBytes),
      NETWORK_SPEED_TEST_UPLOAD_BYTES: String(target.runtime.networkSpeedTest.uploadBytes),
      NETWORK_SPEED_TEST_MAX_DURATION_MS: String(target.runtime.networkSpeedTest.maxDurationMs),
    } : {}),
  };
  const envLines = Object.entries(environment)
    .map(([key, value]) => `      ${key}: ${quoteYaml(value)}`)
    .join("\n");
  const volumeLines = target.runtime.sourceMounts
    .map((mount) => `      - ${quoteYaml(`${mount.source}:${mount.target}${mount.readOnly ? ":ro" : ""}`)}`)
    .join("\n");
  const speed = target.runtime.networkSpeedTest;
  const speedTestService = speed?.enabled ? `
  network-speed-test:
    image: ${target.runtime.image}
    container_name: ${speed.containerName}
    restart: unless-stopped
    command: ["node", "src/speed-server.js"]
    ports:
      - "${speed.hostPort}:${speed.containerPort}"
    volumes:
      - "/root/webterm/src:/app/src:ro"
    environment:
      SPEED_TEST_HOST: "0.0.0.0"
      SPEED_TEST_PORT: "${speed.containerPort}"
` : "";
  return `name: ${target.runtime.composeProject}

services:
  ${target.runtime.serviceName}:
    image: ${target.runtime.image}
    container_name: ${target.runtime.containerName}
    restart: unless-stopped
    ports:
      - "${target.runtime.hostPort}:${target.runtime.containerPort}"
    privileged: ${target.runtime.privileged ? "true" : "false"}
    pid: ${target.runtime.pid}
    extra_hosts:
      - "host.docker.internal:host-gateway"
    env_file:
      - ${target.runtime.envFile}
    volumes:
${volumeLines}
    environment:
${envLines}
${speedTestService}`;
}

function resumeCommandWithPrompt(
  session: WebtermTarget["runtime"]["autoStartSessions"][number],
  prompt: AutoResumePrompt,
): string {
  let command = session.command.trim();
  if (session.resumePrompt) {
    if (!/^\s*(?:mycx|oncx)\s+resume\s+\S+\s*$/.test(session.command)) {
      throw new Error(`resumePrompt requires a mycx/oncx resume command: ${session.title}`);
    }
    command = `${command} ${quoteShellArg(prompt.prompt)}`;
  }
  return session.fallbackShell ? `${command}; exec /bin/bash -l` : command;
}

function quoteShellArg(value: string): string {
  return `'${value.replace(/'/g, `'"'"'`)}'`;
}

function policyChecks(target: WebtermTarget, compose: string): Array<Record<string, unknown> & { ok: boolean }> {
  return [
    { name: "enabled", ok: target.enabled, detail: "target must be enabled" },
    { name: "compose-path", ok: target.runtime.composePath.endsWith(".yaml"), detail: target.runtime.composePath },
    { name: "container-name", ok: compose.includes(`container_name: ${target.runtime.containerName}`), detail: target.runtime.containerName },
    { name: "public-https", ok: target.publicExposure.publicBaseUrl.startsWith("https://"), detail: target.publicExposure.publicBaseUrl },
    {
      name: "shared-edge-upstream",
      ok: target.publicExposure.management === "pk01"
        || (target.publicExposure.upstream.host === "127.0.0.1" && target.publicExposure.upstream.port === target.runtime.hostPort),
      detail: `${target.publicExposure.upstream.host}:${target.publicExposure.upstream.port}`,
    },
    {
      name: "shared-edge-websocket",
      ok: target.publicExposure.management === "pk01" || target.publicExposure.websocket === true,
      detail: `websocket=${target.publicExposure.websocket}`,
    },
    { name: "compose-port", ok: compose.includes(`"${target.runtime.hostPort}:${target.runtime.containerPort}"`), detail: `${target.runtime.hostPort}:${target.runtime.containerPort}` },
    { name: "source-mounts-present", ok: target.runtime.sourceMounts.length >= 3, detail: `mounts=${target.runtime.sourceMounts.length}` },
    { name: "network-speed-test", ok: !target.runtime.networkSpeedTest?.enabled || compose.includes(`container_name: ${target.runtime.networkSpeedTest.containerName}`), detail: target.runtime.networkSpeedTest?.enabled ? `${target.runtime.networkSpeedTest.hostPort}:${target.runtime.networkSpeedTest.containerPort}` : "disabled" },
  ];
}

function caddyExposure(target: WebtermTarget): Parameters<typeof applyPk01CaddySiteBlock>[2] {
  if (target.publicExposure.pk01 === null) throw new Error("pk01 exposure is not configured for this target");
  return {
    enabled: target.publicExposure.enabled,
    dns: { hostname: target.publicExposure.dns.hostname },
    frpc: { remotePort: target.publicExposure.upstream.port },
    pk01: target.publicExposure.pk01,
  };
}

function renderWebtermCaddySiteBlock(params: {
  hostname: string;
  upstream: string;
  responseHeaderTimeoutSeconds: number;
}): string {
  return `${params.hostname} {
  reverse_proxy ${params.upstream} {
    flush_interval -1
    stream_close_delay 5m
    transport http {
      response_header_timeout ${params.responseHeaderTimeoutSeconds}s
    }
  }
}`;
}

function targetSummary(target: WebtermTarget): Record<string, unknown> {
  return {
    id: target.id,
    enabled: target.enabled,
    mode: target.runtime.mode,
    workDir: target.runtime.workDir,
    composeProject: target.runtime.composeProject,
    containerName: target.runtime.containerName,
    image: target.runtime.image,
    runtimeDataDomain: target.runtime.runtimeDataDomain,
    runtimeDatabase: {
      sourceRef: target.runtime.runtimeDatabase.sourceRef,
      keys: [target.runtime.runtimeDatabase.userKey, target.runtime.runtimeDatabase.passwordKey, target.runtime.runtimeDatabase.databaseKey],
      valuesPrinted: false,
    },
    autoStartSessions: target.runtime.autoStartSessions.map((session) => session.title),
    sessionStartModes: {
      defaultMode: target.runtime.sessionStartModes.defaultMode,
      options: target.runtime.sessionStartModes.options.map((mode) => mode.id),
    },
    autoResumePrompt: {
      enabled: target.runtime.autoResumePrompt.enabled,
      taggedSessions: target.runtime.autoStartSessions.filter((session) => session.resumePrompt).map((session) => session.title),
      readiness: target.runtime.autoResumePrompt.readiness,
      promptBytes: Buffer.byteLength(target.runtime.autoResumePrompt.prompt),
      sendEnter: target.runtime.autoResumePrompt.sendEnter,
      enterDelayMs: target.runtime.autoResumePrompt.enterDelayMs,
    },
    browserTerminalHistory: target.runtime.browserTerminalHistory,
    browserOutputPolicy: target.runtime.browserOutputPolicy,
    browserPerformance: target.runtime.browserPerformance,
    sourceMounts: target.runtime.sourceMounts.map((mount) => `${mount.source}->${mount.target}${mount.readOnly ? ":ro" : ""}`),
    hostPort: target.runtime.hostPort,
    containerPort: target.runtime.containerPort,
  };
}

function exposureSummary(target: WebtermTarget): Record<string, unknown> {
  return {
    enabled: target.publicExposure.enabled,
    publicBaseUrl: target.publicExposure.publicBaseUrl,
    hostname: target.publicExposure.dns.hostname,
    expectedA: target.publicExposure.dns.expectedA,
    upstream: `${target.publicExposure.upstream.host}:${target.publicExposure.upstream.port}`,
    healthPath: target.publicExposure.healthPath,
    websocket: target.publicExposure.websocket,
    management: target.publicExposure.management,
    pk01Route: target.publicExposure.pk01?.route ?? null,
    marker: target.publicExposure.pk01 === null ? null : caddyManagedBlockMarkers(serviceName).start,
  };
}

function localHttpProbe(port: number, path: string): Record<string, unknown> {
  return httpProbe(`http://127.0.0.1:${port}`, path);
}

function publicHttpProbe(baseUrl: string, path: string): Record<string, unknown> {
  return httpProbe(baseUrl, path);
}

function localHttpProbeWithRetry(port: number, path: string, attempts: number, delayMs: number): Record<string, unknown> {
  return httpProbeWithRetry(() => localHttpProbe(port, path), attempts, delayMs);
}

function providerHealthProbe(port: number): Record<string, unknown> {
  const url = `http://127.0.0.1:${port}/healthz`;
  const result = spawnSync("curl", ["-fsS", "--connect-timeout", "5", "--max-time", "15", url], { encoding: "utf8" });
  let payload: Record<string, unknown> = {};
  try {
    payload = JSON.parse(result.stdout) as Record<string, unknown>;
  } catch {
    payload = {};
  }
  const providers = Number(payload.providers);
  const sessions = Number(payload.sessions);
  return {
    ok: result.status === 0 && payload.ok === true && Number.isInteger(providers) && providers >= 1,
    url,
    authenticated: false,
    providers: Number.isInteger(providers) ? providers : null,
    sessions: Number.isInteger(sessions) ? sessions : null,
    exitCode: result.status,
    error: result.status === 0 && providers < 1 ? "no-provider" : undefined,
    stderrTail: result.status === 0 ? "" : result.stderr.slice(-1000),
  };
}

function providerHealthProbeWithRetry(port: number, attempts: number, delayMs: number): Record<string, unknown> {
  return httpProbeWithRetry(() => providerHealthProbe(port), attempts, delayMs);
}

function publicHttpProbeWithRetry(baseUrl: string, path: string, attempts: number, delayMs: number): Record<string, unknown> {
  return httpProbeWithRetry(() => publicHttpProbe(baseUrl, path), attempts, delayMs);
}

function httpProbeWithRetry(run: () => Record<string, unknown>, attempts: number, delayMs: number): Record<string, unknown> {
  let latest = run();
  for (let attempt = 1; attempt < attempts && latest.ok !== true; attempt += 1) {
    spawnSync("sleep", [(delayMs / 1000).toFixed(3)]);
    latest = run();
  }
  return { ...latest, attempts };
}

function httpProbe(baseUrl: string, path: string): Record<string, unknown> {
  const url = `${baseUrl.replace(/\/+$/u, "")}${path}`;
  const result = spawnSync("curl", ["-fsS", "-L", "--connect-timeout", "5", "--max-time", "15", "-o", "/dev/null", "-w", "%{http_code}", url], { encoding: "utf8" });
  const status = Number(result.stdout.trim());
  return {
    ok: result.status === 0 && Number.isInteger(status) && status >= 200 && status < 500,
    url,
    status: Number.isInteger(status) ? status : null,
    exitCode: result.status,
    stderrTail: result.status === 0 ? "" : result.stderr.slice(-1000),
  };
}

function dnsProbe(hostname: string): Record<string, unknown> & { addresses: string[] } {
  const result = spawnSync("getent", ["hosts", hostname], { encoding: "utf8" });
  const addresses = result.stdout
    .split(/\r?\n/u)
    .map((line) => line.trim().split(/\s+/u)[0] ?? "")
    .filter((value) => /^\d+\.\d+\.\d+\.\d+$/u.test(value));
  return { ok: result.status === 0 && addresses.length > 0, hostname, addresses, exitCode: result.status };
}

function commandSummary(result: SpawnSyncReturns<string>): Record<string, unknown> {
  return {
    exitCode: result.status,
    stdoutTail: result.status === 0 ? result.stdout.slice(-2000) : result.stdout.slice(-4000),
    stderrTail: result.status === 0 ? "" : result.stderr.slice(-4000),
  };
}

function parseCommon(args: string[]): ReturnType<typeof parseOpsCommonOptions> {
  return parseOpsCommonOptions(args);
}

function wantsFull(args: string[]): boolean {
  return args.includes("--full") || args.includes("--raw");
}

function parseEnvironment(record: Record<string, unknown>, path: string): Record<string, string> {
  const result: Record<string, string> = {};
  for (const [key, value] of Object.entries(record)) {
    if (!/^[A-Z_][A-Z0-9_]*$/u.test(key)) throw new Error(`${path}.${key} must be an env key`);
    if (typeof value !== "string") throw new Error(`${path}.${key} must be a string`);
    result[key] = value;
  }
  return result;
}

function parseSourceMounts(value: unknown, path: string): WebtermTarget["runtime"]["sourceMounts"] {
  if (!Array.isArray(value)) throw new Error(`${path} must be an array`);
  return value.map((item, index) => {
    const record = asRecord(item, `${path}[${index}]`);
    return {
      source: stringField(record, "source", `${path}[${index}]`),
      target: stringField(record, "target", `${path}[${index}]`),
      readOnly: booleanField(record, "readOnly", `${path}[${index}]`),
    };
  });
}

function parseAutoStartSessions(value: unknown, path: string): WebtermTarget["runtime"]["autoStartSessions"] {
  if (!Array.isArray(value)) throw new Error(`${path} must be an array`);
  return value.map((item, index) => {
    const itemPath = `${path}[${index}]`;
    const record = asRecord(item, itemPath);
    return {
      id: stringField(record, "id", itemPath),
      title: stringField(record, "title", itemPath),
      cwd: stringField(record, "cwd", itemPath),
      command: stringField(record, "command", itemPath),
      cols: integerField(record, "cols", itemPath),
      rows: integerField(record, "rows", itemPath),
      resumePrompt: booleanField(record, "resumePrompt", itemPath),
      fallbackShell: booleanField(record, "fallbackShell", itemPath),
    };
  });
}

function parseRuntimeDatabase(record: Record<string, unknown>, path: string): RuntimeDatabase {
  return {
    sourceRef: stringField(record, "sourceRef", path),
    userKey: stringField(record, "userKey", path),
    passwordKey: stringField(record, "passwordKey", path),
    databaseKey: stringField(record, "databaseKey", path),
    host: stringField(record, "host", path),
    port: portField(record, "port", path),
  };
}

function runtimeDatabaseEnvironment(config: RuntimeDatabase): Record<string, string> {
  const values: Record<string, string> = {};
  for (const line of readFileSync(config.sourceRef, "utf8").split(/\r?\n/u)) {
    const trimmed = line.trim();
    if (!trimmed || trimmed.startsWith("#")) continue;
    const separator = trimmed.indexOf("=");
    if (separator <= 0) continue;
    values[trimmed.slice(0, separator)] = trimmed.slice(separator + 1).replace(/^['"]|['"]$/gu, "");
  }
  const required = (key: string): string => {
    const value = values[key];
    if (!value) throw new Error(`missing ${key} in ${config.sourceRef}`);
    return value;
  };
  return {
    WEBTERM_DATABASE_URL: `postgres://${encodeURIComponent(required(config.userKey))}:${encodeURIComponent(required(config.passwordKey))}@${config.host}:${config.port}/${encodeURIComponent(required(config.databaseKey))}`,
  };
}

function parseSessionStartModes(record: Record<string, unknown>, path: string): SessionStartModes {
  const defaultMode = stringField(record, "defaultMode", path);
  const optionsRecord = recordField(record, "options", path);
  const options = Object.entries(optionsRecord).map(([id, item]) => {
    const itemPath = `${path}.options.${id}`;
    const option = asRecord(item, itemPath);
    return {
      id,
      label: stringField(option, "label", itemPath),
      command: commandField(option, itemPath),
    };
  });
  if (options.length === 0) throw new Error(`${path}.options must be a non-empty record`);
  if (!options.some((option) => option.id === defaultMode)) throw new Error(`${path}.defaultMode must reference an option id`);
  return { defaultMode, options };
}

function commandField(record: Record<string, unknown>, path: string): string {
  const value = record.command;
  if (typeof value !== "string") throw new Error(`${path}.command must be a string`);
  return value.trim();
}

function parseAutoResumePrompt(record: Record<string, unknown>, path: string): AutoResumePrompt {
  const readiness = recordField(record, "readiness", path);
  return {
    enabled: booleanField(record, "enabled", path),
    prompt: stringField(record, "prompt", path),
    sendEnter: booleanField(record, "sendEnter", path),
    enterDelayMs: integerField(record, "enterDelayMs", path),
    readiness: {
      minDelayMs: integerField(readiness, "minDelayMs", `${path}.readiness`),
      minOutputBytes: integerField(readiness, "minOutputBytes", `${path}.readiness`),
      quietPeriodMs: integerField(readiness, "quietPeriodMs", `${path}.readiness`),
      timeoutMs: integerField(readiness, "timeoutMs", `${path}.readiness`),
    },
  };
}

function parseBrowserTerminalHistory(record: Record<string, unknown>, path: string): BrowserTerminalHistory {
  return {
    desktopScrollbackLines: positiveIntegerField(record, "desktopScrollbackLines", path),
    mobileScrollbackLines: positiveIntegerField(record, "mobileScrollbackLines", path),
    mobileMaxWidthPx: positiveIntegerField(record, "mobileMaxWidthPx", path),
  };
}

function parseBrowserOutputPolicy(record: Record<string, unknown>, path: string): BrowserOutputPolicy {
  const compression = recordField(record, "compression", path);
  return {
    maxFramesPerSecond: positiveIntegerField(record, "maxFramesPerSecond", path),
    maxFrameBytes: positiveIntegerField(record, "maxFrameBytes", path),
    compression: {
      enabled: booleanField(compression, "enabled", `${path}.compression`),
      thresholdBytes: positiveIntegerField(compression, "thresholdBytes", `${path}.compression`),
      level: positiveIntegerField(compression, "level", `${path}.compression`),
      memLevel: positiveIntegerField(compression, "memLevel", `${path}.compression`),
      concurrencyLimit: positiveIntegerField(compression, "concurrencyLimit", `${path}.compression`),
      serverContextTakeover: booleanField(compression, "serverContextTakeover", `${path}.compression`),
      clientContextTakeover: booleanField(compression, "clientContextTakeover", `${path}.compression`),
    },
  };
}

function parseBrowserPerformance(record: Record<string, unknown>, path: string): BrowserPerformance {
  const allowedModes = ["disconnect", "receive-only", "low-frequency", "live"] as const;
  const defaultBackgroundMode = stringField(record, "defaultBackgroundMode", path);
  if (!allowedModes.includes(defaultBackgroundMode as typeof allowedModes[number])) {
    throw new Error(`${path}.defaultBackgroundMode must reference a supported mode`);
  }
  const modesRecord = recordField(record, "backgroundModes", path);
  const backgroundModes = allowedModes.map((id) => ({
    id,
    label: stringField(recordField(modesRecord, id, `${path}.backgroundModes`), "label", `${path}.backgroundModes.${id}`),
  }));
  const unknownModes = Object.keys(modesRecord).filter((id) => !allowedModes.includes(id as typeof allowedModes[number]));
  if (unknownModes.length > 0) throw new Error(`${path}.backgroundModes contains unsupported modes: ${unknownModes.join(", ")}`);
  return {
    animationsEnabled: booleanField(record, "animationsEnabled", path),
    defaultBackgroundMode: defaultBackgroundMode as BrowserPerformance["defaultBackgroundMode"],
    defaultMobileTerminalPercent: integerInRangeField(record, "defaultMobileTerminalPercent", path, 20, 70),
    backgroundModes,
    deferredOutputMaxBytes: positiveIntegerField(record, "deferredOutputMaxBytes", path),
    lowFrequencyRenderIntervalMs: positiveIntegerField(record, "lowFrequencyRenderIntervalMs", path),
    lowFrequencyRenderJitterMs: integerField(record, "lowFrequencyRenderJitterMs", path),
    dashboardSampleWindowSeconds: positiveIntegerField(record, "dashboardSampleWindowSeconds", path),
    dashboardRefreshIntervalMs: positiveIntegerField(record, "dashboardRefreshIntervalMs", path),
  };
}

function positiveIntegerField(record: Record<string, unknown>, key: string, path: string): number {
  const value = integerField(record, key, path);
  if (value <= 0) throw new Error(`${path}.${key} must be positive`);
  return value;
}

function integerInRangeField(record: Record<string, unknown>, key: string, path: string, min: number, max: number): number {
  const value = integerField(record, key, path);
  if (value < min || value > max) throw new Error(`${path}.${key} must be from ${min} to ${max}`);
  return value;
}

function parseLiteral<T extends string>(value: string, expected: T, path: string): T {
  if (value !== expected) throw new Error(`${path} must be ${expected}`);
  return expected;
}

function parsePid(value: string, path: string): "host" | "container" {
  if (value !== "host" && value !== "container") throw new Error(`${path} must be host or container`);
  return value;
}

function portField(record: Record<string, unknown>, key: string, path: string): number {
  const value = integerField(record, key, path);
  if (value < 1 || value > 65535) throw new Error(`${path}.${key} must be a TCP port`);
  return value;
}

function stringArray(value: unknown, path: string): string[] {
  if (!Array.isArray(value) || value.some((item) => typeof item !== "string" || item.length === 0)) throw new Error(`${path} must be an array of strings`);
  return value as string[];
}

function quoteYaml(value: string): string {
  return JSON.stringify(value);
}

function fingerprint(value: string): string {
  const result = spawnSync("sha256sum", { input: value, encoding: "utf8" });
  return `sha256:${(result.stdout.split(/\s+/u)[0] ?? "").trim()}`;
}

function renderPlan(result: Record<string, unknown>): RenderedCliResult {
  const target = rec(result.target);
  const exposure = rec(result.publicExposure);
  const failed = arr(result.policy).filter((item) => item.ok === false);
  const lines = [
    "PLATFORM-INFRA WEBTERM PLAN",
    ...table(["FIELD", "VALUE", "DETAIL", "VALUE"], [
      ["TARGET", str(target.id), "mode", str(target.mode)],
      ["PORT", str(target.hostPort), "container", str(target.containerName)],
      ["IMAGE", str(target.image), "compose", str(rec(result.compose).path)],
      ["PUBLIC", bool(exposure.enabled), "url", str(exposure.publicBaseUrl)],
      ["UPSTREAM", str(exposure.upstream), "pk01", str(exposure.pk01Route)],
      ["POLICY", failed.length === 0 ? "ok" : `failed=${failed.length}`, "valuesPrinted", "false"],
    ]),
    "",
    "NEXT",
    "  dry-run: bun scripts/cli.ts platform-infra webterm apply --dry-run",
    "  apply: bun scripts/cli.ts platform-infra webterm apply --confirm",
    "  status: bun scripts/cli.ts platform-infra webterm status",
  ];
  return rendered(result, "platform-infra webterm plan", lines);
}

function renderApply(result: Record<string, unknown>): RenderedCliResult {
  const target = rec(result.target);
  const compose = rec(result.compose);
  const localProbe = rec(result.localProbe);
  const publicProbe = rec(result.publicProbe);
  const caddy = rec(result.pk01Caddy);
  const lines = [
    "PLATFORM-INFRA WEBTERM APPLY",
    ...table(["TARGET", "PORT", "MODE", "STATUS"], [[str(target.id), str(target.hostPort), str(result.mode), result.ok === false ? "failed" : "ok"]]),
    "",
    "COMPOSE",
    ...table(["PATH", "PROJECT", "UP"], [[str(compose.path), str(target.composeProject), str(rec(compose.up).exitCode, "-")]]),
    "",
    "CHECKS",
    ...table(["CHECK", "OK", "DETAIL"], [
      ["local", bool(localProbe.ok), str(localProbe.url, "-")],
      ["provider", str(localProbe.providers, "-"), `sessions=${str(localProbe.sessions, "-")}`],
      ["pk01Caddy", bool(caddy.ok), str(caddy.action, str(caddy.hostname, "-"))],
      ["public", bool(publicProbe.ok), str(publicProbe.url, "-")],
    ]),
    "",
    "NEXT",
    "  status: bun scripts/cli.ts platform-infra webterm status",
    "  validate: bun scripts/cli.ts platform-infra webterm validate",
  ];
  return rendered(result, "platform-infra webterm apply", lines);
}

function renderStatus(result: Record<string, unknown>): RenderedCliResult {
  const target = rec(result.target);
  const container = rec(result.container);
  const localProbe = rec(result.localProbe);
  const publicProbe = rec(result.publicProbe);
  const lines = [
    "PLATFORM-INFRA WEBTERM STATUS",
    ...table(["TARGET", "PORT", "CONTAINER", "STATUS"], [[str(target.id), str(target.hostPort), str(target.containerName), result.ok === false ? "failed" : "ok"]]),
    "",
    "CONTAINER",
    str(container.ps, "-"),
    "",
    "CHECKS",
    ...table(["CHECK", "OK", "DETAIL"], [
      ["local", bool(localProbe.ok), str(localProbe.url, "-")],
      ["provider", str(localProbe.providers, "-"), `sessions=${str(localProbe.sessions, "-")}`],
      ["public", bool(publicProbe.ok), str(publicProbe.url, "-")],
    ]),
  ];
  return rendered(result, "platform-infra webterm status", lines);
}

function renderValidate(result: Record<string, unknown>): RenderedCliResult {
  const dns = rec(result.dns);
  const localProbe = rec(result.localProbe);
  const publicProbe = rec(result.publicProbe);
  const lines = [
    "PLATFORM-INFRA WEBTERM VALIDATE",
    ...table(["CHECK", "OK", "DETAIL"], [
      ["dns", bool(dns.ok), values(dns.addresses).map(String).join(",") || "-"],
      ["local", bool(localProbe.ok), str(localProbe.url, "-")],
      ["provider", str(localProbe.providers, "-"), `sessions=${str(localProbe.sessions, "-")}`],
      ["public", bool(publicProbe.ok), str(publicProbe.url, "-")],
    ]),
  ];
  return rendered(result, "platform-infra webterm validate", lines);
}

function renderLogs(result: Record<string, unknown>): RenderedCliResult {
  const target = rec(result.target);
  const lines = [
    "PLATFORM-INFRA WEBTERM LOGS",
    ...table(["TARGET", "PORT", "TAIL", "STATUS"], [[str(target.id), str(target.hostPort), str(result.tail), result.ok === false ? "failed" : "ok"]]),
    "",
    str(result.logs, "-"),
  ];
  return rendered(result, "platform-infra webterm logs", lines);
}

function rendered(result: Record<string, unknown>, command: string, lines: string[]): RenderedCliResult {
  return { ok: result.ok !== false, command, renderedText: lines.join("\n"), contentType: "text/plain" };
}

function rec(value: unknown): Record<string, unknown> {
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value as Record<string, unknown> : {};
}

function arr(value: unknown): Record<string, unknown>[] {
  return Array.isArray(value) ? value.map(rec) : [];
}

function values(value: unknown): unknown[] {
  return Array.isArray(value) ? value : [];
}

function str(value: unknown, fallback = "-"): string {
  return value === undefined || value === null || value === "" ? fallback : String(value);
}

function bool(value: unknown): string {
  return value === true ? "yes" : value === false ? "no" : "-";
}

function table(headers: string[], rows: string[][]): string[] {
  const widths = headers.map((header, index) => Math.max(header.length, ...rows.map((row) => row[index]?.length ?? 0)));
  const renderRow = (row: string[]) => row.map((cell, index) => cell.padEnd(widths[index] ?? cell.length)).join("  ").trimEnd();
  return [renderRow(headers), ...rows.map(renderRow)];
}
