import test from "node:test";
import assert from "node:assert/strict";
import { readFileSync, existsSync } from "node:fs";
import { fileURLToPath } from "node:url";

import {
  addEvidence,
  applyReconciliation,
  completeContinuation,
  createSeedWorkspace,
  fieldRows,
  getObject,
  normalizeValue,
  resolveException,
  resumeContinuation,
  validateWorkspace,
  verifyReceiptChain
} from "../coherence/core.js";

test("normalization compares formatting-only numeric variants", () => {
  assert.equal(normalizeValue(" $12,480.00 ", "usd"), "#12480");
  assert.equal(normalizeValue("12480", "decimal"), "#12480");
  assert.equal(normalizeValue("  Northstar   Components ", "text"), "northstar components");
});

test("seed workspace distinguishes agreement, conflict, intentional difference, and unknown", async () => {
  const workspace = await createSeedWorkspace();
  const rows = new Map(fieldRows(getObject(workspace)).map((row) => [row.field, row]));

  assert.equal(rows.get("shipment_id").status, "SAME");
  assert.equal(rows.get("customer").status, "SAME");
  assert.equal(rows.get("delivery_address").status, "INTENTIONAL");
  assert.equal(rows.get("pickup_window").status, "DIFFERENT");
  assert.equal(rows.get("quoted_total").status, "DIFFERENT");
});

test("reconciliation updates writable surfaces, preserves evidence, and opens an exception", async () => {
  const workspace = await createSeedWorkspace();
  const result = await applyReconciliation(workspace, {
    objectId: workspace.activeObjectId,
    field: "pickup_window",
    targetValue: "2026-08-14 08:00–10:00",
    reason: "Dispatch email is the latest accepted window.",
    actor: "test operator",
    at: "2026-08-13T16:00:00.000Z"
  });

  const object = getObject(result.workspace);
  assert.equal(object.representations.find((item) => item.id === "representation-carrier").fields.pickup_window, "2026-08-14 08:00–10:00");
  assert.equal(object.representations.find((item) => item.id === "representation-invoice").fields.pickup_window, "");
  assert.equal(result.plan.find((item) => item.representationId === "representation-carrier").action, "UPDATE");
  assert.equal(result.plan.find((item) => item.representationId === "representation-invoice").action, "ATTEST");
  assert.equal(result.workspace.exceptions.length, workspace.exceptions.length + 1);
  assert.equal(workspace.objects[0].representations[1].fields.pickup_window, "2026-08-14 09:00–11:00", "input remains immutable");

  const chain = await verifyReceiptChain(result.workspace.receipts);
  assert.deepEqual(chain, { valid: true, checked: 2, brokenAt: null, reason: null });
});

test("intentional divergence is never overwritten by a correction", async () => {
  const workspace = await createSeedWorkspace();
  const result = await applyReconciliation(workspace, {
    objectId: workspace.activeObjectId,
    field: "delivery_address",
    targetValue: "840 W Cedar Ave, Dock 4",
    reason: "Signed quote is authoritative for physical delivery.",
    actor: "test operator",
    at: "2026-08-13T16:05:00.000Z"
  });

  const carrier = getObject(result.workspace).representations.find((item) => item.id === "representation-carrier");
  assert.equal(carrier.fields.delivery_address, "840 West Cedar Avenue, Dock Four");
  assert.equal(result.plan.find((item) => item.representationId === carrier.id).action, "PRESERVE");
});

test("receipt verification detects post-hoc mutation", async () => {
  const workspace = await createSeedWorkspace();
  workspace.receipts[0].reason = "tampered after capture";
  const chain = await verifyReceiptChain(workspace.receipts);

  assert.equal(chain.valid, false);
  assert.equal(chain.reason, "receipt digest mismatch");
  assert.equal(chain.brokenAt, workspace.receipts[0].id);
});

test("evidence is hashed from exact captured contents", async () => {
  const workspace = await createSeedWorkspace();
  const result = await addEvidence(workspace, {
    objectId: workspace.activeObjectId,
    label: "Amendment email",
    source: "thread-1182 / message 9",
    excerpt: "Fuel surcharge rejected; original quote remains in force.",
    kind: "email",
    at: "2026-08-13T16:10:00.000Z"
  });

  assert.match(result.evidence.digest, /^sha256:[0-9a-f]{64}$/);
  assert.equal(result.workspace.evidence.length, workspace.evidence.length + 1);
});

test("exception judgment is durable and can retain a reusable resolution pattern", async () => {
  const workspace = await createSeedWorkspace();
  const result = resolveException(workspace, "exception-invoice-total", {
    decision: "Reject invoice and request a corrected total.",
    actor: "test operator",
    pattern: "When invoice total exceeds accepted quote without an attached amendment, hold payment and request correction.",
    at: "2026-08-13T16:15:00.000Z"
  });

  assert.equal(result.exception.status, "RESOLVED");
  assert.equal(result.workspace.patterns.length, 1);
  assert.equal(result.workspace.patterns[0].learnedFrom, "exception-invoice-total");
});

test("communication resumes at stored state and closes with an explicit event", async () => {
  const workspace = await createSeedWorkspace();
  const resumed = resumeContinuation(workspace, "continuation-carrier-window", {
    actor: "test operator",
    at: "2026-08-13T16:20:00.000Z"
  });
  assert.equal(resumed.continuation.status, "ACTIVE");
  assert.equal(resumed.continuation.resumeCount, 1);
  assert.match(resumed.continuation.events.at(-1).note, /Read back shipment CE-2048/);

  const completed = completeContinuation(resumed.workspace, "continuation-carrier-window", {
    actor: "test operator",
    note: "Carrier confirmed the original 08:00–10:00 window.",
    at: "2026-08-13T16:30:00.000Z"
  });
  assert.equal(completed.continuation.status, "DONE");
  assert.equal(completed.continuation.nextAction, "Closed");
  assert.equal(completed.continuation.events.at(-1).kind, "COMPLETED");
});

test("workspace import validation rejects unsupported schemas", async () => {
  const workspace = await createSeedWorkspace();
  assert.equal(validateWorkspace(workspace).schemaVersion, 1);
  assert.throws(() => validateWorkspace({ ...workspace, schemaVersion: 99 }), /Unsupported workspace schema/);
});

test("launch surface remains browser-local and is included in homepage, sitemap, and atomic release", () => {
  const root = fileURLToPath(new URL("..", import.meta.url));
  const app = readFileSync(new URL("../coherence/app.js", import.meta.url), "utf8");
  const page = readFileSync(new URL("../coherence/index.html", import.meta.url), "utf8");
  const home = readFileSync(new URL("../index.html", import.meta.url), "utf8");
  const sitemap = readFileSync(new URL("../sitemap.xml", import.meta.url), "utf8");
  const deploy = readFileSync(new URL("../scripts/deploy_vps2.sh", import.meta.url), "utf8");

  assert.ok(root.endsWith("cyberelf\\") || root.endsWith("cyberelf/"));
  assert.doesNotMatch(app, /\bfetch\s*\(/, "alpha must not send workspace payloads over the network");
  assert.match(page, /connect-src 'none'/);
  assert.match(page, /data-view="reconcile"[\s\S]*data-view="attest"[\s\S]*data-view="exceptions"[\s\S]*data-view="continue"/);
  assert.match(home, /href="\/coherence\/"/);
  assert.match(sitemap, /https:\/\/cyberelf\.link\/coherence\//);
  assert.match(deploy, /publish\.ps1/, "legacy entrypoint delegates to the complete suite publisher");
  assert.ok(existsSync(new URL("../../deploy/publish.ps1", import.meta.url)));
  assert.ok(existsSync(new URL("../../deploy/package_release.py", import.meta.url)));
});
