import { expect, test } from "bun:test";
import { createHash } from "node:crypto";
import { spawnSync } from "node:child_process";
import { readFileSync } from "node:fs";
import { resolve } from "node:path";
import { renderGiteaRemotePayloadMaterializer, summarizeGiteaRemotePayloads } from "./platform-infra-gitea-payload";
import { renderGiteaWebhookSyncDesiredManifest } from "./platform-infra-gitea";

const remoteScriptPath = resolve(import.meta.dir, "platform-infra-gitea-remote.sh");
const orchestrationPath = resolve(import.meta.dir, "platform-infra-gitea.ts");

function functionSource(source: string, name: string, nextName: string): string {
  const start = source.indexOf(`${name}() {`);
  const end = source.indexOf(`\n${nextName}() {`, start);
  if (start < 0 || end < 0) throw new Error(`missing shell function ${name}`);
  return source.slice(start, end);
}

test("stdin file envelope materializes a payload larger than the current Gitea manifest without argv or env", () => {
  const currentManifest = renderGiteaWebhookSyncDesiredManifest("NC01");
  const oversizedManifest = `${currentManifest}\n${"payload-$HOME-'quote'-$(command)\n".repeat(8192)}`;
  expect(Buffer.byteLength(oversizedManifest)).toBeGreaterThan(Buffer.byteLength(currentManifest));
  expect(Buffer.byteLength(Buffer.from(oversizedManifest).toString("base64"))).toBeGreaterThan(128 * 1024);

  const payloads = {
    manifest: oversizedManifest,
    repositories: JSON.stringify([{ key: "sentinel", branch: "master" }]),
    hookTopology: JSON.stringify({ version: 1, repositories: [] }),
    statusEvaluator: "print('ok')\n",
  };
  const materializer = renderGiteaRemotePayloadMaterializer(payloads);
  const result = spawnSync("sh", ["-s"], {
    encoding: "utf8",
    input: [
      "set -eu",
      'tmp="$(mktemp -d)"',
      'trap \'rm -rf "$tmp"\' EXIT',
      materializer,
      'unidesk_gitea_materialize_payloads "$tmp"',
      "python3 - \"$tmp\" <<'PY'",
      "import hashlib, json, pathlib, sys",
      "root = pathlib.Path(sys.argv[1])",
      "print(json.dumps({p.name: {'bytes': p.stat().st_size, 'sha256': hashlib.sha256(p.read_bytes()).hexdigest()} for p in root.iterdir()}, sort_keys=True))",
      "PY",
    ].join("\n"),
  });
  expect(result.status).toBe(0);
  expect(result.stderr).toBe("");
  const observed = JSON.parse(result.stdout) as Record<string, { bytes: number; sha256: string }>;
  expect(observed["gitea.k8s.yaml"]).toEqual({
    bytes: Buffer.byteLength(oversizedManifest),
    sha256: createHash("sha256").update(oversizedManifest).digest("hex"),
  });
  expect(materializer).not.toContain("UNIDESK_GITEA_MANIFEST_B64");

  const summary = summarizeGiteaRemotePayloads(payloads) as any;
  expect(summary.kind).toBe("trans-stdin-file-envelope");
  expect(summary.valuesPrinted).toBe(false);
  expect(JSON.stringify(summary)).not.toContain("payload-$HOME");
});

test("Gitea remote runner consumes materialized files instead of base64 environment variables", () => {
  const source = readFileSync(remoteScriptPath, "utf8");
  expect(source).toContain('unidesk_gitea_materialize_payloads "$tmp"');
  expect(source).toContain('$tmp/gitea.k8s.yaml');
  expect(source).toContain('$tmp/repos.json');
  expect(source).toContain('$tmp/hook-topology.json');
  expect(source).not.toContain("UNIDESK_GITEA_MANIFEST_B64");
  expect(source).not.toContain("UNIDESK_GITEA_STATUS_EVALUATOR_B64");
  expect(source).not.toContain("UNIDESK_GITEA_REPOS_B64");
  expect(source).not.toContain("UNIDESK_GITEA_FRPC_TOML_B64");
  expect(source).not.toContain("UNIDESK_GITEA_FRPC_ENABLED");
  expect(source).not.toContain("frpc-rollout");
  expect(source).toContain("kubectl apply --server-side --force-conflicts --dry-run=server");
  expect(source).toContain("target server dry-run failed; manifest apply skipped");
  expect(source.indexOf("kubectl apply --server-side --force-conflicts --dry-run=server")).toBeLessThan(
    source.lastIndexOf('timeout "$UNIDESK_GITEA_WAIT_TIMEOUT_SECONDS" kubectl apply --server-side --force-conflicts'),
  );
});

test("mirror bootstrap materializes every selected GitHub credential key before repository mutation", () => {
  const source = readFileSync(remoteScriptPath, "utf8");
  const orchestration = readFileSync(orchestrationPath, "utf8");
  const upsert = functionSource(source, "upsert_webhook_sync_secret", "run_apply");
  const bootstrap = functionSource(source, "run_mirror_bootstrap", "run_mirror_sync");
  expect(bootstrap.indexOf("upsert_webhook_sync_secret")).toBeLessThan(bootstrap.indexOf('pod="$(kubectl'));
  expect(bootstrap).toContain('"apiBootstrap": {"exitCode": None, "skipped": True}');
  expect(bootstrap).toContain("return 1");
  expect(orchestration).toContain("ensureMirrorSecrets(gitea, target, repositoriesForTarget(gitea, target), true, true, true)");
  expect(orchestration).toContain("Object.keys(params.secrets.githubTokens)");
  expect(orchestration).toContain("env.UNIDESK_GITEA_GITHUB_SECRET_ENV_MAP");

  const result = spawnSync("sh", ["-s"], {
    encoding: "utf8",
    input: [
      "set -u",
      'tmp="$(mktemp -d)"',
      'trap \'rm -rf "$tmp"\' EXIT',
      'export tmp UNIDESK_GITEA_WEBHOOK_SYNC_ENABLED=1 UNIDESK_GITEA_DRY_RUN=0 UNIDESK_GITEA_NAMESPACE=devops-infra UNIDESK_GITEA_WEBHOOK_SECRET_NAME=gitea-github-sync-secrets UNIDESK_GITEA_FIELD_MANAGER=test-manager',
      'export UNIDESK_GITEA_GITHUB_SECRET_ENV_MAP="github-token=GLOBAL_TOKEN,github-token-pikainc-selfmedia=SELFMEDIA_TOKEN"',
      'export GLOBAL_TOKEN=fixture-global SELFMEDIA_TOKEN=fixture-selfmedia UNIDESK_GITEA_ADMIN_USERNAME=fixture-admin UNIDESK_GITEA_ADMIN_PASSWORD=fixture-password UNIDESK_GITEA_GITHUB_WEBHOOK_SECRET=fixture-webhook',
      'kubectl() { if [ "$1" = "-n" ] && [ "$3" = "create" ]; then printf "%s\\n" "apiVersion: v1" "kind: Secret"; printf "%s\\n" "$*" >>"$tmp/kubectl.log"; return 0; fi; if [ "$1" = "apply" ]; then cat >/dev/null; printf "%s\\n" "$*" >>"$tmp/kubectl.log"; return 0; fi; return 1; }',
      upsert,
      "upsert_webhook_sync_secret",
      'cat "$tmp/kubectl.log"',
    ].join("\n"),
  });
  expect(result.status).toBe(0);
  expect(result.stdout).toContain("--from-file=github-token=");
  expect(result.stdout).toContain("--from-file=github-token-pikainc-selfmedia=");
  expect(result.stdout).not.toContain("fixture-global");
  expect(result.stdout).not.toContain("fixture-selfmedia");

  const failed = spawnSync("sh", ["-s"], {
    encoding: "utf8",
    input: [
      "set -u",
      'tmp="$(mktemp -d)"',
      'trap \'rm -rf "$tmp"\' EXIT',
      'export tmp UNIDESK_GITEA_WEBHOOK_SYNC_ENABLED=1 UNIDESK_GITEA_DRY_RUN=0 UNIDESK_GITEA_NAMESPACE=devops-infra UNIDESK_GITEA_WEBHOOK_SECRET_NAME=gitea-github-sync-secrets UNIDESK_GITEA_FIELD_MANAGER=test-manager UNIDESK_GITEA_TARGET_ID=NC01',
      'export UNIDESK_GITEA_GITHUB_SECRET_ENV_MAP="github-token=GLOBAL_TOKEN,github-token-pikainc-selfmedia=SELFMEDIA_TOKEN"',
      'export GLOBAL_TOKEN=fixture-global SELFMEDIA_TOKEN=fixture-selfmedia UNIDESK_GITEA_ADMIN_USERNAME=fixture-admin UNIDESK_GITEA_ADMIN_PASSWORD=fixture-password UNIDESK_GITEA_GITHUB_WEBHOOK_SECRET=fixture-webhook',
      'mirror_repos_json() { printf "%s\\n" "[]" >"$tmp/repos.json"; }',
      'kubectl() { if [ "$1" = "-n" ] && [ "$3" = "create" ]; then printf "%s\\n" "apiVersion: v1" "kind: Secret"; return 0; fi; if [ "$1" = "apply" ]; then cat >/dev/null; return 1; fi; touch "$tmp/unexpected-mutation"; return 1; }',
      upsert,
      bootstrap,
      "run_mirror_bootstrap >\"$tmp/result.json\"",
      'rc=$?',
      'python3 - "$tmp/result.json" "$tmp/unexpected-mutation" "$rc" <<\'PY\'',
      "import json, pathlib, sys",
      "payload=json.load(open(sys.argv[1], encoding='utf-8'))",
      "assert int(sys.argv[3]) == 1",
      "assert payload['steps']['apiBootstrap']['skipped'] is True",
      "assert payload['valuesPrinted'] is False",
      "assert not pathlib.Path(sys.argv[2]).exists()",
      "print('bootstrap-secret-fail-closed')",
      "PY",
    ].join("\n"),
  });
  expect(failed.status).toBe(0);
  expect(failed.stdout).toContain("bootstrap-secret-fail-closed");
  expect(failed.stdout).not.toContain("fixture-global");
  expect(failed.stdout).not.toContain("fixture-selfmedia");
});
