#!/usr/bin/env bun

import { spawnSync } from "node:child_process";
import { createHash } from "node:crypto";
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { isAbsolute, join, resolve } from "node:path";

const options = parseOptions(process.argv.slice(2));
const sourceRoot = resolve(options.sourceRoot);
const configPath = resolve(sourceRoot, options.config);
const root = Bun.YAML.parse(readFileSync(configPath, "utf8"));
const plan = readPlan(root, sourceRoot, configPath);
verifySourceCommit(sourceRoot, options.sourceCommit, plan.sourceCommitEnvironment);

if (options.dryRun) {
  output({ ok: true, action: "platform-infra-public-edge-delivery-plan", mutation: false, sourceCommit: options.sourceCommit, ...plan.summary });
  process.exit(0);
}

const token = process.env[plan.secret.targetKey]?.trim() ?? "";
if (token.length === 0) fail(`缺少 SecretRef 投影环境变量：${plan.secret.targetKey}`);

const temporaryRoot = mkdtempSync(join(tmpdir(), "unidesk-public-edge-delivery-"));
try {
  const archivePath = createArchive(sourceRoot, options.sourceCommit, plan.sourcePaths, temporaryRoot);
  const archiveSha256 = sha256File(archivePath);
  const route = `${plan.route}:${plan.hostCwd}`;
  const remoteArchive = `${plan.archiveRoot}/incoming/public-edge-${options.sourceCommit}.tar.gz`;
  const transPath = join(sourceRoot, "scripts/trans");
  const env = { ...process.env, UNIDESK_TRANS_REPO_ROOT: sourceRoot };
  runTrans(transPath, [route, "sh", "--", `umask 077; mkdir -p ${shellQuote(`${plan.archiveRoot}/incoming`)} ${shellQuote(`${plan.archiveRoot}/tmp`)}`], env);
  runTrans(transPath, [route, "upload", archivePath, remoteArchive], env);
  const remote = runTrans(transPath, [route, "sh"], env, remoteApplyScript({
    archivePath: remoteArchive,
    archiveRoot: plan.archiveRoot,
    archiveSha256,
    authorityId: plan.authorityId,
    config: options.config,
    entrypoint: plan.entrypoint,
    sourceCommit: options.sourceCommit,
    targetId: plan.targetId,
  }), 150_000);
  const runtime = parseOutput(remote.stdout);
  if (runtime.ok !== true) fail("host-local public-edge reconcile 未返回 ok=true");
  output({
    ok: true,
    action: "platform-infra-public-edge-delivery",
    mutation: runtime.mutation === true,
    sourceCommit: options.sourceCommit,
    archive: { sha256: `sha256:${archiveSha256}`, sourcePaths: plan.sourcePaths },
    runtime,
    ...plan.summary,
  });
} finally {
  rmSync(temporaryRoot, { recursive: true, force: true });
}

function parseOptions(args) {
  let sourceRoot = null;
  let sourceCommit = null;
  let config = "config/platform-infra/public-edge.yaml";
  let dryRun = false;
  let confirm = false;
  for (let index = 0; index < args.length; index += 1) {
    const arg = args[index];
    if (arg === "--dry-run") dryRun = true;
    else if (arg === "--confirm") confirm = true;
    else if (arg === "--source-root" || arg === "--source-commit" || arg === "--config") {
      const value = args[index + 1];
      if (value === undefined || value.startsWith("--")) fail(`${arg} 需要参数`);
      if (arg === "--source-root") sourceRoot = value;
      else if (arg === "--source-commit") sourceCommit = value;
      else config = value;
      index += 1;
    } else fail(`不支持的参数：${arg}`);
  }
  if (sourceRoot === null) fail("缺少 --source-root");
  if (sourceCommit === null || !/^[0-9a-f]{40}$/u.test(sourceCommit)) fail("--source-commit 必须是 40 位 commit");
  if (dryRun === confirm) fail("必须且只能选择 --dry-run 或 --confirm");
  return { sourceRoot, sourceCommit, config, dryRun, confirm };
}

function readPlan(root, sourceRoot, configPath) {
  const delivery = record(root.delivery, "delivery");
  if (delivery.enabled !== true) fail("delivery.enabled 必须为 true");
  const defaults = record(root.defaults, "defaults");
  const targetId = string(defaults.targetId, "defaults.targetId");
  const target = record(record(root.targets, "targets")[targetId], `targets.${targetId}`);
  const transport = record(delivery.transport, "delivery.transport");
  const secret = record(transport.secretRef, "delivery.transport.secretRef");
  const helper = record(delivery.hostHelper, "delivery.hostHelper");
  const sourcePaths = array(helper.sourcePaths, "delivery.hostHelper.sourcePaths").map((value, index) => relativePath(value, `delivery.hostHelper.sourcePaths[${index}]`));
  const entrypoint = relativePath(helper.entrypoint, "delivery.hostHelper.entrypoint");
  const configRelative = relativeWithin(sourceRoot, configPath);
  if (!sourcePaths.some((path) => entrypoint === path || entrypoint.startsWith(`${path}/`))) fail("host helper 不在 sourcePaths 中");
  if (!sourcePaths.some((path) => configRelative === path || configRelative.startsWith(`${path}/`))) fail("public-edge config 不在 sourcePaths 中");
  const secretSummary = {
    configRef: relativePath(secret.configRef, "delivery.transport.secretRef.configRef"),
    targetId: string(secret.targetId, "delivery.transport.secretRef.targetId"),
    secretName: string(secret.secretName, "delivery.transport.secretRef.secretName"),
    targetKey: string(secret.targetKey, "delivery.transport.secretRef.targetKey"),
  };
  verifySecretBinding(sourceRoot, secretSummary);
  const authorityId = string(delivery.authorityId, "delivery.authorityId");
  const archiveRoot = absolutePath(helper.archiveRoot, "delivery.hostHelper.archiveRoot");
  return {
    authorityId,
    sourceCommitEnvironment: string(delivery.sourceCommitEnvironment, "delivery.sourceCommitEnvironment"),
    targetId,
    route: string(target.route, `targets.${targetId}.route`),
    hostCwd: absolutePath(transport.hostCwd, "delivery.transport.hostCwd"),
    archiveRoot,
    entrypoint,
    sourcePaths,
    secret: secretSummary,
    summary: {
      authorityId,
      sourceBranch: string(delivery.sourceBranch, "delivery.sourceBranch"),
      pipelineId: string(delivery.pipelineId, "delivery.pipelineId"),
      configRef: `${configRelative}#targets.${targetId}`,
      hostHelper: { archiveRoot, entrypoint, sourcePaths },
      secretRef: secretSummary,
    },
  };
}

function verifySecretBinding(sourceRoot, secret) {
  const root = Bun.YAML.parse(readFileSync(resolve(sourceRoot, secret.configRef), "utf8"));
  const target = array(root.targets, "secret targets").find((item) => record(item, "secret target").id === secret.targetId);
  if (target === undefined || record(target, "secret target").enabled !== true) fail(`Secret target 未启用：${secret.targetId}`);
  const binding = array(root.kubernetesSecrets, "kubernetesSecrets").find((item) => record(item, "secret binding").name === secret.secretName);
  if (binding === undefined) fail(`Secret binding 不存在：${secret.secretName}`);
  const item = array(record(binding, "secret binding").data, "secret data").find((entry) => record(entry, "secret data").targetKey === secret.targetKey);
  if (item === undefined) fail(`Secret targetKey 不存在：${secret.targetKey}`);
}

function verifySourceCommit(sourceRoot, sourceCommit, environmentName) {
  if ((process.env[environmentName]?.trim() ?? "") !== sourceCommit) fail(`${environmentName} 与 --source-commit 不一致`);
  const result = runChecked("git", ["rev-parse", "HEAD"], sourceRoot);
  if (result.stdout.trim() !== sourceCommit) fail("source checkout HEAD 与 --source-commit 不一致");
}

function createArchive(sourceRoot, sourceCommit, sourcePaths, temporaryRoot) {
  const tarPath = join(temporaryRoot, "source.tar");
  const markerPath = join(temporaryRoot, ".unidesk-source-commit");
  const ancestryPath = join(temporaryRoot, ".unidesk-source-ancestors");
  writeFileSync(markerPath, `${sourceCommit}\n`, { mode: 0o600 });
  const ancestry = runChecked("git", ["rev-list", sourceCommit], sourceRoot).stdout;
  writeFileSync(ancestryPath, ancestry, { mode: 0o600 });
  runChecked("git", ["archive", "--format=tar", "--prefix=source/", `--output=${tarPath}`, sourceCommit, "--", ...sourcePaths], sourceRoot);
  runChecked("tar", ["--append", "--file", tarPath, "--directory", temporaryRoot, ".unidesk-source-commit", ".unidesk-source-ancestors"], sourceRoot);
  runChecked("gzip", ["-n", tarPath], sourceRoot);
  return `${tarPath}.gz`;
}

function remoteApplyScript(input) {
  return `set -eu
archive=${shellQuote(input.archivePath)}
archive_root=${shellQuote(input.archiveRoot)}
expected_sha=${shellQuote(input.archiveSha256)}
source_commit=${shellQuote(input.sourceCommit)}
actual_sha="$(sha256sum "$archive" | awk '{print $1}')"
test "$actual_sha" = "$expected_sha"
tmp="$(mktemp -d "$archive_root/tmp/$source_commit.XXXXXX")"
cleanup() { rm -rf "$tmp"; rm -f "$archive"; }
trap cleanup EXIT HUP INT TERM
tar -xzf "$archive" -C "$tmp"
test "$(cat "$tmp/.unidesk-source-commit")" = "$source_commit"
cd "$tmp/source"
bun ${shellQuote(input.entrypoint)} --source-root "$tmp/source" --source-commit "$source_commit" --authority ${shellQuote(input.authorityId)} --config ${shellQuote(input.config)} --target ${shellQuote(input.targetId)} --confirm
`;
}

function runTrans(path, args, env, input, timeout = 60_000) {
  const result = spawnSync(path, args, { encoding: "utf8", env, input, timeout, maxBuffer: 16 * 1024 * 1024 });
  if (result.status !== 0) {
    const detail = result.stderr || result.stdout || result.error?.message || `status=${result.status ?? "null"} signal=${result.signal ?? "none"}`;
    fail(`trans ${args.slice(1, 3).join(" ")} 失败：${detail}`);
  }
  return result;
}

function runChecked(command, args, cwd) {
  const result = spawnSync(command, args, { cwd, encoding: "utf8", timeout: 60_000, maxBuffer: 16 * 1024 * 1024 });
  if (result.status !== 0) fail(`${command} ${args.join(" ")} 失败：${result.stderr || result.stdout}`);
  return result;
}

function parseOutput(stdout) {
  const text = stdout.trim();
  for (const candidate of [text, text.slice(text.lastIndexOf("\n") + 1)]) {
    try {
      const parsed = JSON.parse(candidate);
      if (parsed && typeof parsed === "object") return parsed;
    } catch {}
  }
  return { ok: false, error: "remote-output-invalid", stdoutTail: text.slice(-2000) };
}

function record(value, label) {
  if (value === null || typeof value !== "object" || Array.isArray(value)) fail(`${label} 必须是对象`);
  return value;
}
function array(value, label) {
  if (!Array.isArray(value) || value.length === 0) fail(`${label} 必须是非空数组`);
  return value;
}
function string(value, label) {
  if (typeof value !== "string" || value.trim().length === 0) fail(`${label} 必须是非空字符串`);
  return value.trim();
}
function absolutePath(value, label) {
  const path = string(value, label);
  if (!isAbsolute(path)) fail(`${label} 必须是绝对路径`);
  return path.replace(/\/+$/u, "") || "/";
}
function relativePath(value, label) {
  const path = string(value, label);
  if (isAbsolute(path) || !/^[A-Za-z0-9._/-]+$/u.test(path) || path.split("/").some((part) => part === "" || part === "..")) fail(`${label} 必须是仓库相对路径`);
  return path;
}
function relativeWithin(root, path) {
  const prefix = `${root.replace(/\/+$/u, "")}/`;
  if (!path.startsWith(prefix)) fail("--config 必须位于 source root");
  return path.slice(prefix.length);
}
function sha256File(path) {
  return createHash("sha256").update(readFileSync(path)).digest("hex");
}
function shellQuote(value) {
  return `'${String(value).replace(/'/gu, `'\\''`)}'`;
}
function output(value) {
  process.stdout.write(`${JSON.stringify({ ...value, valuesPrinted: false }, null, 2)}\n`);
}
function fail(message) {
  process.stderr.write(`${JSON.stringify({ ok: false, action: "platform-infra-public-edge-delivery", error: message, valuesPrinted: false })}\n`);
  process.exit(1);
}
