/**
 * Contract tests: cross-surface consistency - versions, skill counts, scope coverage.
 */
import { describe, it } from "node:test";
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import { resolve } from "node:path";
import {
  getAgentProfiles,
  getKnownAgentIds,
} from "../../src/cli/agents/registry.js";
import { SKILL_NAMES } from "../../src/cli/constants.js";
import { AUDIT_VERSION } from "../../src/cli/constants.js";
import { loadManifest } from "../../src/cli/manifest/manifest.js";
import { SETUP_CHECKS } from "../../src/cli/audit/check-goat-flow.js";
import { AGENT_CHECKS } from "../../src/cli/audit/check-agent-setup.js";
import { HARNESS_CHECKS } from "../../src/cli/audit/harness/index.js";

const BUILD_CHECKS = [...SETUP_CHECKS, ...AGENT_CHECKS];

const PROJECT_ROOT = resolve(import.meta.dirname, "..", "..");

// ---------------------------------------------------------------------------
// Package version matches AUDIT_VERSION
// ---------------------------------------------------------------------------
describe("version alignment", () => {
  it("package.json version matches AUDIT_VERSION", () => {
    const pkg = JSON.parse(
      readFileSync(resolve(PROJECT_ROOT, "package.json"), "utf-8"),
    );
    assert.equal(
      pkg.version,
      AUDIT_VERSION,
      `package.json (${pkg.version}) must match AUDIT_VERSION (${AUDIT_VERSION})`,
    );
  });
});

// ---------------------------------------------------------------------------
// SKILL_NAMES count matches manifest.json canonical skills
// ---------------------------------------------------------------------------
describe("skill count alignment", () => {
  it("SKILL_NAMES matches manifest.json canonical skills", () => {
    const structure = loadManifest();
    const canonical = structure.skills.canonical;
    assert.deepStrictEqual(
      [...SKILL_NAMES].sort(),
      [...canonical].sort(),
      `SKILL_NAMES (${SKILL_NAMES.length}) must match manifest.json canonical (${canonical.length})`,
    );
  });
});

// ---------------------------------------------------------------------------
// Build checks have unique IDs
// ---------------------------------------------------------------------------
describe("build check IDs", () => {
  it("all build check IDs are unique", () => {
    const ids = BUILD_CHECKS.map((c) => c.id);
    const unique = new Set(ids);
    assert.equal(
      ids.length,
      unique.size,
      `Duplicate build check IDs: ${ids.filter((id, i) => ids.indexOf(id) !== i)}`,
    );
  });
});

// ---------------------------------------------------------------------------
// Quality checks cover all 5 concerns
// ---------------------------------------------------------------------------
describe("harness check concern coverage", () => {
  it("harness checks cover all 5 concerns", () => {
    const concerns = new Set(HARNESS_CHECKS.map((c) => c.concern));
    assert.ok(concerns.has("context"), "Should have context checks");
    assert.ok(concerns.has("constraints"), "Should have constraints checks");
    assert.ok(concerns.has("verification"), "Should have verification checks");
    assert.ok(concerns.has("recovery"), "Should have recovery checks");
    assert.ok(
      concerns.has("feedback_loop"),
      "Should have feedback_loop checks",
    );
  });
});

describe("dashboard runner type alignment", () => {
  it("keeps the dashboard RunnerId union local and aligned with CLI agent IDs", () => {
    const globalsPath = resolve(PROJECT_ROOT, "src/dashboard/globals.d.ts");
    const globals = readFileSync(globalsPath, "utf-8");

    assert.doesNotMatch(
      globals,
      /\.\.\/cli\/types\.js/,
      "dashboard globals must not import ../cli/types.js or the dashboard build emits source-shadow JS",
    );

    const runnerIdDeclaration = globals.match(/type RunnerId =([\s\S]*?);/);
    assert.ok(
      runnerIdDeclaration,
      "src/dashboard/globals.d.ts should declare a RunnerId union",
    );

    const runnerIds = [...runnerIdDeclaration[1].matchAll(/"([^"]+)"/g)].map(
      (match) => match[1],
    );

    assert.deepStrictEqual(
      runnerIds,
      getKnownAgentIds(),
      "dashboard RunnerId union must stay aligned with the CLI agent list",
    );
  });
});

describe("manifest-backed agent capability alignment", () => {
  it("exposes capability metadata through the runtime registry", () => {
    const manifest = loadManifest();

    for (const profile of getAgentProfiles()) {
      const manifestAgent = manifest.agents[profile.id];
      assert.ok(manifestAgent, `manifest should include ${profile.id}`);
      assert.equal(
        profile.terminalBinary,
        manifestAgent.capabilities.terminal_binary,
      );
      assert.deepEqual(
        profile.setupSurfaces,
        manifestAgent.capabilities.setup_surfaces,
      );
      assert.equal(
        profile.promptInvocationStyle,
        manifestAgent.capabilities.prompt_invocation_style,
      );
      assert.equal(
        profile.skillSource,
        manifestAgent.capabilities.skill_source,
      );
      assert.equal(
        profile.supportsPostTurnHook,
        manifestAgent.hook_events.post_turn !== null,
      );
      assert.ok(
        profile.terminalBinary.trim(),
        `${profile.id} needs a terminal binary`,
      );
      assert.ok(
        profile.setupSurfaces.length > 0,
        `${profile.id} needs at least one setup surface`,
      );
    }
  });

  it("keeps dashboard and terminal runner support manifest-backed", () => {
    const dashboardRoutes = readFileSync(
      resolve(PROJECT_ROOT, "src/cli/server/dashboard-routes.ts"),
      "utf-8",
    );
    const terminalSource = readFileSync(
      resolve(PROJECT_ROOT, "src/cli/server/terminal.ts"),
      "utf-8",
    );

    assert.match(
      dashboardRoutes,
      /const SUPPORTED_AGENTS = AGENT_PROFILES\.map/,
      "dashboard-supported agents should be projected from getAgentProfiles()",
    );
    assert.match(
      dashboardRoutes,
      /window\.__GOAT_FLOW_AGENTS__ = \$\{JSON\.stringify\(SUPPORTED_AGENTS\)\}/,
      "dashboard shell should inject the manifest-backed supported-agent list",
    );
    assert.match(
      terminalSource,
      /for \(const profile of getAgentProfiles\(\)\)/,
      "terminal runner support should iterate manifest-backed profiles",
    );
    assert.doesNotMatch(
      terminalSource,
      /RUNNER_BINARIES/,
      "terminal should not carry a duplicate runner-binary allowlist",
    );
  });
});
