import { describe, expect, test } from "bun:test";

import { parseSecretSyncOptions } from "./agentrun/control-plane";
import type { SecretSyncOptions } from "./agentrun/options";
import { compactAgentRunSecretSyncPayload, renderAgentRunSecretSyncResult } from "./agentrun/secret-sync-output";
import { confirmedSecretSyncPlan } from "./agentrun/trigger";

const options = (output: SecretSyncOptions["output"], overrides: Partial<SecretSyncOptions> = {}): SecretSyncOptions => ({
  confirm: false,
  dryRun: true,
  node: "NC01",
  lane: "nc01-v02",
  secretIds: [],
  full: false,
  raw: false,
  output,
  ...overrides,
});

function blockedFixture(): Record<string, unknown> {
  const planItems = Array.from({ length: 13 }, (_, index) => {
    const id = index === 11 ? "tool-github-ssh-private-key" : index === 12 ? "tool-github-ssh-known-hosts" : `fixture-secret-${index}`;
    return {
      id,
      namespace: "agentrun-v02",
      secret: index >= 11 ? "agentrun-v01-tool-github-ssh" : `fixture-target-${index}`,
      key: index === 11 ? "id_ed25519" : index === 12 ? "known_hosts" : `key-${index}`,
      sourceMode: "file",
      sourcePath: "/root/.unidesk/.state/secrets/<redacted>",
      present: true,
      sourceFilePresent: true,
      fingerprint: `sha256:${String(index).padStart(64, "a")}`,
      valueBytes: 120 + index,
      degradedReason: null,
      valuesPrinted: false,
    };
  });
  return {
    ok: false,
    command: "agentrun control-plane secret-sync",
    mode: "dry-run-blocked-before-mutation",
    status: "blocked",
    mutation: false,
    blockedBeforeMutation: true,
    secretSyncStarted: false,
    target: { node: "NC01", lane: "nc01-v02", namespace: "agentrun-v02", valuesPrinted: false },
    degradedReason: "provider-secret-activation-fence-unavailable",
    plan: { secretCount: planItems.length, items: planItems, omittedCount: 0, valuesPrinted: false },
    activationPreflight: {
      ok: false,
      status: "blocked",
      reason: "provider-secret-activation-fence-unavailable",
      targetSecretCount: 1,
      targetSecrets: [{
        namespace: "agentrun-v02",
        name: "agentrun-v01-provider-codex",
        desiredKeys: ["auth.json", "config.toml"],
        queryOk: true,
        present: true,
        mutationCount: 1,
        noOp: false,
        mutations: [{
          id: "provider-codex-config",
          key: "config.toml",
          desiredFingerprintSuffix: "aaaaaaaaaaaa",
          runtimeFingerprintSuffix: "bbbbbbbbbbbb",
          changeKind: "change-key",
          valuesPrinted: false,
        }],
        valuesPrinted: false,
      }],
      activationRiskCount: 1,
      activationRisks: [{
        pod: "agentrun-runner-rjob_fixture-pod",
        jobName: "agentrun-runner-rjob_fixture",
        phase: "Pending",
        runId: "run_fixture",
        commandId: "cmd_fixture",
        runnerJobId: "rjob_fixture",
        waitingReasons: ["CreateContainerConfigError"],
        classification: "nonterminal-runner-required-provider-secret-missing",
        dependencies: [{
          source: "volume-secret",
          name: "agentrun-v01-provider-codex",
          affectedDesiredKeys: ["config.toml"],
          mutationKinds: ["change-key"],
          reason: "nonterminal-runner-provider-secret-mutation",
          valuesPrinted: false,
        }],
        valuesPrinted: false,
      }],
      requiresManagerFence: true,
      targetSecretObservationComplete: true,
      runnerObservationComplete: true,
      allObservationsComplete: true,
      providerSecretMutationCount: 1,
      providerSecretDataNoOp: false,
      providerSecretMutationAuthorized: false,
      managerAuthority: {
        owner: "agentrun-manager-provider-activation-fence",
        atomicFenceAvailable: false,
        mutationAuthorized: false,
        noOpOnly: true,
        trackingIssue: "https://github.com/pikasTech/agentrun/issues/284",
        valuesPrinted: false,
      },
      capture: {
        stdoutTail: "FULL_PROBE_BODY_ONLY_VISIBLE_WITH_EXPLICIT_FULL",
        valuesPrinted: false,
      },
      valuesPrinted: false,
    },
    execution: {
      providerSecretClassification: "exact-target-ref",
      providerSecretWriteBlockedCount: 2,
      secretSyncStarted: false,
      valuesPrinted: false,
    },
    next: {
      observeRunners: "bun scripts/cli.ts agentrun control-plane cleanup-runners --node NC01 --lane nc01-v02 --dry-run",
      retryAfterManagerFence: "bun scripts/cli.ts agentrun control-plane secret-sync --node NC01 --lane nc01-v02 --confirm",
    },
    valuesPrinted: false,
  };
}

describe("AgentRun secret-sync bounded output", () => {
  test("confirmed sync reports all written secrets without stale provider classification", () => {
    const items = [{ id: "provider-gpt-pika-auth-json" }, { id: "provider-gpt-pika-config" }];
    expect(confirmedSecretSyncPlan(items, 2)).toEqual({
      secretCount: 2,
      items,
      writableSecretCount: 2,
      valuesPrinted: false,
    });
  });

  test("parses compact machine output and explicit disclosure options", () => {
    expect(parseSecretSyncOptions(["--node", "NC01", "--lane", "nc01-v02", "--dry-run", "-o", "yaml"])).toEqual({
      confirm: false,
      dryRun: true,
      node: "NC01",
      lane: "nc01-v02",
      secretIds: [],
      full: false,
      raw: false,
      output: "yaml",
    });
    expect(parseSecretSyncOptions(["--node", "NC01", "--lane", "nc01-v02", "--secret-id", "tool-github-ssh-private-key", "--full", "--output=json"])).toMatchObject({
      secretIds: ["tool-github-ssh-private-key"],
      full: true,
      raw: false,
      output: "json",
    });
    expect(parseSecretSyncOptions(["--node", "NC01", "--lane", "nc01-v02", "--raw"])).toMatchObject({ full: true, raw: true });
    expect(() => parseSecretSyncOptions(["--node", "NC01", "--lane", "nc01-v02", "-o", "wide"])).toThrow("must be one of human,json,yaml");
  });

  test("uses one bounded Secret-safe projection for human, JSON, and YAML", () => {
    const fixture = blockedFixture();
    const compact = compactAgentRunSecretSyncPayload(fixture);
    const human = renderAgentRunSecretSyncResult(fixture, options("human")).renderedText;
    const jsonText = renderAgentRunSecretSyncResult(fixture, options("json")).renderedText;
    const yamlText = renderAgentRunSecretSyncResult(fixture, options("yaml")).renderedText;
    const json = JSON.parse(jsonText) as Record<string, any>;
    const yaml = Bun.YAML.parse(yamlText) as Record<string, any>;

    expect(json).toEqual(compact);
    expect(yaml).toEqual(json);
    expect(json.kind).toBe("AgentRunSecretSyncPlan");
    expect(json.status).toMatchObject({ status: "blocked", reason: "provider-secret-activation-fence-unavailable", mutation: false, valuesPrinted: false });
    expect(json.plan.secretCount).toBe(13);
    expect(json.plan.omittedCount).toBe(0);
    expect(json.plan.items.some((item: Record<string, unknown>) => item.id === "tool-github-ssh-private-key")).toBe(true);
    expect(json.plan.items.some((item: Record<string, unknown>) => item.id === "tool-github-ssh-known-hosts")).toBe(true);
    expect(json.activationFence).toMatchObject({
      kind: "ProviderSecretActivationFence",
      identity: "providersecretactivationfence/NC01/nc01-v02",
      status: "blocked",
      reason: "provider-secret-activation-fence-unavailable",
      resourceCount: 1,
      valuesPrinted: false,
    });
    expect(json.activationFence.resources[0]).toMatchObject({
      identity: "runnerjob/rjob_fixture",
      status: "Pending",
      runId: "run_fixture",
      commandId: "cmd_fixture",
      runnerJobId: "rjob_fixture",
    });
    expect(json.activationFence.resources[0].drillDown.map((item: Record<string, unknown>) => item.identity)).toEqual([
      "runnerjob/rjob_fixture",
      "command/cmd_fixture",
      "run/run_fixture",
    ]);
    expect(json.next.secretById).toContain("--secret-id <secret-id> --dry-run -o json");
    expect(human).toContain("ACTIVATION FENCE");
    expect(human).toContain("runnerjob/rjob_fixture");
    expect(human).toContain("tool-github-ssh-private-key");
    expect(human).toContain("VALUES PRINTED false");
    for (const output of [human, jsonText, yamlText]) {
      expect(Buffer.byteLength(output, "utf8")).toBeLessThan(10_240);
      expect(output).not.toContain("FULL_PROBE_BODY_ONLY_VISIBLE_WITH_EXPLICIT_FULL");
      expect(output).not.toContain("/root/.unidesk/.state/secrets/<redacted>");
    }
  });

  test("keeps exhaustive probe details behind explicit full or raw", () => {
    const fixture = blockedFixture();
    const full = renderAgentRunSecretSyncResult(fixture, options("json", { full: true })).renderedText;
    const raw = renderAgentRunSecretSyncResult(fixture, options("human", { full: true, raw: true })).renderedText;
    expect(JSON.parse(full)).toEqual(fixture);
    expect(JSON.parse(raw)).toEqual(fixture);
    expect(full).toContain("FULL_PROBE_BODY_ONLY_VISIBLE_WITH_EXPLICIT_FULL");
    expect(raw).toContain("FULL_PROBE_BODY_ONLY_VISIBLE_WITH_EXPLICIT_FULL");
  });
});
