import assert from "node:assert/strict";
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { afterEach, describe, it } from "node:test";

import {
  evaluateBoundThirdPartyFacts,
  inspectFactSourceRegistry,
} from "../FactSourceRegistry.ts";

const roots: string[] = [];

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

function registryFixture(): string {
  const root = mkdtempSync(join(tmpdir(), "codeflowmu-fact-source-"));
  roots.push(root);
  const sources = join(root, "fcop", "shared", "fact-sources", "sources");
  const standards = join(root, "fcop", "shared", "fact-sources", "standards");
  mkdirSync(sources, { recursive: true });
  mkdirSync(standards, { recursive: true });
  mkdirSync(join(root, "facts"), { recursive: true });
  writeFileSync(
    join(root, "facts", "people.json"),
    JSON.stringify({ records: { zhangsan: { phone: "13800000000" } } }),
    "utf8",
  );
  writeFileSync(
    join(sources, "FACT-SOURCE-PEOPLE.md"),
    `---\nfact_source_id: FACT-SOURCE-PEOPLE\ntitle: 人员主数据\nstatus: active\nsource_type: data_source\nadapter: json_file\nlocation: facts/people.json\nmanaged_by: ADMIN\n---\n\n# 人员主数据\n`,
    "utf8",
  );
  writeFileSync(
    join(standards, "FACT-STANDARD-PHONE.md"),
    `---\nfact_standard_id: FACT-STANDARD-PHONE\ntitle: 手机号核查\nstatus: active\nsource_ids:\n  - FACT-SOURCE-PEOPLE\nvalidators:\n  - check_id: phone_matches\n    validator_id: value_equals\n    source_id: FACT-SOURCE-PEOPLE\n    actual_path: person.phone\n    expected_path: records.zhangsan.phone\n    sensitive: true\nmanaged_by: ADMIN\n---\n\n# 手机号核查\n`,
    "utf8",
  );
  return root;
}

describe("FactSourceRegistry", () => {
  it("loads Markdown registrations and verifies a report fact without exposing raw values", async () => {
    const root = registryFixture();
    const registry = await inspectFactSourceRegistry(root);
    assert.equal(registry.layout_ready, true);
    assert.equal(registry.issues.length, 0);

    const result = await evaluateBoundThirdPartyFacts({
      projectRoot: root,
      taskFrontmatter: { fact_standard_ids: ["FACT-STANDARD-PHONE"] },
      reportFrontmatter: { reported_facts: { person: { phone: "13800000000" } } },
    });
    assert.equal(result.state, "verified");
    assert.match(result.checks[0]?.actual_digest ?? "", /^sha256:/);
    assert.doesNotMatch(JSON.stringify(result), /13800000000/);
  });

  it("reports a conflict rather than inventing a business decision", async () => {
    const root = registryFixture();
    const result = await evaluateBoundThirdPartyFacts({
      projectRoot: root,
      taskFrontmatter: { fact_standard_ids: ["FACT-STANDARD-PHONE"] },
      reportFrontmatter: { reported_facts: { person: { phone: "13900000000" } } },
    });
    assert.equal(result.state, "conflict");
    assert.equal(result.checks[0]?.state, "conflict");
  });

  it("records a missing reported fact as a conflict without crashing the digest", async () => {
    const root = registryFixture();
    const result = await evaluateBoundThirdPartyFacts({
      projectRoot: root,
      taskFrontmatter: { fact_standard_ids: ["FACT-STANDARD-PHONE"] },
      reportFrontmatter: { reported_facts: {} },
    });
    assert.equal(result.state, "conflict");
    assert.match(result.checks[0]?.actual_digest ?? "", /^sha256:/);
  });

  it("treats an unavailable registered source as unavailable, not Agent failure", async () => {
    const root = registryFixture();
    rmSync(join(root, "facts", "people.json"));
    const result = await evaluateBoundThirdPartyFacts({
      projectRoot: root,
      taskFrontmatter: { fact_standard_ids: ["FACT-STANDARD-PHONE"] },
      reportFrontmatter: { reported_facts: { person: { phone: "13800000000" } } },
    });
    assert.equal(result.state, "unavailable");
    assert.equal(result.conflicts.length, 0);
  });
});
