import { createHash } from "node:crypto";
import { spawn } from "node:child_process";
import {
  chmodSync,
  existsSync,
  mkdtempSync,
  mkdirSync,
  readFileSync,
  rmSync,
  statSync,
  symlinkSync,
  writeFileSync,
} from "node:fs";
import { tmpdir } from "node:os";
import { isAbsolute, join } from "node:path";
import { gzipSync } from "node:zlib";
import type { UniDeskConfig } from "./config";
import { repoRoot, rootPath } from "./config";
import { startJob } from "./jobs";
import {
  capture,
  compactCapture,
  fingerprintValues,
  parseEnvFile,
  parseJsonOutput,
  parseOpsApplyOptions,
  parseOpsCommonOptions,
  readYamlRecord,
  shQuote,
  type OpsApplyOptions,
  type OpsCommonOptions,
} from "./platform-infra-ops-library";

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

interface Artifact {
  localPath: string;
  remotePath: string;
  sha256: string;
}

interface ImageArtifact extends Artifact {
  buildTarget: string;
  image: string;
  imageId: string;
  bytes: number;
}

interface ArtifactDistribution {
  mode: "local-http";
  workDir: string;
  bindHost: string;
  port: number;
  publicBaseUrl: string;
}

interface Route {
  listenPort: number;
  upstreams: Array<{ host: string; port: number }>;
}

interface Backend {
  id: string;
  bindHost: string;
  port: number;
  unit: string;
}

interface NginxTarget {
  route: string;
  runtime: {
    workDir: string;
    composePath: string;
    envPath: string;
    projectName: string;
    containerName: string;
    networkMode: "host";
    restart: string;
    resources: { cpus: string; memory: string; pidsLimit: number };
    logDir: string;
    routes: Route[];
    environment: Record<string, string>;
  };
  apiKey: {
    sourceRef: string;
    sourceKey: string;
    targetKey: string;
    requestHeader: string;
  };
  validation: {
    requestCount: number;
    cleanupAfterSuccess: boolean;
    backendScriptPath: string;
    backends: Backend[];
  };
}

interface NginxConfig {
  version: number;
  kind: "platform-infra-nginx";
  metadata: { name: string };
  defaults: { target: string };
  artifacts: {
    image: ImageArtifact;
    composePlugin: Artifact;
    distribution: ArtifactDistribution;
  };
  targets: Record<string, NginxTarget>;
}

interface SecretMaterial {
  sourcePath: string;
  value: string;
  fingerprint: string;
}

interface RemoteJobOutcome {
  exitCode: number;
  stdout: string;
  stderr: string;
  polls: number;
}

export function nginxHelp(): Record<string, unknown> {
  return {
    command: "platform-infra nginx plan|apply|status",
    output: "json",
    usage: [
      "bun scripts/cli.ts platform-infra nginx plan [--target PK01]",
      "bun scripts/cli.ts platform-infra nginx apply [--target PK01] --dry-run",
      "bun scripts/cli.ts platform-infra nginx apply [--target PK01] --confirm",
      "bun scripts/cli.ts platform-infra nginx status [--target PK01] [--full|--raw]",
    ],
    configTruth: configLabel,
    secretPolicy: "API key values are never printed; output contains presence and fingerprint only.",
  };
}

export async function runPlatformInfraNginxCommand(
  config: UniDeskConfig,
  args: string[],
): Promise<Record<string, unknown>> {
  const [action = "plan"] = args;
  if (action === "plan") return plan(parseOpsCommonOptions(args.slice(1)));
  if (action === "apply") return await apply(config, parseOpsApplyOptions(args.slice(1)));
  if (action === "status") return await status(config, parseOpsCommonOptions(args.slice(1)));
  return { ok: false, error: "unsupported-platform-infra-nginx-command", args, help: nginxHelp() };
}

function readConfig(): NginxConfig {
  const config = readYamlRecord<NginxConfig>(configPath, "platform-infra-nginx");
  assert(config.version === 1, `${configLabel}.version must be 1`);
  assert(config.metadata.name === "nginx", `${configLabel}.metadata.name must be nginx`);
  assert(Object.keys(config.targets).length > 0, `${configLabel}.targets must not be empty`);
  validateArtifact(config.artifacts.image, "artifacts.image");
  validateArtifact(config.artifacts.composePlugin, "artifacts.composePlugin");
  validateDistribution(config.artifacts.distribution);
  assert(Number.isInteger(config.artifacts.image.bytes), `${configLabel}.artifacts.image.bytes must be an integer`);
  assert(config.artifacts.image.bytes > 0, `${configLabel}.artifacts.image.bytes must be positive`);
  assert(/^sha256:[a-f0-9]{64}$/u.test(config.artifacts.image.imageId), "artifacts.image.imageId is invalid");
  for (const [targetId, target] of Object.entries(config.targets)) validateTarget(targetId, target);
  return config;
}

function validateDistribution(distribution: ArtifactDistribution): void {
  assert(distribution.mode === "local-http", "artifacts.distribution.mode must be local-http");
  absolutePath(distribution.workDir, "artifacts.distribution.workDir");
  assert(distribution.bindHost === "0.0.0.0", "artifacts.distribution.bindHost must be 0.0.0.0");
  port(distribution.port, "artifacts.distribution.port");
  const expectedSuffix = `:${distribution.port}`;
  assert(
    distribution.publicBaseUrl.startsWith("http://") && distribution.publicBaseUrl.endsWith(expectedSuffix),
    "artifacts.distribution.publicBaseUrl must be HTTP and end with the declared port",
  );
}

function validateArtifact(artifact: Artifact, path: string): void {
  absolutePath(artifact.localPath, `${path}.localPath`);
  absolutePath(artifact.remotePath, `${path}.remotePath`);
  assert(/^[a-f0-9]{64}$/u.test(artifact.sha256), `${configLabel}.${path}.sha256 is invalid`);
}

function validateTarget(id: string, target: NginxTarget): void {
  simpleName(id, `targets.${id}`);
  simpleName(target.route, `targets.${id}.route`);
  const runtime = target.runtime;
  for (const [key, value] of [
    ["workDir", runtime.workDir],
    ["composePath", runtime.composePath],
    ["envPath", runtime.envPath],
    ["logDir", runtime.logDir],
    ["backendScriptPath", target.validation.backendScriptPath],
  ]) absolutePath(value, `targets.${id}.${key}`);
  assert(runtime.networkMode === "host", `targets.${id}.runtime.networkMode must be host`);
  simpleName(runtime.projectName, `targets.${id}.runtime.projectName`);
  simpleName(runtime.containerName, `targets.${id}.runtime.containerName`);
  assert(runtime.routes.length > 0, `targets.${id}.runtime.routes must not be empty`);
  const listenPorts = new Set<number>();
  for (const route of runtime.routes) {
    port(route.listenPort, `targets.${id}.runtime.routes.listenPort`);
    assert(!listenPorts.has(route.listenPort), `targets.${id}.runtime.routes contains a duplicate listenPort`);
    listenPorts.add(route.listenPort);
    assert(route.upstreams.length > 0, `targets.${id}.runtime.routes.upstreams must not be empty`);
    for (const upstream of route.upstreams) {
      assert(/^[A-Za-z0-9_.-]+$/u.test(upstream.host), `targets.${id} upstream host is invalid`);
      port(upstream.port, `targets.${id} upstream port`);
    }
  }
  assert(target.validation.requestCount === 4, `targets.${id}.validation.requestCount must be 4`);
  assert(target.validation.cleanupAfterSuccess, `targets.${id}.validation.cleanupAfterSuccess must be true`);
  assert(target.validation.backends.length === 2, `targets.${id}.validation.backends must contain two backends`);
  for (const backend of target.validation.backends) {
    simpleName(backend.id, `targets.${id}.validation.backends.id`);
    assert(backend.bindHost === "127.0.0.1", `targets.${id} validation backend must bind to 127.0.0.1`);
    port(backend.port, `targets.${id} validation backend port`);
    assert(/^[A-Za-z0-9_.@-]+\.service$/u.test(backend.unit), `targets.${id} backend unit is invalid`);
  }
  for (const key of Object.keys(runtime.environment)) {
    assert(/^(NGINX|LOGGER)_[A-Z0-9_]+$/u.test(key), `targets.${id}.runtime.environment key ${key} is invalid`);
  }
  assert(target.apiKey.sourceKey === "LOGGER_API_KEY", `targets.${id}.apiKey.sourceKey must be LOGGER_API_KEY`);
  assert(target.apiKey.targetKey === "LOGGER_API_KEY", `targets.${id}.apiKey.targetKey must be LOGGER_API_KEY`);
  assert(target.apiKey.requestHeader === "X-API-Key", `targets.${id}.apiKey.requestHeader must be X-API-Key`);
}

function resolveTarget(config: NginxConfig, requested: string | null): { id: string; target: NginxTarget } {
  const id = requested ?? config.defaults.target;
  const target = config.targets[id];
  if (target === undefined) throw new Error(`unknown nginx target: ${id}`);
  return { id, target };
}

function plan(options: OpsCommonOptions): Record<string, unknown> {
  const config = readConfig();
  const { id, target } = resolveTarget(config, options.targetId);
  const secret = readSecret(target);
  const image = localArtifactSummary(config.artifacts.image);
  const composePlugin = localArtifactSummary(config.artifacts.composePlugin);
  const compose = renderCompose(config, target);
  return {
    ok: image.valid && composePlugin.valid,
    action: "platform-infra-nginx-plan",
    mutation: false,
    target: targetSummary(id, target),
    artifacts: { image, composePlugin },
    compose: {
      sha256: sha256(compose),
      bytes: Buffer.byteLength(compose),
      routes: renderRoutes(target.runtime.routes),
    },
    apiKey: secretSummary(target, secret),
    validation: validationSummary(target),
    next: {
      dryRun: `bun scripts/cli.ts platform-infra nginx apply --target ${id} --dry-run`,
      apply: `bun scripts/cli.ts platform-infra nginx apply --target ${id} --confirm`,
      status: `bun scripts/cli.ts platform-infra nginx status --target ${id}`,
    },
  };
}

async function apply(config: UniDeskConfig, options: OpsApplyOptions): Promise<Record<string, unknown>> {
  const nginx = readConfig();
  const { id, target } = resolveTarget(nginx, options.targetId);
  const secret = readSecret(target);
  const image = localArtifactSummary(nginx.artifacts.image);
  const composePlugin = localArtifactSummary(nginx.artifacts.composePlugin);
  if (!image.valid || !composePlugin.valid) {
    return { ok: false, action: "platform-infra-nginx-apply", mode: "artifact-invalid", image, composePlugin };
  }
  if (options.confirm && !options.wait) {
    const job = startJob(
      `platform_infra_nginx_apply_${id.toLowerCase()}`,
      ["bun", "scripts/cli.ts", "platform-infra", "nginx", "apply", "--target", id, "--confirm", "--wait"],
      `Deploy YAML-controlled Nginx to ${id}, run four serial requests, and remove host test backends`,
    );
    return {
      ok: true,
      action: "platform-infra-nginx-apply",
      mode: "async-job",
      mutation: true,
      target: targetSummary(id, target),
      job,
      statusCommand: `bun scripts/cli.ts job status ${job.id} --tail-bytes 12000`,
    };
  }
  const compose = renderCompose(nginx, target);
  if (options.dryRun) {
    return {
      ok: true,
      action: "platform-infra-nginx-apply",
      mode: "dry-run",
      mutation: false,
      target: targetSummary(id, target),
      compose: {
        sha256: sha256(compose),
        bytes: Buffer.byteLength(compose),
        routes: renderRoutes(target.runtime.routes),
      },
      apiKey: secretSummary(target, secret),
      artifacts: { image, composePlugin },
      validation: validationSummary(target),
    };
  }

  const prepare = await capture(config, target.route, ["sh"], prepareRemoteScript(target));
  if (prepare.exitCode !== 0) return failedApply(id, target, "prepare", prepare, secret);
  const distribution = nginx.artifacts.distribution;
  const distributedImage = `${distribution.workDir}/${fileName(nginx.artifacts.image.remotePath)}`;
  const distributedPlugin = `${distribution.workDir}/${fileName(nginx.artifacts.composePlugin.remotePath)}`;
  const uploads = [
    uploadSecret(target.route, secret.sourcePath, target.runtime.envPath),
  ];
  const failedUpload = uploads.find((upload) => !upload.ok);
  if (failedUpload !== undefined) {
    return {
      ok: false,
      action: "platform-infra-nginx-apply",
      mode: "confirmed",
      stage: "upload",
      target: targetSummary(id, target),
      uploads,
      apiKey: secretSummary(target, secret),
    };
  }
  const source = await startLocalArtifactSource(nginx);
  let outcome: RemoteJobOutcome;
  try {
    const remoteScript = applyRemoteScript(nginx, target, compose, {
      image: `${distribution.publicBaseUrl}/${fileName(distributedImage)}`,
      composePlugin: `${distribution.publicBaseUrl}/${fileName(distributedPlugin)}.gz`,
    });
    const start = await capture(
      config,
      target.route,
      ["sh"],
      startRemoteJobScript(target, remoteScript),
    );
    if (start.exitCode !== 0) return failedApply(id, target, "remote-job-start", start, secret);
    outcome = await waitRemoteJob(config, target);
  } finally {
    stopLocalArtifactSource(source);
  }
  const parsed = parseJsonOutput(outcome.stdout);
  return {
    ok: outcome.exitCode === 0 && parsed?.ok === true,
    action: "platform-infra-nginx-apply",
    mode: "confirmed",
    mutation: true,
    target: targetSummary(id, target),
    uploads,
    apiKey: secretSummary(target, secret),
    validation: parsed ?? remoteJobSummary(outcome, true),
    remote: remoteJobSummary(outcome, options.full || outcome.exitCode !== 0),
    next: { status: `bun scripts/cli.ts platform-infra nginx status --target ${id}` },
  };
}

async function waitRemoteJob(config: UniDeskConfig, target: NginxTarget): Promise<RemoteJobOutcome> {
  const maximumPolls = 4000;
  for (let poll = 1; poll <= maximumPolls; poll += 1) {
    const remote = await capture(config, target.route, ["sh"], pollRemoteJobScript(target));
    if (remote.exitCode !== 0) {
      return { exitCode: remote.exitCode, stdout: remote.stdout, stderr: remote.stderr, polls: poll };
    }
    const result = parseJsonOutput(remote.stdout);
    if (result?.done === true) {
      return {
        exitCode: Number(result.exitCode),
        stdout: decodeBase64(String(result.stdoutBase64 ?? "")),
        stderr: decodeBase64(String(result.stderrBase64 ?? "")),
        polls: poll,
      };
    }
    await Bun.sleep(2000);
  }
  const stopped = await capture(config, target.route, ["sh"], stopRemoteJobScript(target));
  return {
    exitCode: 124,
    stdout: stopped.stdout,
    stderr: `remote nginx apply job exceeded ${maximumPolls * 2} seconds`,
    polls: maximumPolls,
  };
}

async function status(config: UniDeskConfig, options: OpsCommonOptions): Promise<Record<string, unknown>> {
  const nginx = readConfig();
  const { id, target } = resolveTarget(nginx, options.targetId);
  const secret = readSecret(target);
  const remote = await capture(config, target.route, ["sh"], statusRemoteScript(nginx, target));
  const parsed = parseJsonOutput(remote.stdout);
  return {
    ok: remote.exitCode === 0 && parsed?.ok === true,
    action: "platform-infra-nginx-status",
    target: targetSummary(id, target),
    apiKey: secretSummary(target, secret),
    summary: parsed,
    remote: compactCapture(remote, { full: options.full || remote.exitCode !== 0 }),
    ...(options.raw ? { raw: remote } : {}),
  };
}

function prepareRemoteScript(target: NginxTarget): string {
  return `set -eu
install -d -m 0755 ${shQuote(target.runtime.workDir)} ${shQuote(target.runtime.logDir)}
`;
}

async function startLocalArtifactSource(
  config: NginxConfig,
): Promise<{ pid: number; workDir: string }> {
  const distribution = config.artifacts.distribution;
  const imageLink = `${distribution.workDir}/${fileName(config.artifacts.image.remotePath)}`;
  const pluginLink = `${distribution.workDir}/${fileName(config.artifacts.composePlugin.remotePath)}`;
  stopOwnedLocalServer(distribution.workDir);
  rmSync(distribution.workDir, { recursive: true, force: true });
  mkdirSync(distribution.workDir, { recursive: true, mode: 0o755 });
  symlinkSync(config.artifacts.image.localPath, imageLink);
  writeFileSync(
    `${pluginLink}.gz`,
    gzipSync(readFileSync(config.artifacts.composePlugin.localPath), { level: 1 }),
    { mode: 0o644 },
  );
  const child = spawn(
    "python3",
    [
      "-m",
      "http.server",
      String(distribution.port),
      "--bind",
      distribution.bindHost,
      "--directory",
      distribution.workDir,
    ],
    { detached: true, stdio: "ignore" },
  );
  child.unref();
  if (child.pid === undefined) throw new Error("failed to start local artifact HTTP server");
  writeFileSync(join(distribution.workDir, "server.pid"), `${child.pid}\n`, { mode: 0o600 });
  const expectedBytes = String(config.artifacts.image.bytes);
  for (let attempt = 1; attempt <= 20; attempt += 1) {
    try {
      const response = await fetch(
        `http://127.0.0.1:${distribution.port}/${fileName(imageLink)}`,
        { method: "HEAD" },
      );
      if (response.ok && response.headers.get("content-length") === expectedBytes) {
        return { pid: child.pid, workDir: distribution.workDir };
      }
    } catch {
      // The server may still be binding its socket.
    }
    await Bun.sleep(100);
  }
  stopLocalArtifactSource({ pid: child.pid, workDir: distribution.workDir });
  throw new Error("local artifact HTTP server did not become ready");
}

function stopOwnedLocalServer(workDir: string): void {
  const pidPath = join(workDir, "server.pid");
  if (!existsSync(pidPath)) return;
  const pid = Number(readFileSync(pidPath, "utf8").trim());
  if (Number.isInteger(pid) && pid > 1) {
    try {
      process.kill(pid, "SIGTERM");
    } catch {
      // A previous server may already have exited.
    }
  }
}

function stopLocalArtifactSource(source: { pid: number; workDir: string }): void {
  try {
    process.kill(source.pid, "SIGTERM");
  } catch {
    // The server may already have exited.
  }
  rmSync(source.workDir, { recursive: true, force: true });
}

function remoteJobDir(target: NginxTarget): string {
  return `${target.runtime.workDir}/apply-job`;
}

function startRemoteJobScript(target: NginxTarget, script: string): string {
  const jobDir = remoteJobDir(target);
  const runner = `#!/bin/sh
set +e
sh ${jobDir}/apply.sh >${jobDir}/stdout.log 2>${jobDir}/stderr.log
code=$?
printf '%s\\n' "$code" >${jobDir}/exit
`;
  return `set -eu
job_dir=${shQuote(jobDir)}
if [ -f "$job_dir/pid" ] && kill -0 "$(cat "$job_dir/pid")" 2>/dev/null; then
  printf '%s\n' 'nginx apply job is already running' >&2
  exit 1
fi
rm -rf "$job_dir"
install -d -m 0700 "$job_dir"
printf '%s' ${shQuote(Buffer.from(script).toString("base64"))} | base64 -d > "$job_dir/apply.sh"
printf '%s' ${shQuote(Buffer.from(runner).toString("base64"))} | base64 -d > "$job_dir/runner.sh"
chmod 0700 "$job_dir/apply.sh" "$job_dir/runner.sh"
nohup sh "$job_dir/runner.sh" >/dev/null 2>&1 &
printf '%s\n' "$!" > "$job_dir/pid"
printf '{"ok":true,"started":true}\n'
`;
}

function pollRemoteJobScript(target: NginxTarget): string {
  const jobDir = shQuote(remoteJobDir(target));
  return `set -eu
job_dir=${jobDir}
if [ ! -f "$job_dir/exit" ]; then
  printf '{"ok":true,"done":false}\n'
  exit 0
fi
code=$(cat "$job_dir/exit")
stdout=$(tail -c 20000 "$job_dir/stdout.log" 2>/dev/null | base64 -w0)
stderr=$(tail -c 20000 "$job_dir/stderr.log" 2>/dev/null | base64 -w0)
printf '{"ok":true,"done":true,"exitCode":%s,"stdoutBase64":"%s","stderrBase64":"%s"}\n' \
  "$code" "$stdout" "$stderr"
`;
}

function stopRemoteJobScript(target: NginxTarget): string {
  const jobDir = shQuote(remoteJobDir(target));
  return `set -eu
job_dir=${jobDir}
[ ! -f "$job_dir/pid" ] || kill "$(cat "$job_dir/pid")" 2>/dev/null || true
printf '{"ok":true,"stopped":true}\n'
`;
}

function applyRemoteScript(
  config: NginxConfig,
  target: NginxTarget,
  compose: string,
  artifactUrls: { image: string; composePlugin: string },
): string {
  const backendScript = renderBackendScript();
  const unitFiles = target.validation.backends.map((backend) => ({
    path: `/etc/systemd/system/${backend.unit}`,
    content: renderBackendUnit(target.validation.backendScriptPath, backend),
  }));
  const firstRoute = target.runtime.routes[0]!;
  const expected = target.validation.backends.map((backend) => backend.id).join(",");
  const actualExpected = Array.from(
    { length: target.validation.requestCount },
    (_, index) => target.validation.backends[index % target.validation.backends.length]!.id,
  ).join(",");
  const unitNames = target.validation.backends.map((backend) => shQuote(backend.unit)).join(" ");
  const unitPaths = target.validation.backends
    .map((backend) => shQuote(`/etc/systemd/system/${backend.unit}`))
    .join(" ");
  const unitWrites = unitFiles
    .map((unit) => {
      const encoded = shQuote(Buffer.from(unit.content).toString("base64"));
      return `printf '%s' ${encoded} | base64 -d > ${shQuote(unit.path)}`;
    })
    .join("\n");
  const adminBase = [
    "http://127.0.0.1:",
    target.runtime.environment.NGINX_ADMIN_PORT,
    target.runtime.environment.LOGGER_ADMIN_PREFIX,
  ].join("");
  const validationFormat = [
    '{"ok":true,"requests":',
    String(target.validation.requestCount),
    ',"backendSequence":"%s","loggedSequence":"%s",',
    '"unauthorizedStatus":%s,"activeTestBackends":%s,"logCleared":true}\\n',
  ].join("");
  assert(expected.length > 0, "validation backends are missing");
  return `set -eu
image_artifact=${shQuote(config.artifacts.image.remotePath)}
plugin=${shQuote(config.artifacts.composePlugin.remotePath)}
plugin_archive="$plugin.gz"
env_file=${shQuote(target.runtime.envPath)}
compose_file=${shQuote(target.runtime.composePath)}
log_file=${shQuote(`${target.runtime.logDir}/requests.jsonl`)}
backend_script=${shQuote(target.validation.backendScriptPath)}
cleanup_backends() {
  systemctl stop ${unitNames} >/dev/null 2>&1 || true
  systemctl disable ${unitNames} >/dev/null 2>&1 || true
  rm -f ${unitPaths} "$backend_script"
  systemctl daemon-reload >/dev/null 2>&1 || true
}
trap cleanup_backends EXIT
gzip -dc "$env_file.gz" > "$env_file"
chmod 0600 "$env_file"
rm -f "$env_file.gz"
image_sha=$(sha256sum "$image_artifact" 2>/dev/null | awk '{print $1}' || true)
if [ "$image_sha" != ${shQuote(config.artifacts.image.sha256)} ]; then
  rm -f "$image_artifact"
  curl -fsSL --retry 2 --connect-timeout 5 --max-time 3600 \
    ${shQuote(artifactUrls.image)} -o "$image_artifact"
fi
rm -f "$plugin" "$plugin_archive"
curl -fsSL --retry 2 --connect-timeout 5 --max-time 3600 \
  ${shQuote(artifactUrls.composePlugin)} -o "$plugin_archive"
gzip -dc "$plugin_archive" > "$plugin"
rm -f "$plugin_archive"
[ "$(sha256sum "$image_artifact" | awk '{print $1}')" = ${shQuote(config.artifacts.image.sha256)} ]
[ "$(sha256sum "$plugin" | awk '{print $1}')" = ${shQuote(config.artifacts.composePlugin.sha256)} ]
chmod 0755 "$plugin"
docker compose version >/dev/null
chmod 0600 "$env_file"
printf '%s' ${shQuote(Buffer.from(compose).toString("base64"))} | base64 -d > "$compose_file"
printf '%s' ${shQuote(Buffer.from(backendScript).toString("base64"))} | base64 -d > "$backend_script"
chmod 0755 "$backend_script"
${unitWrites}
systemctl daemon-reload
systemctl enable --now ${unitNames} >/dev/null
for backend_port in ${target.validation.backends.map((backend) => backend.port).join(" ")}; do
  for attempt in $(seq 1 10); do
    if curl -fsS http://127.0.0.1:"$backend_port"/ >/dev/null; then break; fi
    [ "$attempt" -lt 10 ] || exit 1
    sleep 1
  done
done
docker load -i "$image_artifact" >/dev/null
loaded_image_id=$(docker image inspect ${shQuote(config.artifacts.image.image)} --format '{{.Id}}')
[ "$loaded_image_id" = ${shQuote(config.artifacts.image.imageId)} ]
docker compose -f "$compose_file" -p ${shQuote(target.runtime.projectName)} up -d --force-recreate
for attempt in $(seq 1 20); do
  if wget -qO- ${adminBase}/health >/dev/null; then break; fi
  [ "$attempt" -lt 20 ] || exit 1
  sleep 1
done
actual=
for sequence in $(seq 1 ${target.validation.requestCount}); do
  backend=$(curl -fsS -D - -o /dev/null http://127.0.0.1:${firstRoute.listenPort}/validation?sequence="$sequence" \
    | awk 'BEGIN { IGNORECASE=1 } /^X-Backend:/ { gsub("\\r", "", $2); print $2; exit }')
  actual="\${actual}\${actual:+,}\${backend}"
done
[ "$actual" = ${shQuote(actualExpected)} ]
set -a
. "$env_file"
set +a
logger_curl() {
  printf 'header = "${target.apiKey.requestHeader}: %s"\n' "$LOGGER_API_KEY" | curl --config - "$@"
}
unauthorized=$(curl -sS -o /dev/null -w '%{http_code}' \
  ${adminBase}/logs/info)
[ "$unauthorized" = 401 ]
logger_curl -fsS -o /tmp/nginx-logger-info.json \
  ${adminBase}/logs/info
logger_curl -fsS -o /tmp/nginx-logger-tail.jsonl \
  '${adminBase}/logs/tail?lines=${target.validation.requestCount}'
logger_curl -fsS -o /tmp/nginx-logger-download.jsonl \
  ${adminBase}/logs/download
[ -s /tmp/nginx-logger-download.jsonl ]
logged=$(python3 -c 'import json,sys; print(",".join(json.loads(x)["backend"] for x in open(sys.argv[1])))' \
  /tmp/nginx-logger-tail.jsonl)
[ "$logged" = ${shQuote(actualExpected)} ]
logger_curl -fsS -X POST -o /tmp/nginx-logger-clear.json \
  ${adminBase}/logs/clear
[ ! -s "$log_file" ]
cleanup_backends
trap - EXIT
active_backends=0
for unit in ${unitNames}; do
  systemctl is-active --quiet "$unit" && active_backends=$((active_backends + 1)) || true
done
printf ${shQuote(validationFormat)} \
  "$actual" "$logged" "$unauthorized" "$active_backends"
`;
}

function statusRemoteScript(config: NginxConfig, target: NginxTarget): string {
  const adminPort = target.runtime.environment.NGINX_ADMIN_PORT;
  const prefix = target.runtime.environment.LOGGER_ADMIN_PREFIX;
  const statusFormat = [
    '{"ok":%s,"running":%s,"imageId":"%s","expectedImageId":"%s",',
    '"healthStatus":%s,"logBytes":%s,"activeTestBackends":%s}\\n',
  ].join("");
  return `set -eu
container=${shQuote(target.runtime.containerName)}
running=false
if [ "$(docker inspect -f '{{.State.Running}}' "$container" 2>/dev/null || true)" = true ]; then running=true; fi
image_id=$(docker inspect -f '{{.Image}}' "$container" 2>/dev/null || true)
health_code=$(curl -sS -o /dev/null -w '%{http_code}' http://127.0.0.1:${adminPort}${prefix}/health 2>/dev/null || true)
log_bytes=$(wc -c < ${shQuote(`${target.runtime.logDir}/requests.jsonl`)} 2>/dev/null || printf 0)
active_backends=0
for unit in ${target.validation.backends.map((backend) => shQuote(backend.unit)).join(" ")}; do
  systemctl is-active --quiet "$unit" && active_backends=$((active_backends + 1)) || true
done
ok=false
[ "$running" = true ] && [ "$image_id" = ${shQuote(config.artifacts.image.imageId)} ] && \
  [ "$health_code" = 200 ] && [ "$active_backends" -eq 0 ] && ok=true
printf ${shQuote(statusFormat)} \
  "$ok" "$running" "$image_id" ${shQuote(config.artifacts.image.imageId)} \
  "\${health_code:-0}" "\${log_bytes:-0}" "$active_backends"
`;
}

function renderCompose(config: NginxConfig, target: NginxTarget): string {
  const environment = {
    ...target.runtime.environment,
    NGINX_ROUTES: renderRoutes(target.runtime.routes),
  };
  const envLines = Object.entries(environment)
    .sort(([left], [right]) => left.localeCompare(right))
    .map(([key, value]) => `      ${key}: ${yamlString(value.replaceAll("$", () => "$$"))}`)
    .join("\n");
  return `name: ${target.runtime.projectName}

services:
  nginx:
    image: ${config.artifacts.image.image}
    container_name: ${target.runtime.containerName}
    restart: ${target.runtime.restart}
    network_mode: ${target.runtime.networkMode}
    mem_limit: ${target.runtime.resources.memory}
    cpus: ${yamlString(target.runtime.resources.cpus)}
    pids_limit: ${target.runtime.resources.pidsLimit}
    env_file:
      - ${target.runtime.envPath}
    environment:
${envLines}
    volumes:
      - ${target.runtime.logDir}:/var/log/logger
    logging:
      driver: json-file
      options:
        max-size: 5m
        max-file: "2"
`;
}

function renderBackendScript(): string {
  return `#!/usr/bin/env python3
import sys
from http.server import BaseHTTPRequestHandler, HTTPServer

host, port, backend = sys.argv[1], int(sys.argv[2]), sys.argv[3]

class Handler(BaseHTTPRequestHandler):
    protocol_version = "HTTP/1.1"
    def do_GET(self):
        body = ('{"backend":"%s"}\\n' % backend).encode("ascii")
        self.send_response(200)
        self.send_header("Content-Type", "application/json")
        self.send_header("Content-Length", str(len(body)))
        self.send_header("X-Backend", backend)
        self.end_headers()
        self.wfile.write(body)
    def log_message(self, format, *args):
        return

HTTPServer((host, port), Handler).serve_forever()
`;
}

function renderBackendUnit(scriptPath: string, backend: Backend): string {
  return `[Unit]
Description=Nginx validation backend ${backend.id}
After=network.target

[Service]
Type=simple
ExecStart=/usr/bin/python3 ${scriptPath} ${backend.bindHost} ${backend.port} ${backend.id}
Restart=on-failure
MemoryMax=32M
CPUQuota=10%

[Install]
WantedBy=multi-user.target
`;
}

function readSecret(target: NginxTarget): SecretMaterial {
  const sourcePath = isAbsolute(target.apiKey.sourceRef)
    ? target.apiKey.sourceRef
    : join(repoRoot, target.apiKey.sourceRef);
  assert(existsSync(sourcePath), `${target.apiKey.sourceRef} is missing`);
  const values = parseEnvFile(readFileSync(sourcePath, "utf8"));
  const value = values[target.apiKey.sourceKey];
  assert(
    typeof value === "string" && value.length >= 16,
    `${target.apiKey.sourceRef} is missing ${target.apiKey.sourceKey}`,
  );
  return {
    sourcePath,
    value,
    fingerprint: fingerprintValues({ [target.apiKey.sourceKey]: value }, [target.apiKey.sourceKey]),
  };
}

function localArtifactSummary(artifact: Artifact & { bytes?: number }): Record<string, unknown> & { valid: boolean } {
  const present = existsSync(artifact.localPath);
  const bytes = present ? statSync(artifact.localPath).size : 0;
  const digest = present ? sha256(readFileSync(artifact.localPath)) : null;
  const valid = present && digest === artifact.sha256 && (artifact.bytes === undefined || bytes === artifact.bytes);
  return { present, bytes, sha256: digest, expectedSha256: artifact.sha256, valid };
}

function uploadArtifact(route: string, localPath: string, remotePath: string): Record<string, unknown> {
  const result = Bun.spawnSync(["trans", route, "upload", localPath, remotePath], {
    stdout: "pipe",
    stderr: "pipe",
  });
  const stdout = new TextDecoder().decode(result.stdout);
  const parsed = parseJsonOutput(stdout);
  return {
    ok: result.exitCode === 0 && parsed?.ok === true,
    localPath,
    remotePath,
    bytes: parsed?.bytes ?? 0,
    sha256: parsed?.sha256 ?? null,
    verified: parsed?.verified === true,
    stderrBytes: result.stderr.length,
  };
}

function uploadSecret(route: string, sourcePath: string, remotePath: string): Record<string, unknown> {
  const directory = mkdtempSync(join(tmpdir(), "unidesk-nginx-secret-"));
  const transferPath = join(directory, "runtime.env.gz");
  try {
    writeFileSync(transferPath, gzipSync(readFileSync(sourcePath)));
    chmodSync(transferPath, 0o600);
    const result = uploadArtifact(route, transferPath, `${remotePath}.gz`);
    return { ...result, localPath: "gzip-secret-transfer", valuesPrinted: false };
  } finally {
    rmSync(directory, { recursive: true, force: true });
  }
}

function decodeBase64(value: string): string {
  return Buffer.from(value, "base64").toString("utf8");
}

function remoteJobSummary(outcome: RemoteJobOutcome, full: boolean): Record<string, unknown> {
  return {
    exitCode: outcome.exitCode,
    polls: outcome.polls,
    stdoutBytes: Buffer.byteLength(outcome.stdout),
    stderrBytes: Buffer.byteLength(outcome.stderr),
    stdoutTail: full ? outcome.stdout.slice(-8000) : "",
    stderrTail: full ? outcome.stderr.slice(-4000) : "",
  };
}

function targetSummary(id: string, target: NginxTarget): Record<string, unknown> {
  return {
    id,
    route: target.route,
    runtime: "host-docker-compose",
    container: target.runtime.containerName,
    resources: target.runtime.resources,
    routes: target.runtime.routes,
    adminPort: target.runtime.environment.NGINX_ADMIN_PORT,
  };
}

function validationSummary(target: NginxTarget): Record<string, unknown> {
  return {
    requestCount: target.validation.requestCount,
    concurrency: 1,
    backends: "host-python-systemd-temporary",
    cleanupAfterSuccess: target.validation.cleanupAfterSuccess,
  };
}

function secretSummary(target: NginxTarget, secret: SecretMaterial): Record<string, unknown> {
  return {
    sourceRef: target.apiKey.sourceRef,
    sourceKey: target.apiKey.sourceKey,
    targetKey: target.apiKey.targetKey,
    present: true,
    fingerprint: secret.fingerprint,
    valuesPrinted: false,
  };
}

function failedApply(
  id: string,
  target: NginxTarget,
  stage: string,
  remote: Awaited<ReturnType<typeof capture>>,
  secret: SecretMaterial,
): Record<string, unknown> {
  return {
    ok: false,
    action: "platform-infra-nginx-apply",
    mode: "confirmed",
    stage,
    target: targetSummary(id, target),
    apiKey: secretSummary(target, secret),
    remote: compactCapture(remote, { full: true }),
  };
}

function renderRoutes(routes: Route[]): string {
  return routes
    .map((route) => {
      const upstreams = route.upstreams.map((upstream) => `${upstream.host}:${upstream.port}`).join(",");
      return `${route.listenPort}=${upstreams}`;
    })
    .join(";");
}

function sha256(value: string | Buffer): string {
  return createHash("sha256").update(value).digest("hex");
}

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

function fileName(path: string): string {
  return path.slice(path.lastIndexOf("/") + 1);
}

function absolutePath(value: string, path: string): void {
  assert(typeof value === "string" && isAbsolute(value), `${configLabel}.${path} must be an absolute path`);
}

function simpleName(value: string, path: string): void {
  assert(typeof value === "string" && /^[A-Za-z0-9._-]+$/u.test(value), `${configLabel}.${path} is invalid`);
}

function port(value: number, path: string): void {
  assert(Number.isInteger(value) && value >= 1 && value <= 65535, `${configLabel}.${path} must be a valid port`);
}

function assert(condition: unknown, message: string): asserts condition {
  if (!condition) throw new Error(message);
}
