// Use .js because the production static server advertises it as JavaScript.
export const SCHEMA_VERSION = 1;

const RECEIPT_PREFIX = "sha256:";

const clone = (value) => structuredClone(value);

const nowIso = (value) => value || new Date().toISOString();

const makeId = (prefix) => {
  const suffix = globalThis.crypto?.randomUUID?.() || `${Date.now()}-${Math.random().toString(16).slice(2)}`;
  return `${prefix}-${suffix}`;
};

export function canonicalStringify(value) {
  if (value === null || typeof value !== "object") return JSON.stringify(value);
  if (Array.isArray(value)) return `[${value.map(canonicalStringify).join(",")}]`;

  return `{${Object.keys(value)
    .sort()
    .map((key) => `${JSON.stringify(key)}:${canonicalStringify(value[key])}`)
    .join(",")}}`;
}

export async function sha256(value) {
  const bytes = new TextEncoder().encode(String(value));
  const digest = await globalThis.crypto.subtle.digest("SHA-256", bytes);
  return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("");
}

export const COMPARISON_POLICIES = Object.freeze({ exact: "Exact text / identifiers", text: "Text, ignoring case and repeated spaces", decimal: "Decimal number", usd: "USD amount" });

export function comparisonPolicy(object, field) { return object.comparisonPolicies && Object.hasOwn(object.comparisonPolicies, field) ? object.comparisonPolicies[field] : "exact"; }

function safeField(field) {
  if (typeof field !== "string" || !field || field !== field.trim() || ["__proto__", "constructor", "prototype"].includes(field)) throw new Error("Field name is empty, reserved, or contains outer whitespace.");
}

export function normalizeValue(value, policy = "exact") {
  if (value === null || value === undefined) return "";
  if (!Object.hasOwn(COMPARISON_POLICIES, policy)) throw new Error("Unknown comparison policy.");
  const text = String(value).trim();
  if (!text) return "";
  if (policy === "exact") return text;
  if (policy === "text") return text.replace(/\s+/g, " ").toLocaleLowerCase("en-US");
  const amount = policy === "usd" ? text.replace(/^(?:USD\s+|\$)/i, "").replace(/^-(?:USD\s+|\$)/i, "-") : text;
  const pattern = policy === "usd" ? /^-?(?:\d+|\d{1,3}(?:,\d{3})+)(\.\d+)?$/ : /^-?\d+(\.\d+)?$/;
  if (pattern.test(amount)) {
    const numeric = amount.replaceAll(",", "");
    const negative = numeric.startsWith("-");
    const [integer, fraction = ""] = numeric.replace(/^-/, "").split(".");
    const whole = integer.replace(/^0+(?=\d)/, "");
    const decimal = fraction.replace(/0+$/, "");
    const sign = negative && (whole !== "0" || decimal) ? "-" : "";
    return `#${sign}${whole}${decimal ? `.${decimal}` : ""}`;
  }

  throw new Error(policy === "usd" ? "Value is not a valid USD amount." : "Value is not a valid decimal number.");
}

function observedValue(value, policy) {
  try { return { normalized: normalizeValue(value, policy), comparisonError: null }; }
  catch (error) { return { normalized: "", comparisonError: error.message }; }
}

export function getObject(workspace, objectId = workspace.activeObjectId) {
  const object = workspace.objects.find((candidate) => candidate.id === objectId);
  if (!object) throw new Error(`Unknown object: ${objectId}`);
  return object;
}

export function fieldRows(object) {
  const fields = new Set(Object.keys(object.canonical || {}));
  for (const representation of object.representations) {
    Object.keys(representation.fields || {}).forEach((field) => fields.add(field));
  }

  return Array.from(fields)
    .sort((left, right) => left.localeCompare(right))
    .map((field) => {
      const policy = comparisonPolicy(object, field);
      const observations = object.representations.map((representation) => {
        const value = representation.fields?.[field] ?? "";
        return {
          representationId: representation.id,
          system: representation.system,
          role: representation.role,
          writable: representation.writable,
          value,
          ...observedValue(value, policy),
          intentionalReason: representation.intentionalFields?.[field] || ""
        };
      });
      const populated = observations.filter((observation) => observation.normalized);
      const groups = new Map();
      for (const observation of populated) {
        const group = groups.get(observation.normalized) || [];
        group.push(observation);
        groups.set(observation.normalized, group);
      }

      let status = "UNKNOWN";
      const asserted = observedValue(object.canonical?.[field]?.value, policy);
      const canonicalValue = asserted.normalized;
      const divergent = populated.filter((observation) => canonicalValue && observation.normalized !== canonicalValue);
      if (asserted.comparisonError || observations.some(item => item.comparisonError)) {
        status = "UNKNOWN";
      } else if (groups.size > 1 || divergent.length) {
        const intentional = canonicalValue && divergent.length > 0 && divergent.every((observation) => observation.intentionalReason);
        status = intentional ? "INTENTIONAL" : "DIFFERENT";
      } else if (populated.length >= 2 && groups.size === 1) {
        status = observations.some((observation) => !observation.normalized) ? "UNKNOWN" : "SAME";
      }

      return {
        field,
        comparisonPolicy: policy,
        comparisonError: asserted.comparisonError || observations.find(item => item.comparisonError)?.comparisonError || null,
        status,
        canonical: object.canonical?.[field] || null,
        observations,
        distinctValues: groups.size
      };
    });
}

export function summarizeObject(object) {
  const rows = fieldRows(object);
  const statuses = { SAME: 0, DIFFERENT: 0, INTENTIONAL: 0, UNKNOWN: 0 };
  rows.forEach((row) => {
    statuses[row.status] += 1;
  });
  return {
    representations: object.representations.length,
    fields: rows.length,
    ...statuses
  };
}

export function correctionPlan(object, field, targetValue, policy = comparisonPolicy(object, field)) {
  safeField(field);
  const normalizedTarget = normalizeValue(targetValue, policy);
  if (!normalizedTarget) throw new Error("A correction target is required.");

  return object.representations.map((representation) => {
    const before = representation.fields?.[field] ?? "";
    const intentionalReason = representation.intentionalFields?.[field] || "";
    let action = "NOOP";
    let note = "Already matches the asserted value.";

    if (intentionalReason) {
      action = "PRESERVE";
      note = intentionalReason;
    } else if (!representation.writable && observedValue(before, policy).normalized !== normalizedTarget) {
      action = "ATTEST";
      note = "Read-only surface; preserve the observation and open an exception.";
    } else if (representation.writable && observedValue(before, policy).normalized !== normalizedTarget) {
      action = "UPDATE";
      note = "Writable surface will be updated in this workspace.";
    }

    return {
      representationId: representation.id,
      system: representation.system,
      recordKey: representation.recordKey,
      action,
      before,
      after: action === "UPDATE" ? String(targetValue).trim() : before,
      note
    };
  });
}

function receiptPayload(receipt) {
  const { id: _id, hash: _hash, ...payload } = receipt;
  return payload;
}

async function appendReceipt(workspace, payload) {
  const previousHash = workspace.receipts.at(-1)?.hash || null;
  const fullPayload = { version: 1, previousHash, ...payload };
  const hash = `${RECEIPT_PREFIX}${await sha256(canonicalStringify(fullPayload))}`;
  const receipt = { id: `receipt-${hash.slice(RECEIPT_PREFIX.length, RECEIPT_PREFIX.length + 16)}`, hash, ...fullPayload };
  workspace.receipts.push(receipt);
  return receipt;
}

export async function verifyReceiptChain(receipts) {
  let previousHash = null;
  for (let index = 0; index < receipts.length; index += 1) {
    const receipt = receipts[index];
    if (receipt.previousHash !== previousHash) {
      return { valid: false, checked: index, brokenAt: receipt.id, reason: "previous hash mismatch" };
    }
    const expected = `${RECEIPT_PREFIX}${await sha256(canonicalStringify(receiptPayload(receipt)))}`;
    if (receipt.hash !== expected) {
      return { valid: false, checked: index, brokenAt: receipt.id, reason: "receipt digest mismatch" };
    }
    previousHash = receipt.hash;
  }
  return { valid: true, checked: receipts.length, brokenAt: null, reason: null };
}

export async function applyReconciliation(workspace, input) {
  const at = nowIso(input.at);
  const next = clone(workspace);
  const object = getObject(next, input.objectId);
  const field = String(input.field || "").trim();
  const targetValue = String(input.targetValue ?? "").trim();
  const reason = String(input.reason || "").trim();
  const actor = String(input.actor || "").trim();
  if (!field || !targetValue || !reason || !actor) {
    throw new Error("Field, asserted value, reason, and actor are required.");
  }
  safeField(field);
  const previousComparisonPolicy = comparisonPolicy(object, field);
  const policy = input.comparisonPolicy || previousComparisonPolicy;
  const plan = correctionPlan(object, field, targetValue, policy);
  object.comparisonPolicies = { ...object.comparisonPolicies, [field]: policy };
  object.canonical[field] = { value: targetValue, assertedBy: actor, assertedAt: at, reason };

  for (const operation of plan) {
    if (operation.action === "UPDATE") {
      const representation = object.representations.find((candidate) => candidate.id === operation.representationId);
      representation.fields[field] = targetValue;
      representation.observedAt = at;
    }
    if (operation.action === "ATTEST") {
      const alreadyOpen = next.exceptions.some((exception) =>
        exception.status === "OPEN" &&
        exception.objectId === object.id &&
        exception.field === field &&
        exception.representationId === operation.representationId
      );
      if (!alreadyOpen) {
        next.exceptions.push({
          id: makeId("exception"),
          objectId: object.id,
          representationId: operation.representationId,
          field,
          status: "OPEN",
          expected: targetValue,
          observed: operation.before || "No value observed",
          systems: [operation.system],
          reason: operation.note,
          openedAt: at,
          resolution: null
        });
      }
    }
  }

  const receipt = await appendReceipt(next, {
    action: "RECONCILE_FIELD",
    objectId: object.id,
    objectLabel: object.label,
    field,
    targetValue,
    comparisonPolicy: policy,
    previousComparisonPolicy,
    reason,
    actor,
    at,
    evidenceDigests: next.evidence.filter((item) => item.objectId === object.id).map((item) => item.digest).sort(),
    operations: plan
  });
  next.updatedAt = at;
  return { workspace: next, receipt, plan };
}

export async function setComparisonPolicy(workspace, input) {
  const next = clone(workspace);
  const object = getObject(next, input.objectId);
  const field = String(input.field || ""); safeField(field);
  if (!fieldRows(object).some(row => row.field === field)) throw new Error("Select an observed field.");
  if (!Object.hasOwn(COMPARISON_POLICIES, input.policy)) throw new Error("Unknown comparison policy.");
  const actor = String(input.actor || "").trim(), reason = String(input.reason || "").trim();
  if (!actor || !reason) throw new Error("Actor and reason are required for a comparison policy change.");
  const at = nowIso(input.at), previous = comparisonPolicy(object, field);
  object.comparisonPolicies = { ...object.comparisonPolicies, [field]: input.policy };
  const receipt = await appendReceipt(next, { action: "SET_COMPARISON_POLICY", objectId: object.id, objectLabel: object.label, field, previousPolicy: previous, comparisonPolicy: input.policy, actor, reason, at, evidenceDigests: [], operations: [] });
  next.updatedAt = at;
  return { workspace: next, receipt };
}

export async function addEvidence(workspace, input) {
  const object = getObject(workspace, input.objectId);
  const label = String(input.label || "").trim();
  const source = String(input.source || "").trim();
  const excerpt = String(input.excerpt || "").trim();
  if (!label || !source || !excerpt) throw new Error("Evidence label, source, and contents are required.");

  const at = nowIso(input.at);
  const next = clone(workspace);
  const evidence = {
    id: makeId("evidence"),
    objectId: object.id,
    label,
    kind: String(input.kind || "document").trim(),
    source,
    excerpt,
    capturedAt: at,
    digest: `${RECEIPT_PREFIX}${await sha256(excerpt)}`
  };
  next.evidence.push(evidence);
  next.updatedAt = at;
  return { workspace: next, evidence };
}

export function addObject(workspace, input) {
  const label = String(input.label || "").trim();
  const kind = String(input.kind || "record").trim();
  const externalKey = String(input.externalKey || "").trim();
  if (!label || !externalKey) throw new Error("Object label and external key are required.");
  const at = nowIso(input.at);
  const next = clone(workspace);
  const object = {
    id: makeId("object"),
    label,
    kind,
    externalKey,
    canonical: {},
    representations: []
  };
  next.objects.push(object);
  next.activeObjectId = object.id;
  next.updatedAt = at;
  return { workspace: next, object };
}

export function addRepresentation(workspace, input) {
  const next = clone(workspace);
  const object = getObject(next, input.objectId);
  const system = String(input.system || "").trim();
  const recordKey = String(input.recordKey || "").trim();
  const fields = input.fields;
  if (!system || !recordKey || !fields || typeof fields !== "object" || Array.isArray(fields)) {
    throw new Error("System, record key, and a JSON field object are required.");
  }
  for (const field of Object.keys(fields)) safeField(field);
  if (Object.values(fields).some(value => value !== null && typeof value !== "string")) throw new Error("Field values must be strings or null. Quote numbers to preserve exact precision and identifiers.");
  const at = nowIso(input.at);
  const representation = {
    id: makeId("representation"),
    system,
    recordKey,
    role: String(input.role || "operational").trim(),
    writable: Boolean(input.writable),
    observedAt: at,
    intentionalFields: input.intentionalFields && typeof input.intentionalFields === "object" ? input.intentionalFields : {},
    fields: Object.fromEntries(Object.entries(fields).map(([key, value]) => [String(key).trim(), String(value ?? "").trim()]))
  };
  object.representations.push(representation);
  next.updatedAt = at;
  return { workspace: next, representation };
}

export function queueException(workspace, input) {
  const next = clone(workspace);
  const object = getObject(next, input.objectId);
  const field = String(input.field || "").trim();
  const expected = String(input.expected || object.canonical?.[field]?.value || "").trim();
  const reason = String(input.reason || "").trim();
  if (!field || !expected || !reason) throw new Error("Exception field, expected state, and reason are required.");
  const observations = object.representations
    .filter((representation) => {
      const observed = observedValue(representation.fields?.[field], comparisonPolicy(object, field));
      const asserted = observedValue(expected, comparisonPolicy(object, field));
      return observed.comparisonError || asserted.comparisonError || observed.normalized !== asserted.normalized;
    })
    .map((representation) => `${representation.system}: ${representation.fields?.[field] || "missing"}`);
  const at = nowIso(input.at);
  const exception = {
    id: makeId("exception"),
    objectId: object.id,
    representationId: null,
    field,
    status: "OPEN",
    expected,
    observed: observations.join(" | ") || "No conflicting observation found",
    systems: object.representations.map((representation) => representation.system),
    reason,
    openedAt: at,
    resolution: null
  };
  next.exceptions.push(exception);
  next.updatedAt = at;
  return { workspace: next, exception };
}

export function resolveException(workspace, exceptionId, input) {
  const next = clone(workspace);
  const exception = next.exceptions.find((candidate) => candidate.id === exceptionId);
  if (!exception) throw new Error(`Unknown exception: ${exceptionId}`);
  if (exception.status === "RESOLVED") throw new Error("This exception is already resolved.");
  const decision = String(input.decision || "").trim();
  const actor = String(input.actor || "").trim();
  if (!decision || !actor) throw new Error("Resolution and actor are required.");
  const at = nowIso(input.at);
  exception.status = "RESOLVED";
  exception.resolution = { decision, actor, at };

  const pattern = String(input.pattern || "").trim();
  if (pattern) {
    next.patterns.push({
      id: makeId("pattern"),
      objectKind: getObject(next, exception.objectId).kind,
      field: exception.field,
      description: pattern,
      learnedFrom: exception.id,
      createdAt: at,
      createdBy: actor
    });
  }
  next.updatedAt = at;
  return { workspace: next, exception };
}

export function createContinuation(workspace, input) {
  const object = getObject(workspace, input.objectId);
  const required = ["title", "counterparty", "purpose", "waitingOn", "nextAction", "owner"];
  for (const key of required) {
    if (!String(input[key] || "").trim()) throw new Error(`${key} is required.`);
  }
  const at = nowIso(input.at);
  const next = clone(workspace);
  const continuation = {
    id: makeId("continuation"),
    objectId: object.id,
    title: String(input.title).trim(),
    counterparty: String(input.counterparty).trim(),
    channel: String(input.channel || "unspecified").trim(),
    owner: String(input.owner).trim(),
    status: "WAITING",
    purpose: String(input.purpose).trim(),
    waitingOn: String(input.waitingOn).trim(),
    nextAction: String(input.nextAction).trim(),
    dueAt: String(input.dueAt || "").trim() || null,
    createdAt: at,
    updatedAt: at,
    resumeCount: 0,
    events: [{ id: makeId("event"), at, kind: "OPENED", author: String(input.owner).trim(), note: "Continuation opened with explicit handoff state." }]
  };
  next.continuations.push(continuation);
  next.updatedAt = at;
  return { workspace: next, continuation };
}

export function appendContinuationEvent(workspace, continuationId, input) {
  const next = clone(workspace);
  const continuation = next.continuations.find((candidate) => candidate.id === continuationId);
  if (!continuation) throw new Error(`Unknown continuation: ${continuationId}`);
  const note = String(input.note || "").trim();
  const author = String(input.author || "").trim();
  if (!note || !author) throw new Error("Event note and author are required.");
  const at = nowIso(input.at);
  continuation.events.push({ id: makeId("event"), at, kind: String(input.kind || "NOTE").trim(), author, note });
  continuation.updatedAt = at;
  next.updatedAt = at;
  return { workspace: next, continuation };
}

export function resumeContinuation(workspace, continuationId, input = {}) {
  const next = clone(workspace);
  const continuation = next.continuations.find((candidate) => candidate.id === continuationId);
  if (!continuation) throw new Error(`Unknown continuation: ${continuationId}`);
  if (continuation.status === "DONE") throw new Error("A completed continuation cannot be resumed.");
  const at = nowIso(input.at);
  const actor = String(input.actor || continuation.owner).trim();
  continuation.status = "ACTIVE";
  continuation.resumeCount += 1;
  continuation.updatedAt = at;
  continuation.events.push({ id: makeId("event"), at, kind: "RESUMED", author: actor, note: `Resumed at the stored next action: ${continuation.nextAction}` });
  next.updatedAt = at;
  return { workspace: next, continuation };
}

export function completeContinuation(workspace, continuationId, input) {
  const next = clone(workspace);
  const continuation = next.continuations.find((candidate) => candidate.id === continuationId);
  if (!continuation) throw new Error(`Unknown continuation: ${continuationId}`);
  if (continuation.status === "DONE") throw new Error("This continuation is already completed.");
  const note = String(input.note || "").trim();
  const actor = String(input.actor || continuation.owner).trim();
  if (!note) throw new Error("Completion note is required.");
  const at = nowIso(input.at);
  continuation.status = "DONE";
  continuation.waitingOn = "Nothing";
  continuation.nextAction = "Closed";
  continuation.updatedAt = at;
  continuation.events.push({ id: makeId("event"), at, kind: "COMPLETED", author: actor, note });
  next.updatedAt = at;
  return { workspace: next, continuation };
}

export function validateWorkspace(workspace) {
  const record = (value, label) => {
    if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error(`${label} must be an object.`);
  };
  const text = (value, label) => {
    if (typeof value !== "string" || !value.trim()) throw new Error(`${label} must be a nonempty string.`);
  };
  const walk = (value, depth = 0) => {
    if (depth > 24) throw new Error("Workspace nesting is too deep.");
    if (typeof value === "string" && value.length > 250000) throw new Error("Workspace text is too large.");
    if (!value || typeof value !== "object") return;
    if (Array.isArray(value) && value.length > 10000) throw new Error("Workspace collection is too large.");
    for (const [key, child] of Object.entries(value)) {
      if (["__proto__", "prototype", "constructor"].includes(key)) throw new Error(`Unsupported field name: ${key}`);
      walk(child, depth + 1);
    }
  };
  walk(workspace);
  if (!workspace || typeof workspace !== "object") throw new Error("Workspace must be a JSON object.");
  if (workspace.schemaVersion !== SCHEMA_VERSION) throw new Error(`Unsupported workspace schema: ${workspace.schemaVersion}`);
  if (!Array.isArray(workspace.objects) || workspace.objects.length === 0) throw new Error("Workspace must contain at least one object.");
  if (!workspace.objects.some((object) => object.id === workspace.activeObjectId)) throw new Error("Active object does not exist.");
  for (const key of ["evidence", "receipts", "exceptions", "patterns", "continuations"]) {
    if (!Array.isArray(workspace[key])) throw new Error(`Workspace ${key} must be an array.`);
  }
  const ids = new Set();
  const identify = (item, label) => {
    record(item, label); text(item.id, `${label} id`);
    if (ids.has(item.id)) throw new Error(`Duplicate workspace id: ${item.id}`);
    ids.add(item.id);
  };
  text(workspace.id, "Workspace id"); text(workspace.name, "Workspace name");
  const objectIds = new Set(workspace.objects.map(item => item?.id));
  const repObjects = new Map();
  for (const object of workspace.objects) {
    identify(object, "Object"); text(object.label, "Object label"); text(object.externalKey, "External key");
    record(object.canonical, "Canonical fields");
    if (object.comparisonPolicies !== undefined) {
      record(object.comparisonPolicies, "Comparison policies");
      for (const [field, policy] of Object.entries(object.comparisonPolicies)) { safeField(field); if (!Object.hasOwn(COMPARISON_POLICIES, policy)) throw new Error("Unsupported comparison policy."); }
    }
    for (const value of Object.values(object.canonical)) { record(value, "Canonical value"); text(value.value, "Canonical value"); }
    if (!Array.isArray(object.representations)) throw new Error("Representations must be an array.");
    for (const rep of object.representations) {
      identify(rep, "Representation"); text(rep.system, "System"); text(rep.recordKey, "Record key");
      if (typeof rep.writable !== "boolean") throw new Error("Writable must be a boolean.");
      record(rep.fields, "Representation fields"); record(rep.intentionalFields, "Intentional fields");
      if (Object.values(rep.fields).some(value => typeof value !== "string")) throw new Error("Field values must be strings.");
      if (Object.values(rep.intentionalFields).some(value => typeof value !== "string")) throw new Error("Intentional reasons must be strings.");
      repObjects.set(rep.id, object.id);
    }
  }
  for (const key of ["evidence", "receipts", "exceptions", "continuations"]) {
    for (const item of workspace[key]) {
      identify(item, key);
      if (!objectIds.has(item.objectId)) throw new Error(`${key} references an unknown object.`);
      if (key === "evidence") { text(item.excerpt, "Evidence contents"); text(item.digest, "Evidence digest"); }
      if (key === "receipts") { text(item.hash, "Receipt hash"); if (!Array.isArray(item.evidenceDigests)) throw new Error("Receipt evidence digests must be an array."); }
      if (key === "exceptions") {
        if (!["OPEN", "RESOLVED"].includes(item.status)) throw new Error("Invalid exception status.");
        if (item.representationId && repObjects.get(item.representationId) !== item.objectId) throw new Error("Exception references a representation belonging to another object.");
        if (!Array.isArray(item.systems)) throw new Error("Exception systems must be an array.");
        if (item.status === "RESOLVED") { record(item.resolution, "Resolution"); text(item.resolution.decision, "Resolution decision"); }
      }
      if (key === "continuations") {
        if (!["WAITING", "ACTIVE", "DONE"].includes(item.status)) throw new Error("Invalid continuation status.");
        if (!Array.isArray(item.events)) throw new Error("Continuation events must be an array.");
        for (const event of item.events) { identify(event, "Event"); text(event.note, "Event note"); }
      }
    }
  }
  for (const pattern of workspace.patterns) {
    identify(pattern, "Pattern");
    if (!workspace.exceptions.some(item => item.id === pattern.learnedFrom)) throw new Error("Pattern references an unknown exception.");
  }
  return clone(workspace);
}

export async function verifyWorkspace(workspace) {
  const checked = validateWorkspace(workspace);
  const chain = await verifyReceiptChain(checked.receipts);
  if (!chain.valid) throw new Error(`Receipt chain rejected: ${chain.reason}`);
  const digests = new Map();
  for (const evidence of checked.evidence) {
    if (`${RECEIPT_PREFIX}${await sha256(evidence.excerpt)}` !== evidence.digest) throw new Error(`Evidence digest mismatch: ${evidence.label || evidence.id}`);
    if (!digests.has(evidence.objectId)) digests.set(evidence.objectId, new Set());
    digests.get(evidence.objectId).add(evidence.digest);
  }
  for (const receipt of checked.receipts) {
    if (receipt.evidenceDigests.some(digest => !digests.get(receipt.objectId)?.has(digest))) throw new Error(`Receipt references missing evidence: ${receipt.id}`);
  }
  return checked;
}

export async function exportReceiptBundle(workspace) {
  const checked = await verifyWorkspace(workspace);
  return { schemaVersion: 1, activeObjectId: checked.activeObjectId, exportedAt: new Date().toISOString(), scope: "Complete workspace chain; filter by objectId after verification.", verification: await verifyReceiptChain(checked.receipts), receipts: checked.receipts };
}

export async function createSeedWorkspace() {
  const createdAt = "2026-08-13T15:00:00.000Z";
  const objectId = "object-shipment-ce-2048";
  const workspace = {
    schemaVersion: SCHEMA_VERSION,
    id: "workspace-coherence-public-alpha",
    name: "Cross-system coherence workspace",
    createdAt,
    updatedAt: createdAt,
    activeObjectId: objectId,
    objects: [{
      id: objectId,
      label: "Shipment CE-2048",
      kind: "shipment",
      externalKey: "CE-2048",
      comparisonPolicies: { quoted_total: "usd", customer: "text" },
      canonical: {
        shipment_id: { value: "CE-2048", assertedBy: "signed quote", assertedAt: createdAt, reason: "Shared identifier across all observed surfaces." },
        customer: { value: "Northstar Components", assertedBy: "signed quote", assertedAt: createdAt, reason: "Contracting party." },
        delivery_address: { value: "840 W Cedar Ave, Dock 4", assertedBy: "signed quote", assertedAt: createdAt, reason: "Contract delivery location." },
        pickup_window: { value: "2026-08-14 08:00–10:00", assertedBy: "dispatch email", assertedAt: createdAt, reason: "Latest accepted window in the dispatch thread." },
        quoted_total: { value: "$12,480.00", assertedBy: "signed quote", assertedAt: createdAt, reason: "Accepted quote total before invoicing." }
      },
      representations: [
        {
          id: "representation-tms",
          system: "Atlas TMS",
          recordKey: "LOAD-7741",
          role: "operational",
          writable: true,
          observedAt: createdAt,
          intentionalFields: {},
          fields: {
            shipment_id: "CE-2048",
            customer: "Northstar Components",
            delivery_address: "840 W Cedar Ave, Dock 4",
            pickup_window: "2026-08-14 08:00–10:00",
            quoted_total: "12480.00"
          }
        },
        {
          id: "representation-carrier",
          system: "Blue Mesa portal",
          recordKey: "BM-99104",
          role: "counterparty",
          writable: true,
          observedAt: createdAt,
          intentionalFields: {
            delivery_address: "Portal standardizes address tokens; preserve this display form as an intentional difference."
          },
          fields: {
            shipment_id: "CE-2048",
            customer: "Northstar Components",
            delivery_address: "840 West Cedar Avenue, Dock Four",
            pickup_window: "2026-08-14 09:00–11:00",
            quoted_total: "$12,480"
          }
        },
        {
          id: "representation-email",
          system: "Dispatch email",
          recordKey: "thread-1182",
          role: "evidence",
          writable: false,
          observedAt: createdAt,
          intentionalFields: {},
          fields: {
            shipment_id: "CE-2048",
            customer: "Northstar Components",
            delivery_address: "840 W Cedar Ave, Dock 4",
            pickup_window: "2026-08-14 08:00–10:00",
            quoted_total: "$12,480.00"
          }
        },
        {
          id: "representation-invoice",
          system: "Invoice PDF",
          recordKey: "INV-8820",
          role: "evidence",
          writable: false,
          observedAt: createdAt,
          intentionalFields: {},
          fields: {
            shipment_id: "CE-2048",
            customer: "Northstar Components",
            delivery_address: "840 W Cedar Ave, Dock 4",
            pickup_window: "",
            quoted_total: "$12,840.00"
          }
        }
      ]
    }],
    evidence: [],
    receipts: [],
    exceptions: [{
      id: "exception-invoice-total",
      objectId,
      representationId: "representation-invoice",
      field: "quoted_total",
      status: "OPEN",
      expected: "$12,480.00",
      observed: "$12,840.00",
      systems: ["Invoice PDF", "Atlas TMS", "signed quote"],
      reason: "Invoice total does not correspond to the accepted quote; no fuel-surcharge amendment is attached.",
      openedAt: "2026-08-13T15:12:00.000Z",
      resolution: null
    }],
    patterns: [],
    continuations: [{
      id: "continuation-carrier-window",
      objectId,
      title: "Carrier callback: confirm pickup window",
      counterparty: "Blue Mesa dispatch",
      channel: "phone + email",
      owner: "Mara / operations",
      status: "WAITING",
      purpose: "Determine whether the portal's 09:00–11:00 slot supersedes the accepted 08:00–10:00 pickup window.",
      waitingOn: "Blue Mesa dispatcher to confirm trailer availability",
      nextAction: "Read back shipment CE-2048, then ask whether 09:00–11:00 replaces—not supplements—the signed window.",
      dueAt: "2026-08-14T14:00:00.000Z",
      createdAt: "2026-08-13T15:20:00.000Z",
      updatedAt: "2026-08-13T15:31:00.000Z",
      resumeCount: 0,
      events: [
        { id: "event-call", at: "2026-08-13T15:20:00.000Z", kind: "OUTBOUND_CALL", author: "Mara / operations", note: "Called Blue Mesa dispatch; no answer." },
        { id: "event-voicemail", at: "2026-08-13T15:22:00.000Z", kind: "VOICEMAIL", author: "Mara / operations", note: "Left shipment ID, conflict, and direct callback number." },
        { id: "event-email", at: "2026-08-13T15:31:00.000Z", kind: "EMAIL", author: "Mara / operations", note: "Sent the exact same yes/no question with both windows quoted." }
      ]
    }]
  };

  for (const evidence of [
    {
      id: "evidence-signed-quote",
      label: "Signed quote Q-4417",
      kind: "contract",
      source: "quotes/Q-4417.pdf",
      excerpt: "CE-2048 | Northstar Components | pickup 2026-08-14 08:00–10:00 | total USD 12,480.00"
    },
    {
      id: "evidence-dispatch-email",
      label: "Accepted dispatch window",
      kind: "email",
      source: "thread-1182 / message 7",
      excerpt: "Confirmed: CE-2048 remains booked for 08:00–10:00 on August 14."
    },
    {
      id: "evidence-invoice",
      label: "Carrier invoice INV-8820",
      kind: "invoice",
      source: "billing/INV-8820.pdf",
      excerpt: "Invoice INV-8820 | CE-2048 | amount due USD 12,840.00"
    }
  ]) {
    workspace.evidence.push({
      ...evidence,
      objectId,
      capturedAt: createdAt,
      digest: `${RECEIPT_PREFIX}${await sha256(evidence.excerpt)}`
    });
  }

  await appendReceipt(workspace, {
    action: "OBSERVATION_SNAPSHOT",
    objectId,
    objectLabel: "Shipment CE-2048",
    actor: "public alpha fixture",
    at: createdAt,
    reason: "Bind the initial four-system observation to the three independent evidence items.",
    evidenceDigests: workspace.evidence.map((item) => item.digest).sort(),
    operations: workspace.objects[0].representations.map((representation) => ({
      representationId: representation.id,
      system: representation.system,
      recordKey: representation.recordKey,
      action: "OBSERVE",
      before: null,
      after: canonicalStringify(representation.fields),
      note: representation.writable ? "Writable surface observed." : "Independent read-only evidence observed."
    }))
  });

  return workspace;
}
