import { afterEach, describe, expect, test } from "bun:test";
import { spawnSync } from "node:child_process";
import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { dirname, join } from "node:path";

import { rootPath } from "../../src/config";
import { renderGitOpsResources } from "./publish-unidesk-host-gitops.mjs";

const temporaryRoots: string[] = [];

afterEach(() => {
  for (const root of temporaryRoots.splice(0)) rmSync(root, { recursive: true, force: true });
});

describe("UniDesk Host GitOps publisher", () => {
  test("renders the YAML-owned HWLAB Code Agent runtime into the scripts ConfigMap", () => {
    const [resource] = renderGitOpsResources({
      resources: [{
        id: "hwlab-nc01-v03-runtime-gitops-scripts",
        renderer: "hwlab-runtime-gitops-scripts",
        configRef: "config/hwlab-node-lanes.yaml#lanes.v03.targets.NC01",
        manifestPath: "deploy/gitops/unidesk-host/hwlab-runtime-gitops-scripts.yaml",
        namespace: "hwlab-ci",
      }],
    }, rootPath());

    const configMap = Bun.YAML.parse(resource.content) as Record<string, any>;
    const overlay = JSON.parse(configMap.data["runtime-gitops-overlay.json"]);
    expect(overlay.codeAgentRuntime).toMatchObject({
      enabled: true,
      managerUrl: "http://agentrun-mgr.agentrun-v02.svc.cluster.local:8080",
      runnerNamespace: "agentrun-v02",
      secretNamespace: "agentrun-v02",
      kafkaEventBridge: {
        features: {
          directPublish: true,
          liveKafkaSse: true,
          transactionalProjector: false,
        },
      },
    });
  });

  test("image skip publishes changed GitOps resources without changing service state", () => {
    const root = mkdtempSync(join(tmpdir(), "unidesk-host-gitops-skip-"));
    temporaryRoots.push(root);
    const seed = join(root, "seed");
    const bare = join(root, "gitops.git");
    const releaseDir = join(root, "release");
    const publisherWorktree = join(root, "publisher");
    const configPath = join(root, "config.yaml");
    const statePath = "deploy/gitops-state/unidesk-host/todo-note.json";
    const resourcePath = "deploy/gitops/unidesk-host/hwlab-runtime-gitops-scripts.yaml";
    const runtimeSourceCommit = "a".repeat(40);
    const sourceCommit = "b".repeat(40);
    const digest = `sha256:${"c".repeat(64)}`;

    mkdirSync(seed);
    git(["init", "-b", "unidesk-host-gitops"], seed);
    identity(seed);
    write(seed, "deploy/gitops/unidesk-host/todo-note.yaml", "existing service manifest\n");
    write(seed, resourcePath, "stale configmap must remain unchanged\n");
    write(seed, statePath, `${JSON.stringify({
      version: 1,
      kind: "UniDeskHostReleaseState",
      serviceRef: "services.todoNote",
      sourceCommit: runtimeSourceCommit,
      digest,
      digestRef: `registry.example/todo-note@${digest}`,
    }, null, 2)}\n`);
    git(["add", "."], seed);
    git(["commit", "-m", "seed gitops"], seed);
    const originalHead = git(["rev-parse", "HEAD"], seed).stdout.trim();
    git(["init", "--bare", bare], root);
    git(["remote", "add", "origin", `file://${bare}`], seed);
    git(["push", "origin", "unidesk-host-gitops"], seed);

    const serviceReleaseDir = join(releaseDir, "services", "todo-note");
    mkdirSync(serviceReleaseDir, { recursive: true });
    writeFileSync(join(serviceReleaseDir, "action"), "skip\n");
    writeFileSync(join(serviceReleaseDir, "reason"), "image-inputs-unchanged\n");
    writeFileSync(join(serviceReleaseDir, "baseline-source-commit"), `${runtimeSourceCommit}\n`);
    writeFileSync(configPath, Bun.YAML.stringify({
      services: { todoNote: {} },
      delivery: {
        enabled: true,
        services: [{
          id: "todo-note",
          serviceRef: "services.todoNote",
          imagePaths: ["src/components/microservices/todo-note/src"],
          image: { repository: "registry.example/todo-note" },
          gitops: {
            manifestPath: "deploy/gitops/unidesk-host/todo-note.yaml",
            releaseStatePath: statePath,
          },
        }],
        gitops: {
          readUrl: `file://${bare}`,
          writeUrl: `file://${bare}`,
          branch: "unidesk-host-gitops",
          resources: [{
            id: "hwlab-runtime-gitops-scripts",
            renderer: "hwlab-runtime-gitops-scripts",
            configRef: "config/hwlab-node-lanes.yaml#lanes.v03.targets.NC01",
            manifestPath: resourcePath,
            namespace: "hwlab-ci",
          }],
          author: { name: "UniDesk Test", email: "unidesk-test@example.invalid" },
        },
      },
    }));

    const result = spawnSync("bun", [
      "scripts/native/cicd/publish-unidesk-host-gitops.mjs",
      "--config", configPath,
      "--source-root", rootPath(),
      "--metadata", join(root, "unused-metadata"),
      "--release-dir", releaseDir,
      "--source-commit", sourceCommit,
      "--worktree", publisherWorktree,
    ], { cwd: rootPath(), encoding: "utf8", timeout: 30_000 });
    expect(result.status, result.stderr).toBe(0);
    const output = JSON.parse(result.stdout) as Record<string, any>;
    expect(output).toMatchObject({
      action: "skip",
      status: "published",
      imageStatus: "skipped",
      resourceStatus: "published",
      resourceChanged: true,
      changed: true,
      pushAttempts: 1,
      resources: [{ id: "hwlab-runtime-gitops-scripts", path: resourcePath }],
    });
    expect(output.gitopsCommit).not.toBe(originalHead);
    expect(git(["rev-parse", "refs/heads/unidesk-host-gitops"], bare).stdout.trim()).toBe(output.gitopsCommit);
    expect(show(bare, resourcePath)).not.toBe("stale configmap must remain unchanged\n");
    expect(JSON.parse(show(bare, statePath)).sourceCommit).toBe(runtimeSourceCommit);
  });
});

function write(root: string, relativePath: string, content: string) {
  const path = join(root, relativePath);
  mkdirSync(dirname(path), { recursive: true });
  writeFileSync(path, content);
}

function identity(root: string) {
  git(["config", "user.name", "UniDesk Test"], root);
  git(["config", "user.email", "unidesk-test@example.invalid"], root);
}

function show(bare: string, relativePath: string): string {
  return git(["show", `refs/heads/unidesk-host-gitops:${relativePath}`], bare).stdout;
}

function git(args: string[], cwd: string) {
  const result = spawnSync("git", args, { cwd, encoding: "utf8" });
  if (result.status !== 0) throw new Error(`git ${args.join(" ")} failed: ${result.stderr || result.stdout}`);
  return result;
}
