import { randomBytes } from "node:crypto";
import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { dirname, isAbsolute } from "node:path";
import type { UniDeskConfig } from "./config";
import { rootPath } from "./config";
import { startJob } from "./jobs";
import type { RenderedCliResult } from "./output";
import { capture, compactCapture, fingerprintValues, parseEnvFile, readYamlRecord, redactText, resolveRepoPath, shQuote } from "./platform-infra-ops-library";
import { renderSystemdSocketPairConvergeScript } from "./platform-infra-systemd-socket-pair";

const configPath = rootPath("config", "platform-infra", "selfmedia-voice.yaml");
const configLabel = "config/platform-infra/selfmedia-voice.yaml";

interface VoiceTarget {
  id: string;
  role: "gpu-service" | "frps";
  route: string;
  workDir: string;
  composeProject: string;
  cacheDir?: string;
  dataDir?: string;
  sourceDir?: string;
}

interface VoiceBridge {
  targetId: string;
  mode: "systemd-socket-proxyd";
  listenAddress: string;
  listenPort: number;
  upstreamAddress: string;
  upstreamPort: number;
  baseUrl: string;
  socketProxydPath: string;
  socketUnitName: string;
  socketUnitPath: string;
  serviceUnitName: string;
  serviceUnitPath: string;
  probePath: string;
  timeoutSeconds: number;
}

interface VoiceConfig {
  version: number;
  kind: "platform-infra-selfmedia-voice";
  metadata: { id: string; owner: string; relatedIssues: number[]; mdtodo: string };
  rollout: { prerequisite: string; approved: boolean };
  artifacts: Record<"root" | "containerfile" | "api" | "modelInit" | "sourceInit" | "d518Compose" | "nc01Compose" | "frpcTemplate" | "frpsTemplate", string>;
  image: {
    name: string;
    base: { repository: string; tag: string; digest: string; imageId: string };
    source: { repository: string; ref: string; submodules: "recursive"; cache: { retryAttempts: number; retryDelaySeconds: number } };
    modelSource: { provider: "modelscope-git-lfs"; id: string; repository: string; branch: string; ref: string; requireBranchHeadMatch: boolean };
    referenceVoice: { id: string; path: string; sha256: string; promptText: string };
  };
  dependencies: {
    pip: { version: string; indexUrl: string };
    setuptools: { installSpec: string };
    torch: { versionIdentity: string; libraryPath: string };
    torchaudio: { package: string; installSpec: string; versionIdentity: string; abiPolicy: "torch-major-minor-match" };
    openaiWhisper: { installSpec: string; noBuildIsolation: boolean };
    onnxRuntimeGpu: { installSpec: string; indexUrl: string; independentLayer: boolean };
  };
  frp: {
    images: { frps: string; frpc: string };
    secret: { sourceRef: string; sourceKey: string };
    server: { targetId: string; address: string; controlPort: number; workDir: string; composeProject: string };
    tunnel: { targetId: string; proxyName: string; remotePort: number; localAddress: string; localPort: number };
  };
  bridge: VoiceBridge;
  targets: VoiceTarget[];
  runtime: {
    serviceName: string;
    frpcServiceName: string;
    frpsServiceName: string;
    bindAddress: string;
    port: number;
    publicBaseUrl: string;
    inference: {
      device: string;
      fp16: boolean;
      loadTrt: boolean;
      loadVllm: boolean;
      replicas: number;
      workers: number;
      concurrency: "serial";
      referenceVoice: string;
      speed: { min: number; max: number };
      cudaAllocator: string;
      cudaModuleLoading: string;
      speechTokenizerProvider: "CPUExecutionProvider";
    };
    operations: { applyTimeoutSeconds: number };
    smoke: { healthPath: string; speechPath: string; text: string; voice: string; timeoutSeconds: number };
  };
}

interface CommonOptions { full: boolean; raw: boolean }
interface ApplyOptions extends CommonOptions { confirm: boolean; wait: boolean }
interface LogsOptions extends CommonOptions { component: "all" | "voice" | "frpc" | "frps"; lines: number }

export async function runSelfMediaVoiceCommand(config: UniDeskConfig, args: string[]): Promise<Record<string, unknown> | RenderedCliResult> {
  const [action, subaction] = args;
  if (action === undefined || action === "plan") return renderResult("plan", plan(parseCommon(args.slice(action === undefined ? 0 : 1))));
  if (action === "bridge" && (subaction === undefined || subaction === "plan")) return renderResult("bridge plan", bridgePlan(parseCommon(args.slice(subaction === undefined ? 1 : 2))));
  if (action === "bridge" && subaction === "apply") return renderResult("bridge apply", await bridgeApply(config, parseApply(args.slice(2))));
  if (action === "bridge" && subaction === "status") return renderResult("bridge status", await bridgeStatus(config, parseCommon(args.slice(2))));
  if (action === "bridge" && subaction === "validate") return renderResult("bridge validate", await bridgeValidate(config, parseCommon(args.slice(2))));
  if (action === "secret" && subaction === "init") return renderResult("secret init", secretInit(parseApply(args.slice(2))));
  if (action === "apply") return renderResult("apply", await apply(config, parseApply(args.slice(1))));
  if (action === "status") return renderResult("status", await status(config, parseCommon(args.slice(1))));
  if (action === "logs") return renderResult("logs", await logs(config, parseLogs(args.slice(1))));
  if (action === "validate") return renderResult("validate", await validate(config, parseCommon(args.slice(1))));
  if (action === "help" || action === "--help" || action === "-h") return help();
  return { ok: false, action: "platform-infra-selfmedia-voice", error: `unsupported command: ${args.join(" ")}`, next: help().usage };
}

function help(): Record<string, unknown> {
  return {
    ok: true,
    action: "platform-infra-selfmedia-voice-help",
    usage: [
      "bun scripts/cli.ts platform-infra selfmedia-voice plan [--json|--full|--raw]",
      "bun scripts/cli.ts platform-infra selfmedia-voice bridge plan [--json|--full|--raw]",
      "bun scripts/cli.ts platform-infra selfmedia-voice bridge apply --confirm",
      "bun scripts/cli.ts platform-infra selfmedia-voice bridge status [--json|--full|--raw]",
      "bun scripts/cli.ts platform-infra selfmedia-voice bridge validate [--json|--full|--raw]",
      "bun scripts/cli.ts platform-infra selfmedia-voice secret init --confirm",
      "bun scripts/cli.ts platform-infra selfmedia-voice apply --confirm",
      "bun scripts/cli.ts platform-infra selfmedia-voice status [--json|--full|--raw]",
      "bun scripts/cli.ts platform-infra selfmedia-voice logs [--component voice|frpc|frps|all] [--lines 120]",
      "bun scripts/cli.ts platform-infra selfmedia-voice validate [--json|--full|--raw]",
    ],
  };
}

function parseCommon(args: string[]): CommonOptions {
  let full = false;
  let raw = false;
  for (const arg of args) {
    if (arg === "--full") full = true;
    else if (arg === "--raw" || arg === "--json") raw = true;
    else throw new Error(`unsupported option: ${arg}`);
  }
  return { full, raw };
}

function parseApply(args: string[]): ApplyOptions {
  let confirm = false;
  let wait = false;
  const common: string[] = [];
  for (const arg of args) {
    if (arg === "--confirm") confirm = true;
    else if (arg === "--wait") wait = true;
    else common.push(arg);
  }
  return { ...parseCommon(common), confirm, wait };
}

function parseLogs(args: string[]): LogsOptions {
  let component: LogsOptions["component"] = "all";
  let lines = 120;
  const common: string[] = [];
  for (let index = 0; index < args.length; index += 1) {
    const arg = args[index];
    if (arg === "--component") {
      const value = args[index + 1];
      if (value !== "voice" && value !== "frpc" && value !== "frps" && value !== "all") throw new Error("--component must be voice, frpc, frps, or all");
      component = value;
      index += 1;
    } else if (arg === "--lines") {
      lines = Number(args[index + 1]);
      if (!Number.isInteger(lines) || lines < 1 || lines > 500) throw new Error("--lines must be an integer in 1..500");
      index += 1;
    } else common.push(arg);
  }
  return { ...parseCommon(common), component, lines };
}

function readConfig(): VoiceConfig {
  const config = readYamlRecord<VoiceConfig>(configPath, "platform-infra-selfmedia-voice");
  requireInteger(config.version, "version");
  for (const path of Object.values(config.artifacts)) resolveRepoPath(requireString(path, "artifacts path"));
  if (typeof config.rollout.approved !== "boolean") throw new Error(`${configLabel}.rollout.approved must be a boolean`);
  requireString(config.rollout.prerequisite, "rollout.prerequisite");
  requireString(config.image.base.repository, "image.base.repository");
  requireString(config.image.base.tag, "image.base.tag");
  requireSha256(config.image.base.digest, "image.base.digest");
  requireSha256(config.image.base.imageId, "image.base.imageId");
  requireCommit(config.image.source.ref, "image.source.ref");
  if (config.image.source.submodules !== "recursive") throw new Error(`${configLabel}.image.source.submodules must be recursive`);
  requireInteger(config.image.source.cache.retryAttempts, "image.source.cache.retryAttempts");
  requireInteger(config.image.source.cache.retryDelaySeconds, "image.source.cache.retryDelaySeconds");
  if (config.image.modelSource.provider !== "modelscope-git-lfs") throw new Error(`${configLabel}.image.modelSource.provider must be modelscope-git-lfs`);
  requireCommit(config.image.modelSource.ref, "image.modelSource.ref");
  requireString(config.image.modelSource.branch, "image.modelSource.branch");
  if (config.image.modelSource.requireBranchHeadMatch !== true) throw new Error(`${configLabel}.image.modelSource.requireBranchHeadMatch must be true`);
  requireSha256(config.image.referenceVoice.sha256, "image.referenceVoice.sha256", false);
  requireString(config.dependencies.pip.version, "dependencies.pip.version");
  if (new URL(config.dependencies.pip.indexUrl).protocol !== "https:") throw new Error(`${configLabel}.dependencies.pip.indexUrl must use https`);
  requireString(config.dependencies.setuptools.installSpec, "dependencies.setuptools.installSpec");
  requireString(config.dependencies.torch.versionIdentity, "dependencies.torch.versionIdentity");
  requireAbsolute(config.dependencies.torch.libraryPath, "dependencies.torch.libraryPath");
  requireString(config.dependencies.torchaudio.package, "dependencies.torchaudio.package");
  requireString(config.dependencies.torchaudio.installSpec, "dependencies.torchaudio.installSpec");
  requireString(config.dependencies.torchaudio.versionIdentity, "dependencies.torchaudio.versionIdentity");
  if (config.dependencies.torchaudio.abiPolicy !== "torch-major-minor-match") throw new Error(`${configLabel}.dependencies.torchaudio.abiPolicy must be torch-major-minor-match`);
  requireString(config.dependencies.openaiWhisper.installSpec, "dependencies.openaiWhisper.installSpec");
  if (config.dependencies.openaiWhisper.noBuildIsolation !== true) throw new Error(`${configLabel}.dependencies.openaiWhisper.noBuildIsolation must be true`);
  requireString(config.dependencies.onnxRuntimeGpu.installSpec, "dependencies.onnxRuntimeGpu.installSpec");
  if (new URL(config.dependencies.onnxRuntimeGpu.indexUrl).protocol !== "https:") throw new Error(`${configLabel}.dependencies.onnxRuntimeGpu.indexUrl must use https`);
  if (config.dependencies.onnxRuntimeGpu.independentLayer !== true) throw new Error(`${configLabel}.dependencies.onnxRuntimeGpu.independentLayer must be true`);
  requireAbsolute(config.frp.secret.sourceRef, "frp.secret.sourceRef");
  requirePort(config.frp.server.controlPort, "frp.server.controlPort");
  requirePort(config.frp.tunnel.remotePort, "frp.tunnel.remotePort");
  requirePort(config.frp.tunnel.localPort, "frp.tunnel.localPort");
  if (config.bridge.mode !== "systemd-socket-proxyd") throw new Error(`${configLabel}.bridge.mode must be systemd-socket-proxyd`);
  requireString(config.bridge.targetId, "bridge.targetId");
  requireString(config.bridge.listenAddress, "bridge.listenAddress");
  requirePort(config.bridge.listenPort, "bridge.listenPort");
  requireString(config.bridge.upstreamAddress, "bridge.upstreamAddress");
  requirePort(config.bridge.upstreamPort, "bridge.upstreamPort");
  requireAbsolute(config.bridge.socketProxydPath, "bridge.socketProxydPath");
  requireAbsolute(config.bridge.socketUnitPath, "bridge.socketUnitPath");
  requireAbsolute(config.bridge.serviceUnitPath, "bridge.serviceUnitPath");
  requireString(config.bridge.socketUnitName, "bridge.socketUnitName");
  requireString(config.bridge.serviceUnitName, "bridge.serviceUnitName");
  requireString(config.bridge.probePath, "bridge.probePath");
  requireInteger(config.bridge.timeoutSeconds, "bridge.timeoutSeconds");
  if (config.bridge.timeoutSeconds < 1) throw new Error(`${configLabel}.bridge.timeoutSeconds must be positive`);
  if (config.bridge.listenAddress === "0.0.0.0" || config.bridge.listenAddress === "127.0.0.1") throw new Error(`${configLabel}.bridge.listenAddress must be a pod-reachable host address`);
  if (config.bridge.upstreamAddress !== "127.0.0.1") throw new Error(`${configLabel}.bridge.upstreamAddress must stay loopback-only`);
  if (config.bridge.listenPort === config.bridge.upstreamPort) throw new Error(`${configLabel}.bridge.listenPort must differ from bridge.upstreamPort`);
  if (config.bridge.baseUrl !== `http://${config.bridge.listenAddress}:${config.bridge.listenPort}`) throw new Error(`${configLabel}.bridge.baseUrl must match bridge listen address and port`);
  if (config.bridge.socketUnitPath !== `/etc/systemd/system/${config.bridge.socketUnitName}`) throw new Error(`${configLabel}.bridge.socketUnitPath must match bridge.socketUnitName`);
  if (config.bridge.serviceUnitPath !== `/etc/systemd/system/${config.bridge.serviceUnitName}`) throw new Error(`${configLabel}.bridge.serviceUnitPath must match bridge.serviceUnitName`);
  if (config.bridge.socketUnitName.replace(/\.socket$/u, "") !== config.bridge.serviceUnitName.replace(/\.service$/u, "")) throw new Error(`${configLabel}.bridge socket and service units must share a basename`);
  requirePort(config.runtime.port, "runtime.port");
  requireInteger(config.runtime.operations.applyTimeoutSeconds, "runtime.operations.applyTimeoutSeconds");
  if (config.runtime.operations.applyTimeoutSeconds < 60 || config.runtime.operations.applyTimeoutSeconds > 3600) throw new Error(`${configLabel}.runtime.operations.applyTimeoutSeconds must be in 60..3600`);
  if (config.runtime.port !== config.frp.tunnel.localPort) throw new Error(`${configLabel}.runtime.port must match frp.tunnel.localPort`);
  if (new URL(config.runtime.publicBaseUrl).protocol !== "http:") throw new Error(`${configLabel}.runtime.publicBaseUrl must use http`);
  if (config.targets.length !== 2) throw new Error(`${configLabel}.targets must contain exactly D518 gpu-service and NC01 frps targets`);
  const gpu = targetByRole(config, "gpu-service");
  const frps = targetByRole(config, "frps");
  if (gpu.id !== config.frp.tunnel.targetId || frps.id !== config.frp.server.targetId) throw new Error(`${configLabel} FRP target ids must match target roles`);
  if (!config.targets.some((target) => target.id === config.bridge.targetId)) throw new Error(`${configLabel}.bridge.targetId must reference a declared target`);
  for (const target of config.targets) {
    requireString(target.route, `targets.${target.id}.route`);
    requireAbsolute(target.workDir, `targets.${target.id}.workDir`);
  }
  requireAbsolute(gpu.cacheDir, `targets.${gpu.id}.cacheDir`);
  requireAbsolute(gpu.dataDir, `targets.${gpu.id}.dataDir`);
  requireAbsolute(gpu.sourceDir, `targets.${gpu.id}.sourceDir`);
  requireInteger(config.runtime.inference.replicas, "runtime.inference.replicas");
  requireInteger(config.runtime.inference.workers, "runtime.inference.workers");
  if (config.runtime.inference.concurrency !== "serial") throw new Error(`${configLabel}.runtime.inference.concurrency must be serial`);
  if (config.runtime.inference.referenceVoice !== config.image.referenceVoice.id) throw new Error(`${configLabel}.runtime.inference.referenceVoice must select image.referenceVoice.id`);
  if (typeof config.runtime.inference.speed.min !== "number" || typeof config.runtime.inference.speed.max !== "number" || config.runtime.inference.speed.min >= config.runtime.inference.speed.max) throw new Error(`${configLabel}.runtime.inference.speed must declare an increasing numeric range`);
  if (config.runtime.inference.speechTokenizerProvider !== "CPUExecutionProvider") throw new Error(`${configLabel}.runtime.inference.speechTokenizerProvider must be CPUExecutionProvider`);
  return config;
}

function plan(options: CommonOptions): Record<string, unknown> {
  const config = readConfig();
  const bundle = renderBundle(config);
  const secret = secretSummary(config);
  const policy = policyChecks(config, bundle);
  return {
    ok: policy.every((item) => item.ok),
    action: "platform-infra-selfmedia-voice-plan",
    mutation: false,
    metadata: config.metadata,
    targets: config.targets.map(targetSummary),
    image: config.image,
    dependencies: config.dependencies,
    ports: { frpsControl: config.frp.server.controlPort, remote: config.frp.tunnel.remotePort, d518Loopback: config.runtime.port },
    publicBaseUrl: config.runtime.publicBaseUrl,
    bridge: bridgeSummary(config),
    lowMemory: config.runtime.inference,
    secret,
    artifacts: bundle.summary,
    policy,
    valuesPrinted: false,
    ...(options.full || options.raw ? { rendered: bundle.disclosure } : {}),
    next: {
      secretInit: "bun scripts/cli.ts platform-infra selfmedia-voice secret init --confirm",
      apply: "bun scripts/cli.ts platform-infra selfmedia-voice apply --confirm",
      bridgeApply: "bun scripts/cli.ts platform-infra selfmedia-voice bridge apply --confirm",
      bridgeValidate: "bun scripts/cli.ts platform-infra selfmedia-voice bridge validate",
      status: "bun scripts/cli.ts platform-infra selfmedia-voice status",
      validate: "bun scripts/cli.ts platform-infra selfmedia-voice validate",
    },
  };
}

function bridgePlan(options: CommonOptions): Record<string, unknown> {
  const config = readConfig();
  const units = renderBridgeUnits(config);
  return {
    ok: true,
    action: "platform-infra-selfmedia-voice-bridge-plan",
    mutation: false,
    bridge: bridgeSummary(config),
    artifacts: Object.entries(units).map(([name, value]) => ({ name, bytes: Buffer.byteLength(value), fingerprint: fingerprintValues({ value }, ["value"]) })),
    ...(options.full || options.raw ? { rendered: units } : {}),
    next: {
      apply: "bun scripts/cli.ts platform-infra selfmedia-voice bridge apply --confirm",
      status: "bun scripts/cli.ts platform-infra selfmedia-voice bridge status",
      validate: "bun scripts/cli.ts platform-infra selfmedia-voice bridge validate",
    },
  };
}

async function bridgeApply(configRoot: UniDeskConfig, options: ApplyOptions): Promise<Record<string, unknown>> {
  const config = readConfig();
  if (!options.confirm) return { ok: false, action: "platform-infra-selfmedia-voice-bridge-apply", mutation: false, mode: "missing-confirm", error: "bridge apply requires --confirm" };
  const result = await convergeBridge(configRoot, config);
  return { ok: result.exitCode === 0, action: "platform-infra-selfmedia-voice-bridge-apply", mutation: result.exitCode === 0, mode: "completed", bridge: bridgeSummary(config), result: compactCapture(result, { full: true }), valuesPrinted: false };
}

async function bridgeStatus(configRoot: UniDeskConfig, options: CommonOptions): Promise<Record<string, unknown>> {
  const config = readConfig();
  const result = await capture(configRoot, bridgeTarget(config).route, ["sh"], bridgeStatusScript(config), { runtimeTimeoutMs: (config.bridge.timeoutSeconds + 5) * 1000 });
  return { ok: result.exitCode === 0, action: "platform-infra-selfmedia-voice-bridge-status", mutation: false, bridge: bridgeSummary(config), result: compactCapture(result, { full: true }), valuesPrinted: false };
}

async function bridgeValidate(configRoot: UniDeskConfig, options: CommonOptions): Promise<Record<string, unknown>> {
  const status = await bridgeStatus(configRoot, options);
  return { ...status, action: "platform-infra-selfmedia-voice-bridge-validate" };
}

function renderBridgeUnits(config: VoiceConfig): { socket: string; service: string } {
  const bridge = config.bridge;
  return {
    socket: `[Unit]\nDescription=UniDesk SelfMedia Voice Pod bridge socket\nAfter=network-online.target\nWants=network-online.target\n\n[Socket]\nListenStream=${bridge.listenAddress}:${bridge.listenPort}\nNoDelay=true\n\n[Install]\nWantedBy=sockets.target\n`,
    service: `[Unit]\nDescription=UniDesk SelfMedia Voice Pod bridge service\nAfter=network-online.target docker.service\nWants=network-online.target\n\n[Service]\nExecStart=${bridge.socketProxydPath} ${bridge.upstreamAddress}:${bridge.upstreamPort}\nNoNewPrivileges=true\nPrivateTmp=true\nProtectHome=true\nProtectSystem=strict\n`,
  };
}

async function convergeBridge(configRoot: UniDeskConfig, config: VoiceConfig) {
  const units = renderBridgeUnits(config);
  const bridge = config.bridge;
  const script = renderSystemdSocketPairConvergeScript({
    socket: { name: bridge.socketUnitName, path: bridge.socketUnitPath, content: units.socket },
    service: { name: bridge.serviceUnitName, path: bridge.serviceUnitPath, content: units.service },
    postconditionScript: bridgeStatusScript(config),
  });
  return await capture(configRoot, bridgeTarget(config).route, ["sh"], script, { runtimeTimeoutMs: (bridge.timeoutSeconds + 15) * 1000 });
}

function bridgeStatusScript(config: VoiceConfig): string {
  const units = renderBridgeUnits(config);
  const bridge = config.bridge;
  const probeUrl = `${bridge.baseUrl}${bridge.probePath}`;
  return `set +e\ntmp=$(mktemp -d)\ntrap 'rm -rf "$tmp"' EXIT\nprintf %s ${shQuote(Buffer.from(units.socket).toString("base64"))} | base64 -d >"$tmp/socket"\nprintf %s ${shQuote(Buffer.from(units.service).toString("base64"))} | base64 -d >"$tmp/service"\nsocket_active=$(systemctl is-active ${shQuote(bridge.socketUnitName)} 2>/dev/null)\nsocket_enabled=$(systemctl is-enabled ${shQuote(bridge.socketUnitName)} 2>/dev/null)\nservice_active=$(systemctl is-active ${shQuote(bridge.serviceUnitName)} 2>/dev/null)\nsocket_matches=false\nservice_matches=false\ncmp -s "$tmp/socket" ${shQuote(bridge.socketUnitPath)} && socket_matches=true\ncmp -s "$tmp/service" ${shQuote(bridge.serviceUnitPath)} && service_matches=true\nhttp_code=$(curl --silent --show-error --max-time ${bridge.timeoutSeconds} -o /dev/null -w '%{http_code}' ${shQuote(probeUrl)})\nprobe_rc=$?\nprintf '{"socketActive":"%s","socketEnabled":"%s","serviceActive":"%s","socketMatches":%s,"serviceMatches":%s,"probe":{"url":"%s","httpCode":"%s","exitCode":%s}}\\n' "$socket_active" "$socket_enabled" "$service_active" "$socket_matches" "$service_matches" ${shQuote(probeUrl)} "$http_code" "$probe_rc"\n[ "$socket_active" = active ] && [ "$socket_enabled" = enabled ] && [ "$socket_matches" = true ] && [ "$service_matches" = true ] && [ "$probe_rc" -eq 0 ]\n`;
}

function bridgeSummary(config: VoiceConfig): Record<string, unknown> {
  const bridge = config.bridge;
  return {
    target: bridgeTarget(config).id,
    mode: bridge.mode,
    listen: `${bridge.listenAddress}:${bridge.listenPort}`,
    upstream: `${bridge.upstreamAddress}:${bridge.upstreamPort}`,
    baseUrl: bridge.baseUrl,
    socketUnit: { name: bridge.socketUnitName, path: bridge.socketUnitPath },
    serviceUnit: { name: bridge.serviceUnitName, path: bridge.serviceUnitPath },
    probePath: bridge.probePath,
  };
}

function bridgeTarget(config: VoiceConfig): VoiceTarget {
  const matches = config.targets.filter((target) => target.id === config.bridge.targetId);
  if (matches.length !== 1) throw new Error(`${configLabel}.bridge.targetId must reference exactly one target`);
  return matches[0]!;
}

function secretInit(options: ApplyOptions): Record<string, unknown> {
  const config = readConfig();
  if (!options.confirm) return { ok: false, action: "platform-infra-selfmedia-voice-secret-init", mutation: false, mode: "missing-confirm", error: "secret init requires --confirm", secret: secretSummary(config) };
  const sourceRef = config.frp.secret.sourceRef;
  if (!existsSync(sourceRef)) {
    mkdirSync(dirname(sourceRef), { recursive: true, mode: 0o700 });
    writeFileSync(sourceRef, `${config.frp.secret.sourceKey}=${randomBytes(32).toString("base64url")}\n`, { encoding: "utf8", mode: 0o600, flag: "wx" });
  }
  chmodSync(sourceRef, 0o600);
  return { ok: true, action: "platform-infra-selfmedia-voice-secret-init", mutation: true, mode: "initialized", secret: secretSummary(config), valuesPrinted: false };
}

async function apply(configRoot: UniDeskConfig, options: ApplyOptions): Promise<Record<string, unknown>> {
  const config = readConfig();
  if (!options.confirm) return { ok: false, action: "platform-infra-selfmedia-voice-apply", mutation: false, mode: "missing-confirm", error: "apply requires --confirm" };
  const policy = policyChecks(config, renderBundle(config));
  if (!policy.every((item) => item.ok)) return { ok: false, action: "platform-infra-selfmedia-voice-apply", mutation: false, mode: "policy-blocked", policy };
  const secret = readSecret(config);
  if (!options.wait) {
    const job = startJob(
      "platform_infra_selfmedia_voice_apply",
      ["bun", "scripts/cli.ts", "platform-infra", "selfmedia-voice", "apply", "--confirm", "--wait"],
      "Build and converge the YAML-declared D518 voice service/FRPC and NC01 FRPS host-Docker runtimes",
    );
    return { ok: true, action: "platform-infra-selfmedia-voice-apply", mutation: true, mode: "async-job", job, statePath: job.stateFile, statusCommand: `bun scripts/cli.ts job status ${job.id} --tail-bytes 12000`, valuesPrinted: false };
  }
  const bundle = renderBundle(config);
  const frpsTarget = targetByRole(config, "frps");
  const gpuTarget = targetByRole(config, "gpu-service");
  const frps = await convergeTarget(configRoot, config, frpsTarget, runtimeFiles(config, bundle, secret, "frps"));
  if (frps.exitCode !== 0) return { ok: false, action: "platform-infra-selfmedia-voice-apply", mutation: true, mode: "frps-failed", target: targetSummary(frpsTarget), result: compactCapture(frps, { full: true }), valuesPrinted: false };
  const gpu = await convergeTarget(configRoot, config, gpuTarget, runtimeFiles(config, bundle, secret, "gpu-service"));
  return { ok: gpu.exitCode === 0, action: "platform-infra-selfmedia-voice-apply", mutation: true, mode: "completed", targets: { frps: compactCapture(frps), gpu: compactCapture(gpu, { full: gpu.exitCode !== 0 }) }, secret: secret.summary, valuesPrinted: false };
}

async function status(configRoot: UniDeskConfig, options: CommonOptions): Promise<Record<string, unknown>> {
  const config = readConfig();
  const results: Record<string, unknown> = {};
  for (const target of config.targets) {
    const compose = target.role === "gpu-service" ? config.artifacts.d518Compose : config.artifacts.nc01Compose;
    const probe = target.role === "gpu-service"
      ? `curl --silent --show-error --max-time 10 http://${config.runtime.bindAddress}:${config.runtime.port}${config.runtime.smoke.healthPath}`
      : `printf '{"frpsControlPort":${config.frp.server.controlPort},"remotePort":${config.frp.tunnel.remotePort}}\\n'`;
    const script = `set +e\ncd ${shQuote(target.workDir)}\ndocker compose --project-name ${shQuote(target.composeProject)} --env-file runtime.env -f compose.yaml ps --format json\nrc=$?\n${probe}\nexit $rc\n`;
    const result = await capture(configRoot, target.route, ["sh"], script);
    results[target.id] = { target: targetSummary(target), composeArtifact: compose, result: compactCapture(result, { full: options.full || options.raw }) };
  }
  return { ok: Object.values(results).every((value) => (value as { result: { exitCode: number } }).result.exitCode === 0), action: "platform-infra-selfmedia-voice-status", mutation: false, image: config.image, dependencies: config.dependencies, ports: { control: config.frp.server.controlPort, remote: config.frp.tunnel.remotePort, local: config.runtime.port }, publicBaseUrl: config.runtime.publicBaseUrl, secret: secretSummary(config), targets: results, valuesPrinted: false };
}

async function logs(configRoot: UniDeskConfig, options: LogsOptions): Promise<Record<string, unknown>> {
  const config = readConfig();
  const selected = config.targets.filter((target) => options.component === "all" || (target.role === "frps" ? options.component === "frps" : options.component === "voice" || options.component === "frpc"));
  const results: Record<string, unknown> = {};
  for (const target of selected) {
    const services = target.role === "frps" ? [config.runtime.frpsServiceName] : options.component === "all" ? [config.runtime.serviceName, config.runtime.frpcServiceName] : [options.component];
    const script = `cd ${shQuote(target.workDir)} && docker compose --project-name ${shQuote(target.composeProject)} --env-file runtime.env -f compose.yaml logs --no-color --tail ${options.lines} ${services.map(shQuote).join(" ")}`;
    const result = await capture(configRoot, target.route, ["sh"], script);
    results[target.id] = { exitCode: result.exitCode, stdout: redactText(result.stdout).slice(-12000), stderr: redactText(result.stderr).slice(-4000) };
  }
  return { ok: Object.values(results).every((value) => (value as { exitCode: number }).exitCode === 0), action: "platform-infra-selfmedia-voice-logs", mutation: false, component: options.component, lines: options.lines, targets: results, valuesPrinted: false };
}

async function validate(configRoot: UniDeskConfig, options: CommonOptions): Promise<Record<string, unknown>> {
  const config = readConfig();
  const current = await status(configRoot, options);
  const frpsTarget = targetByRole(config, "frps");
  const request = JSON.stringify({ model: config.image.modelSource.id, input: config.runtime.smoke.text, voice: config.runtime.smoke.voice, response_format: "wav" });
  const script = `set -eu\ntmp=$(mktemp -d)\ntrap 'rm -rf "$tmp"' EXIT\ncurl --fail --silent --show-error --max-time 20 ${shQuote(`${config.runtime.publicBaseUrl}${config.runtime.smoke.healthPath}`)} >"$tmp/health.json"\ncurl --fail --silent --show-error --max-time ${config.runtime.smoke.timeoutSeconds} -H 'content-type: application/json' -d ${shQuote(request)} ${shQuote(`${config.runtime.publicBaseUrl}${config.runtime.smoke.speechPath}`)} >"$tmp/speech.wav"\nffprobe -v error -show_entries format=format_name,duration -of json "$tmp/speech.wav"\n`;
  const smoke = await capture(configRoot, frpsTarget.route, ["sh"], script, { runtimeTimeoutMs: (config.runtime.smoke.timeoutSeconds + 30) * 1000 });
  return { ok: current.ok === true && smoke.exitCode === 0, action: "platform-infra-selfmedia-voice-validate", mutation: false, status: current, smoke: compactCapture(smoke, { full: options.full || options.raw || smoke.exitCode !== 0 }), secret: secretSummary(config), valuesPrinted: false };
}

function renderBundle(config: VoiceConfig): { files: Record<string, string>; summary: Record<string, unknown>[]; disclosure: Record<string, unknown> } {
  const artifact = (path: string) => readFileSync(resolveRepoPath(path), "utf8");
  const replace = (text: string, values: Record<string, string | number>) => text.replace(/\{\{([A-Z_]+)\}\}/gu, (match, key: string) => key in values ? String(values[key]) : match);
  const files = {
    containerfile: artifact(config.artifacts.containerfile),
    api: artifact(config.artifacts.api),
    modelInit: artifact(config.artifacts.modelInit),
    sourceInit: artifact(config.artifacts.sourceInit),
    d518Compose: artifact(config.artifacts.d518Compose),
    nc01Compose: artifact(config.artifacts.nc01Compose),
    frpc: replace(artifact(config.artifacts.frpcTemplate), { SERVER_ADDRESS: config.frp.server.address, CONTROL_PORT: config.frp.server.controlPort, PROXY_NAME: config.frp.tunnel.proxyName, LOCAL_ADDRESS: config.frp.tunnel.localAddress, LOCAL_PORT: config.frp.tunnel.localPort, REMOTE_PORT: config.frp.tunnel.remotePort }),
    frps: replace(artifact(config.artifacts.frpsTemplate), { CONTROL_PORT: config.frp.server.controlPort, REMOTE_PORT: config.frp.tunnel.remotePort }),
  };
  const summary = Object.entries(files).map(([name, value]) => ({ name, bytes: Buffer.byteLength(value), fingerprint: fingerprintValues({ value }, ["value"]) }));
  return { files, summary, disclosure: { frpc: redactText(files.frpc), frps: redactText(files.frps), dependencyIdentity: config.dependencies, runtimeEnvKeys: runtimeEnv(config, targetByRole(config, "gpu-service")).split("\n").filter(Boolean).map((line) => line.split("=", 1)[0]) } };
}

function runtimeFiles(config: VoiceConfig, bundle: ReturnType<typeof renderBundle>, secret: ReturnType<typeof readSecret>, role: VoiceTarget["role"]): Record<string, string> {
  const target = targetByRole(config, role);
  const common = { "runtime.env": runtimeEnv(config, target), "secret.env": `${config.frp.secret.sourceKey}=${secret.value}\n` };
  return role === "frps" ? { ...common, "compose.yaml": bundle.files.nc01Compose, "frps.toml": bundle.files.frps } : { ...common, "compose.yaml": bundle.files.d518Compose, "Containerfile": bundle.files.containerfile, "api.py": bundle.files.api, "model-init.sh": bundle.files.modelInit, "source-init.sh": bundle.files.sourceInit, "frpc.toml": bundle.files.frpc };
}

function runtimeEnv(config: VoiceConfig, target: VoiceTarget): string {
  const values: Record<string, string | number> = target.role === "frps" ? {
    FRPS_IMAGE: config.frp.images.frps,
    FRPS_CONTROL_PORT: config.frp.server.controlPort,
    FRP_REMOTE_PORT: config.frp.tunnel.remotePort,
  } : {
    VOICE_IMAGE: config.image.name,
    BASE_IMAGE: `${config.image.base.repository}@${config.image.base.digest}`,
    SOURCE_REPOSITORY: config.image.source.repository,
    SOURCE_REF: config.image.source.ref,
    SOURCE_SUBMODULES: config.image.source.submodules,
    SOURCE_FETCH_RETRY_ATTEMPTS: config.image.source.cache.retryAttempts,
    SOURCE_FETCH_RETRY_DELAY_SECONDS: config.image.source.cache.retryDelaySeconds,
    MODEL_ID: config.image.modelSource.id,
    MODEL_REPOSITORY: config.image.modelSource.repository,
    MODEL_BRANCH: config.image.modelSource.branch,
    MODEL_REF: config.image.modelSource.ref,
    MODEL_REQUIRE_BRANCH_HEAD_MATCH: String(config.image.modelSource.requireBranchHeadMatch),
    REFERENCE_VOICE_ID: config.image.referenceVoice.id,
    REFERENCE_WAV_PATH: config.image.referenceVoice.path,
    REFERENCE_WAV_SHA256: config.image.referenceVoice.sha256,
    REFERENCE_PROMPT_TEXT: config.image.referenceVoice.promptText,
    PIP_VERSION: config.dependencies.pip.version,
    PIP_INDEX_URL: config.dependencies.pip.indexUrl,
    SETUPTOOLS_INSTALL_SPEC: config.dependencies.setuptools.installSpec,
    TORCH_VERSION_IDENTITY: config.dependencies.torch.versionIdentity,
    TORCH_LIBRARY_PATH: config.dependencies.torch.libraryPath,
    TORCHAUDIO_VERSION_IDENTITY: config.dependencies.torchaudio.versionIdentity,
    OPENAI_WHISPER_INSTALL_SPEC: config.dependencies.openaiWhisper.installSpec,
    ONNXRUNTIME_GPU_INSTALL_SPEC: config.dependencies.onnxRuntimeGpu.installSpec,
    ONNXRUNTIME_GPU_INDEX_URL: config.dependencies.onnxRuntimeGpu.indexUrl,
    VOICE_BIND_ADDRESS: config.runtime.bindAddress,
    VOICE_PORT: config.runtime.port,
    VOICE_CACHE_DIR: requireAbsolute(target.cacheDir, `targets.${target.id}.cacheDir`),
    VOICE_DATA_DIR: requireAbsolute(target.dataDir, `targets.${target.id}.dataDir`),
    VOICE_SOURCE_DIR: requireAbsolute(target.sourceDir, `targets.${target.id}.sourceDir`),
    VOICE_DEVICE: config.runtime.inference.device,
    VOICE_FP16: String(config.runtime.inference.fp16),
    VOICE_LOAD_TRT: String(config.runtime.inference.loadTrt),
    VOICE_LOAD_VLLM: String(config.runtime.inference.loadVllm),
    VOICE_REPLICAS: config.runtime.inference.replicas,
    VOICE_WORKERS: config.runtime.inference.workers,
    VOICE_CONCURRENCY: config.runtime.inference.concurrency,
    VOICE_SPEED_MIN: config.runtime.inference.speed.min,
    VOICE_SPEED_MAX: config.runtime.inference.speed.max,
    PYTORCH_CUDA_ALLOC_CONF: config.runtime.inference.cudaAllocator,
    CUDA_MODULE_LOADING: config.runtime.inference.cudaModuleLoading,
    VOICE_SPEECH_TOKENIZER_PROVIDER: config.runtime.inference.speechTokenizerProvider,
    FRPC_IMAGE: config.frp.images.frpc,
  };
  return Object.entries(values).map(([key, value]) => `${key}=${value}`).join("\n") + "\n";
}

async function convergeTarget(configRoot: UniDeskConfig, config: VoiceConfig, target: VoiceTarget, files: Record<string, string>) {
  const encoded = Object.entries(files).map(([name, value]) => `printf %s ${shQuote(Buffer.from(value).toString("base64"))} | base64 -d > ${shQuote(name)}`).join("\n");
  const sourceEnv = target.role === "gpu-service" ? {
    SOURCE_REPOSITORY: config.image.source.repository,
    SOURCE_REF: config.image.source.ref,
    SOURCE_SUBMODULES: config.image.source.submodules,
    SOURCE_FETCH_RETRY_ATTEMPTS: config.image.source.cache.retryAttempts,
    SOURCE_FETCH_RETRY_DELAY_SECONDS: config.image.source.cache.retryDelaySeconds,
    VOICE_SOURCE_DIR: requireAbsolute(target.sourceDir, `targets.${target.id}.sourceDir`),
    REFERENCE_WAV_SHA256: config.image.referenceVoice.sha256,
    REFERENCE_WAV_PATH: config.image.referenceVoice.path,
  } : null;
  const prepareSource = sourceEnv === null ? "" : `chmod 700 source-init.sh\n${Object.entries(sourceEnv).map(([key, value]) => `${key}=${shQuote(String(value))}`).join(" ")} ./source-init.sh\n`;
  const script = `set -eu\numask 077\nmkdir -p ${shQuote(target.workDir)}\ncd ${shQuote(target.workDir)}\n${encoded}\nchmod 600 secret.env\n${prepareSource}docker compose --project-name ${shQuote(target.composeProject)} --env-file runtime.env -f compose.yaml config >/dev/null\ndocker compose --project-name ${shQuote(target.composeProject)} --env-file runtime.env -f compose.yaml up -d --build\n`;
  return await capture(configRoot, target.route, ["sh"], script, { runtimeTimeoutMs: config.runtime.operations.applyTimeoutSeconds * 1000 });
}

function readSecret(config: VoiceConfig): { value: string; summary: Record<string, unknown> } {
  const sourceRef = config.frp.secret.sourceRef;
  if (!existsSync(sourceRef)) throw new Error(`${sourceRef} is missing; run platform-infra selfmedia-voice secret init --confirm`);
  const values = parseEnvFile(readFileSync(sourceRef, "utf8"));
  const value = values[config.frp.secret.sourceKey];
  if (value === undefined || value.length < 32) throw new Error(`${sourceRef}.${config.frp.secret.sourceKey} must contain at least 32 characters`);
  return { value, summary: { sourceRef, sourceKey: config.frp.secret.sourceKey, presence: true, fingerprint: fingerprintValues({ value }, ["value"]), valuesPrinted: false } };
}

function secretSummary(config: VoiceConfig): Record<string, unknown> {
  if (!existsSync(config.frp.secret.sourceRef)) return { sourceRef: config.frp.secret.sourceRef, sourceKey: config.frp.secret.sourceKey, presence: false, fingerprint: null, valuesPrinted: false };
  try { return readSecret(config).summary; } catch (error) { return { sourceRef: config.frp.secret.sourceRef, sourceKey: config.frp.secret.sourceKey, presence: true, fingerprint: null, error: error instanceof Error ? error.message : String(error), valuesPrinted: false }; }
}

function policyChecks(config: VoiceConfig, bundle: ReturnType<typeof renderBundle>): Array<{ id: string; ok: boolean; detail: string }> {
  const rendered = Object.values(bundle.files).join("\n").toLowerCase();
  return [
    { id: "no-caddy-domain-k8s-exposure", ok: !/(caddy|ingress|nodeport)/u.test(rendered), detail: "Caddy, domain routing, Ingress and NodePort are forbidden" },
    { id: "locked-source-refs", ok: /^[0-9a-f]{40}$/u.test(config.image.source.ref) && /^[0-9a-f]{40}$/u.test(config.image.modelSource.ref), detail: "CosyVoice and ModelScope refs are immutable commits" },
    { id: "locked-base-image", ok: /^sha256:[0-9a-f]{64}$/u.test(config.image.base.digest) && /^sha256:[0-9a-f]{64}$/u.test(config.image.base.imageId), detail: "base image digest and observed D518 image identity are declared" },
    { id: "modelscope-git-lfs", ok: config.image.modelSource.provider === "modelscope-git-lfs" && config.image.modelSource.requireBranchHeadMatch, detail: "model fetch uses Git/LFS and asserts branch head before locked checkout" },
    { id: "dependency-identity", ok: config.dependencies.torch.versionIdentity === config.dependencies.torchaudio.versionIdentity, detail: `torch/torchaudio ${config.dependencies.torch.versionIdentity}; ABI policy ${config.dependencies.torchaudio.abiPolicy}` },
    { id: "single-gpu-runtime", ok: config.runtime.inference.replicas === 1 && config.runtime.inference.workers === 1 && config.runtime.inference.concurrency === "serial", detail: "one replica, one worker and serialized inference prevent concurrent model copies" },
    { id: "rollout-approved", ok: config.rollout.approved, detail: `${config.rollout.prerequisite}: ${config.rollout.approved ? "approved" : "not approved"}` },
    { id: "host-docker-only", ok: config.targets.every((target) => !target.route.includes("k3s")), detail: "Both targets use host-Docker routes" },
    { id: "loopback-service", ok: config.runtime.bindAddress === "127.0.0.1" && config.frp.tunnel.localAddress === "127.0.0.1", detail: "D518 service is loopback-only behind FRPC" },
    { id: "dedicated-frps", ok: targetByRole(config, "frps").composeProject.includes("selfmedia-voice") && config.frp.server.controlPort === 22110, detail: "SelfMedia Voice owns a dedicated FRPS project and control port" },
  ];
}

function renderResult(command: string, data: Record<string, unknown>): RenderedCliResult | Record<string, unknown> {
  if (process.argv.includes("--json") || process.argv.includes("--full") || process.argv.includes("--raw")) return data;
  const lines = [
    `PLATFORM-INFRA SELFMEDIA-VOICE ${command.toUpperCase()}`,
    `ok=${String(data.ok)} action=${String(data.action ?? "-")} mutation=${String(data.mutation ?? false)} mode=${String(data.mode ?? "-")}`,
    `public=${String(data.publicBaseUrl ?? "-")} valuesPrinted=false`,
  ];
  const secret = data.secret as Record<string, unknown> | undefined;
  if (secret !== undefined) lines.push(`secret sourceRef=${String(secret.sourceRef)} presence=${String(secret.presence)} fingerprint=${String(secret.fingerprint ?? "-")}`);
  const bridge = data.bridge as Record<string, unknown> | undefined;
  if (bridge !== undefined) lines.push(`bridge listen=${String(bridge.listen ?? "-")} upstream=${String(bridge.upstream ?? "-")} baseUrl=${String(bridge.baseUrl ?? "-")}`);
  const result = data.result as Record<string, unknown> | undefined;
  if (result !== undefined) lines.push(`result exitCode=${String(result.exitCode ?? "-")} stdoutTail=${String(result.stdoutTail ?? "-").trim()}`);
  const job = data.job as Record<string, unknown> | undefined;
  if (job !== undefined) lines.push(`job id=${String(job.id)} status=${String(job.status)} statePath=${String(data.statePath ?? job.stateFile ?? "-")}`);
  const next = data.next as Record<string, unknown> | undefined;
  if (next !== undefined) lines.push(...Object.entries(next).map(([key, value]) => `next.${key}=${String(value)}`));
  return { ok: data.ok === true, command: `platform-infra selfmedia-voice ${command}`, renderedText: `${lines.join("\n")}\n`, contentType: "text/plain", projection: data };
}

function targetByRole(config: VoiceConfig, role: VoiceTarget["role"]): VoiceTarget {
  const matches = config.targets.filter((target) => target.role === role);
  if (matches.length !== 1) throw new Error(`${configLabel}.targets must contain exactly one ${role} target`);
  return matches[0]!;
}

function targetSummary(target: VoiceTarget): Record<string, unknown> {
  return { id: target.id, role: target.role, route: target.route, workDir: target.workDir, composeProject: target.composeProject, cacheDir: target.cacheDir ?? null, dataDir: target.dataDir ?? null, sourceDir: target.sourceDir ?? null };
}

function requireString(value: unknown, label: string): string {
  if (typeof value !== "string" || value.length === 0) throw new Error(`${configLabel}.${label} must be a non-empty string`);
  return value;
}

function requireInteger(value: unknown, label: string): number {
  if (!Number.isInteger(value)) throw new Error(`${configLabel}.${label} must be an integer`);
  return value as number;
}

function requirePort(value: unknown, label: string): number {
  const port = requireInteger(value, label);
  if (port < 1 || port > 65535) throw new Error(`${configLabel}.${label} must be in 1..65535`);
  return port;
}

function requireAbsolute(value: unknown, label: string): string {
  const path = requireString(value, label);
  if (!isAbsolute(path)) throw new Error(`${configLabel}.${label} must be an absolute path`);
  return path;
}

function requireCommit(value: unknown, label: string): string {
  const ref = requireString(value, label);
  if (!/^[0-9a-f]{40}$/u.test(ref)) throw new Error(`${configLabel}.${label} must be a 40-character commit`);
  return ref;
}

function requireSha256(value: unknown, label: string, prefix = true): string {
  const hash = requireString(value, label);
  const pattern = prefix ? /^sha256:[0-9a-f]{64}$/u : /^[0-9a-f]{64}$/u;
  if (!pattern.test(hash)) throw new Error(`${configLabel}.${label} must be a SHA-256 identity`);
  return hash;
}
