import { validateSync } from "@codeflowmu/protocol";
import type {
  SchemaName,
  TmpaGovernanceObject,
  TmpaIssue,
  TmpaIssueCode,
  TmpaJudgment,
  TmpaReaderEdge,
  TmpaReaderNode,
  TmpaReaderResult,
  TmpaViewState,
} from "@codeflowmu/protocol";

import {
  canonicalBytes,
  compareUnicodeCodePoints,
  sha256Digest,
  stableValue,
  tmpaObjectDigest,
} from "./canonical.ts";
import { createTmpaProfileBundle } from "./profile.ts";
import type {
  TmpaGovernanceReader,
  TmpaReaderOptions,
  TmpaSourceCandidate,
} from "./types.ts";

const INVALID_CODES = new Set<TmpaIssueCode>([
  "SCHEMA_INVALID",
  "UNKNOWN_TYPE",
  "INTEGRITY_MISMATCH",
  "DUPLICATE_ID_CONFLICT",
  "STREAM_DUPLICATE_SEQUENCE",
  "AUTHORITY_DENIED",
  "SOD_VIOLATION",
  "ILLEGAL_TRANSITION",
  "PROHIBITED_CYCLE",
]);

const QUARANTINE_CODES = new Set<TmpaIssueCode>([
  "SCHEMA_INVALID",
  "UNKNOWN_TYPE",
  "INTEGRITY_MISMATCH",
  "DUPLICATE_ID_CONFLICT",
  "STREAM_DUPLICATE_SEQUENCE",
  "PROHIBITED_CYCLE",
]);

const DISPUTED_CODES = new Set<TmpaIssueCode>([
  "PRIMARY_CARRIER_CONFLICT",
  "UNRESOLVED_CONFLICT",
  "STATE_EVIDENCE_CONFLICT",
]);

const SEVERITY_ORDER = new Map([
  ["critical", 0],
  ["error", 1],
  ["warning", 2],
  ["info", 3],
]);

interface SourceRecord extends TmpaSourceCandidate {
  byte_digest: string;
}

interface ParsedCandidate {
  source: SourceRecord;
  object: TmpaGovernanceObject;
  canonical_digest: string;
}

function compareTuple(left: readonly unknown[], right: readonly unknown[]): number {
  for (let index = 0; index < Math.max(left.length, right.length); index += 1) {
    const a = String(left[index] ?? "");
    const b = String(right[index] ?? "");
    if (a !== b) return compareUnicodeCodePoints(a, b);
  }
  return 0;
}

function issueSeverity(code: TmpaIssueCode): TmpaIssue["severity"] {
  if (code === "DUPLICATE_ID_CONFLICT" || code === "PROHIBITED_CYCLE") {
    return "critical";
  }
  return INVALID_CODES.has(code) ? "error" : "warning";
}

function issueJudgment(code: TmpaIssueCode): TmpaJudgment {
  return INVALID_CODES.has(code) ? "invalid" : "undetermined";
}

function setNodeJudgment(
  node: TmpaReaderNode | undefined,
  judgment: TmpaJudgment,
  viewState: TmpaViewState,
): void {
  if (!node || node.judgment === "invalid") return;
  if (judgment === "invalid" || node.judgment === "valid") {
    node.judgment = judgment;
    node.view_state = viewState;
  }
}

function cycleNodes(edges: readonly TmpaReaderEdge[]): Set<string> {
  const adjacency = new Map<string, string[]>();
  for (const edge of edges) {
    const targets = adjacency.get(edge.source_id) ?? [];
    targets.push(edge.target_id);
    adjacency.set(edge.source_id, targets);
  }

  const visiting = new Set<string>();
  const visited = new Set<string>();
  const stack: string[] = [];
  const found = new Set<string>();

  function visit(node: string): void {
    if (visiting.has(node)) {
      const start = stack.indexOf(node);
      for (const member of stack.slice(Math.max(0, start))) found.add(member);
      return;
    }
    if (visited.has(node)) return;
    visiting.add(node);
    stack.push(node);
    for (const target of [...(adjacency.get(node) ?? [])].sort(compareUnicodeCodePoints)) {
      visit(target);
    }
    stack.pop();
    visiting.delete(node);
    visited.add(node);
  }

  for (const node of [...adjacency.keys()].sort(compareUnicodeCodePoints)) visit(node);
  return found;
}

function orderTransitions(candidates: readonly ParsedCandidate[]): ParsedCandidate[] {
  const pending = new Map(candidates.map((candidate) => [candidate.object.id, candidate]));
  const ordered: ParsedCandidate[] = [];

  while (pending.size) {
    const ready = [...pending.values()]
      .filter((candidate) =>
        candidate.object.references
          .filter((reference) => reference.relation === "depends_on")
          .map((reference) => reference.target)
          .every((dependency) => !pending.has(dependency)),
      )
      .sort((left, right) => compareUnicodeCodePoints(left.object.id, right.object.id));
    const selected =
      ready[0] ??
      [...pending.values()].sort((left, right) =>
        compareUnicodeCodePoints(left.object.id, right.object.id),
      )[0];
    if (!selected) break;
    ordered.push(selected);
    pending.delete(selected.object.id);
  }
  return ordered;
}

function allowedRole(allowed: readonly string[], role: string): boolean {
  return allowed.includes("*") || allowed.includes(role);
}

export function createTmpaGovernanceReader(
  options: TmpaReaderOptions,
): TmpaGovernanceReader {
  const coreVersion = options.coreVersion ?? "S1.0";
  const schemaNames: Record<
    typeof coreVersion,
    { object: SchemaName; lifecycle: SchemaName; reader: SchemaName }
  > = {
    "S0.5": {
      object: "tmpa-s0.5-governance-object",
      lifecycle: "tmpa-s0.5-lifecycle-profile",
      reader: "tmpa-s0.5-reader-result",
    },
    "S0.6": {
      object: "tmpa-s0.6-governance-object",
      lifecycle: "tmpa-s0.6-lifecycle-profile",
      reader: "tmpa-s0.6-reader-result",
    },
    "S1.0": {
      object: "tmpa-s1.0-governance-object",
      lifecycle: "tmpa-s1.0-lifecycle-profile",
      reader: "tmpa-s1.0-reader-result",
    },
  };
  const objectSchemaName = schemaNames[coreVersion].object;
  const lifecycleSchemaName = schemaNames[coreVersion].lifecycle;
  const readerSchemaName = schemaNames[coreVersion].reader;
  const profile = options.lifecycleProfile;
  const profileBundle = createTmpaProfileBundle(profile, coreVersion);
  const profileDigest = profileBundle.conformance_profile.digest;
  const allowedTypes = new Set(profile.extensions?.document_types ?? []);
  const transitionMap = new Map(
    profile.transitions.map((item) => [
      `${item.from}\u0000${item.action}\u0000${item.to}`,
      item,
    ]),
  );
  const assignments = profile.extensions?.assignments ?? [];
  const orderingRelations = new Set(profile.extensions?.ordering_relations ?? []);
  const acyclicRelations = new Set(profile.extensions?.acyclic_relations ?? []);

  function readSync(
    sources: readonly TmpaSourceCandidate[],
  ): TmpaReaderResult {
      const profileValidation = validateSync(lifecycleSchemaName, profile);
      if (!profileValidation.valid) {
        const errors = (profileValidation.errors ?? [])
          .map((error) => `${error.instancePath}:${error.keyword}`)
          .sort(compareUnicodeCodePoints)
          .join(", ");
        throw new Error(`TMPA ${coreVersion} lifecycle profile is invalid: ${errors}`);
      }
      const sourceRecords: SourceRecord[] = sources
        .map((source) => ({
          ...source,
          byte_digest: sha256Digest(source.bytes),
        }))
        .sort((left, right) => compareUnicodeCodePoints(left.source_id, right.source_id));

      const sourceSetDigest = sha256Digest(
        canonicalBytes(
          sourceRecords.map(({ source_id, byte_digest }) => ({
            source_id,
            byte_digest,
          })),
        ),
      );
      const issues: TmpaIssue[] = [];
      const parsed: ParsedCandidate[] = [];

      function addIssue(
        code: TmpaIssueCode,
        sourceId: string,
        objectId = "",
        relation = "",
        targetId = "",
        parameters: Record<string, unknown> = {},
      ): void {
        const tuple = {
          code,
          object_id: objectId,
          relation,
          target_id: targetId,
          profile_digest: profileDigest,
        };
        const issueId = sha256Digest(canonicalBytes(tuple));
        if (issues.some((item) => item.issue_id === issueId)) return;
        issues.push({
          issue_id: issueId,
          code,
          severity: issueSeverity(code),
          source_id: sourceId,
          ...(objectId ? { source_object_id: objectId } : {}),
          object_id: objectId,
          ...(relation ? { relation } : {}),
          ...(targetId ? { target_id: targetId } : {}),
          message: JSON.stringify(stableValue(parameters)),
        });
      }

      for (const source of sourceRecords) {
        let object: unknown;
        try {
          object = JSON.parse(Buffer.from(source.bytes).toString("utf8"));
        } catch {
          addIssue("SCHEMA_INVALID", source.source_id, "", "", "", {
            reason: "json_parse",
          });
          continue;
        }

        const validation = validateSync(objectSchemaName, object);
        const objectId =
          object && typeof object === "object" && "id" in object
            ? String((object as { id?: unknown }).id ?? "")
            : "";
        if (!validation.valid) {
          addIssue("SCHEMA_INVALID", source.source_id, objectId, "", "", {
            errors: (validation.errors ?? [])
              .map((error) => `${error.instancePath}:${error.keyword}`)
              .sort(compareUnicodeCodePoints),
          });
          continue;
        }

        const governanceObject = object as TmpaGovernanceObject;
        if (!allowedTypes.has(governanceObject.type)) {
          addIssue("UNKNOWN_TYPE", source.source_id, governanceObject.id, "", "", {
            type: governanceObject.type,
          });
          continue;
        }
        if (governanceObject.integrity.digest !== tmpaObjectDigest(governanceObject)) {
          addIssue("INTEGRITY_MISMATCH", source.source_id, governanceObject.id);
          continue;
        }
        if (
          governanceObject.integrity.signature_algorithm &&
          governanceObject.integrity.signature
        ) {
          addIssue("SIGNATURE_UNVERIFIED", source.source_id, governanceObject.id);
        }
        parsed.push({
          source,
          object: governanceObject,
          canonical_digest: sha256Digest(canonicalBytes(governanceObject)),
        });
      }

      const groups = new Map<string, ParsedCandidate[]>();
      for (const candidate of parsed) {
        const members = groups.get(candidate.object.id) ?? [];
        members.push(candidate);
        groups.set(candidate.object.id, members);
      }

      const accepted: ParsedCandidate[] = [];
      const observationSources = new Map<string, string[]>();
      for (const [id, candidates] of [...groups.entries()].sort(([left], [right]) =>
        compareUnicodeCodePoints(left, right),
      )) {
        const distinct = new Set(candidates.map((candidate) => candidate.canonical_digest));
        if (distinct.size > 1) {
          const sortedSourceIds = candidates
            .map((candidate) => candidate.source.source_id)
            .sort(compareUnicodeCodePoints);
          addIssue(
            "DUPLICATE_ID_CONFLICT",
            sortedSourceIds[0] ?? "unknown",
            id,
            "",
            "",
            { source_ids: sortedSourceIds },
          );
          continue;
        }
        const sorted = [...candidates].sort((left, right) =>
          compareUnicodeCodePoints(left.source.source_id, right.source.source_id),
        );
        const selected = sorted[0];
        if (!selected) continue;
        accepted.push(selected);
        observationSources.set(
          id,
          sorted.map((candidate) => candidate.source.source_id),
        );
      }

      const byId = new Map(accepted.map((candidate) => [candidate.object.id, candidate]));
      const nodes: TmpaReaderNode[] = accepted.map(
        ({ object, canonical_digest }) => ({
          id: object.id,
          source_object_id: object.id,
          source_ids: observationSources.get(object.id) ?? [],
          canonical_digest,
          governed_work_id: object.governed_work.id,
          primary_carrier_id: object.governed_work.primary_carrier_id,
          ...(object.governed_work.parent_id
            ? { parent_work_id: object.governed_work.parent_id }
            : {}),
          ...(object.governed_work.thread_id
            ? { thread_id: object.governed_work.thread_id }
            : {}),
          type: object.type,
          stream_id: object.stream.id,
          stream_sequence: object.stream.sequence,
          role: object.role,
          creator: object.creator,
          judgment: "valid",
          view_state: "authoritative",
        }),
      );
      const nodeById = new Map(nodes.map((node) => [node.id, node]));

      const workGroups = new Map<string, ParsedCandidate[]>();
      for (const candidate of accepted) {
        const workId = candidate.object.governed_work.id;
        const members = workGroups.get(workId) ?? [];
        members.push(candidate);
        workGroups.set(workId, members);
      }

      for (const [workId, candidates] of [...workGroups.entries()].sort(
        ([left], [right]) => compareUnicodeCodePoints(left, right),
      )) {
        const carriers = [
          ...new Set(
            candidates.map(
              (candidate) => candidate.object.governed_work.primary_carrier_id,
            ),
          ),
        ].sort(compareUnicodeCodePoints);
        if (carriers.length > 1 || !carriers.every((carrier) => byId.has(carrier))) {
          const first = candidates[0];
          if (!first) continue;
          addIssue(
            "PRIMARY_CARRIER_CONFLICT",
            first.source.source_id,
            first.object.id,
            "",
            "",
            { work_id: workId, carriers },
          );
          for (const candidate of candidates) {
            setNodeJudgment(
              nodeById.get(candidate.object.id),
              "undetermined",
              "disputed",
            );
          }
        }
      }

      const streamGroups = new Map<string, ParsedCandidate[]>();
      for (const candidate of accepted) {
        const streamId = candidate.object.stream.id;
        const members = streamGroups.get(streamId) ?? [];
        members.push(candidate);
        streamGroups.set(streamId, members);
      }
      for (const [streamId, candidates] of [...streamGroups.entries()].sort(
        ([left], [right]) => compareUnicodeCodePoints(left, right),
      )) {
        const sequenceGroups = new Map<number, ParsedCandidate[]>();
        for (const candidate of candidates) {
          const sequence = candidate.object.stream.sequence;
          const members = sequenceGroups.get(sequence) ?? [];
          members.push(candidate);
          sequenceGroups.set(sequence, members);
        }
        for (const [sequence, members] of [...sequenceGroups.entries()].sort(
          ([left], [right]) => left - right,
        )) {
          if (members.length <= 1) continue;
          const first = members[0];
          if (!first) continue;
          addIssue(
            "STREAM_DUPLICATE_SEQUENCE",
            first.source.source_id,
            first.object.id,
            "",
            "",
            { stream_id: streamId, sequence },
          );
          for (const member of members) {
            setNodeJudgment(
              nodeById.get(member.object.id),
              "invalid",
              "quarantined",
            );
          }
        }
        const sequences = [...sequenceGroups.keys()].sort((left, right) => left - right);
        const firstSequence = sequences[0];
        const lastSequence = sequences.at(-1);
        if (firstSequence !== undefined && lastSequence !== undefined) {
          for (let expected = firstSequence; expected <= lastSequence; expected += 1) {
            if (sequenceGroups.has(expected)) continue;
            const first = candidates[0];
            if (!first) continue;
            addIssue("STREAM_GAP", first.source.source_id, first.object.id, "", "", {
              stream_id: streamId,
              sequence: expected,
            });
          }
        }
      }

      const edges: TmpaReaderEdge[] = [];
      for (const candidate of accepted) {
        for (const reference of candidate.object.references) {
          const edgeId = sha256Digest(
            canonicalBytes({
              source_id: candidate.object.id,
              relation: reference.relation,
              target_id: reference.target,
            }),
          );
          edges.push({
            id: edgeId,
            source_object_id: candidate.object.id,
            source_id: candidate.object.id,
            relation: reference.relation,
            target_id: reference.target,
            ordering: orderingRelations.has(reference.relation),
          });
          if (!byId.has(reference.target)) {
            addIssue(
              "MISSING_REFERENCE",
              candidate.source.source_id,
              candidate.object.id,
              reference.relation,
              reference.target,
            );
            setNodeJudgment(
              nodeById.get(candidate.object.id),
              "undetermined",
              "partial",
            );
          }
        }
      }

      for (const candidate of accepted) {
        const object = candidate.object;
        for (const claim of object.claims ?? []) {
          const missingEvidence = claim.evidence.filter((id) => !byId.has(id));
          const completionClaim = profile.work_graph.completion_claim_predicates.includes(
            claim.predicate,
          );
          if (missingEvidence.length || (completionClaim && claim.evidence.length === 0)) {
            addIssue(
              "CLAIM_EVIDENCE_MISSING",
              candidate.source.source_id,
              object.id,
              "evidences",
              missingEvidence[0] ?? "",
              {
                claim_id: claim.id,
                predicate: claim.predicate,
                missing_evidence: missingEvidence,
              },
            );
            setNodeJudgment(nodeById.get(object.id), "undetermined", "partial");
          }
        }

        const riskLevel = object.risk?.level;
        const requiresHuman =
          object.risk?.requires_human_approval === true ||
          (riskLevel !== undefined &&
            profile.risk_policy.human_approval_levels.includes(riskLevel));
        if (requiresHuman) {
          const approved = object.references.some((reference) => {
            if (!profile.risk_policy.approval_relations.includes(reference.relation)) {
              return false;
            }
            const approval = byId.get(reference.target)?.object;
            const body = approval?.content.body;
            const decision =
              body && typeof body === "object"
                ? String((body as Record<string, unknown>).decision ?? "")
                : "";
            const assigned = Boolean(
              approval &&
                assignments.some(
                  (assignment) =>
                    assignment.identity === approval.creator &&
                    assignment.role === approval.role,
                ),
            );
            const independent = Boolean(
              approval &&
                (!profile.risk_policy.requires_independent_actor ||
                  approval.creator !== object.creator),
            );
            return Boolean(
              approval &&
                (profile.risk_policy.approval_object_types ?? []).includes(
                  approval.type,
                ) &&
                profile.risk_policy.approval_roles.includes(approval.role) &&
                assigned &&
                independent &&
                ["approve", "approved"].includes(decision),
            );
          });
          if (!approved) {
            addIssue(
              "HUMAN_APPROVAL_REQUIRED",
              candidate.source.source_id,
              object.id,
              "",
              "",
              { risk_level: riskLevel ?? "unspecified" },
            );
            setNodeJudgment(nodeById.get(object.id), "undetermined", "pending_human");
          }
        }
      }

      const prohibitedEdges = edges.filter(
        (edge) => acyclicRelations.has(edge.relation) && byId.has(edge.target_id),
      );
      const membersInCycle = cycleNodes(prohibitedEdges);
      if (membersInCycle.size) {
        const members = [...membersInCycle].sort(compareUnicodeCodePoints);
        const firstId = members[0];
        const first = firstId ? byId.get(firstId) : undefined;
        if (first && firstId) {
          addIssue(
            "PROHIBITED_CYCLE",
            first.source.source_id,
            firstId,
            "",
            "",
            { members },
          );
          for (const member of members) {
            setNodeJudgment(nodeById.get(member), "invalid", "quarantined");
          }
        }
      }

      // A review by the same security identity as its subject is never independent.
      for (const review of accepted.filter((candidate) => candidate.object.type === "REVIEW")) {
        const targetId = review.object.references.find(
          (reference) => reference.relation === "reviews",
        )?.target;
        const target = targetId ? byId.get(targetId)?.object : undefined;
        if (target && target.creator === review.object.creator) {
          addIssue(
            "SOD_VIOLATION",
            review.source.source_id,
            review.object.id,
            "reviews",
            target.id,
            { creator: review.object.creator },
          );
          setNodeJudgment(nodeById.get(review.object.id), "invalid", "rejected");
        }
      }

      const workState: Record<string, string> = {};
      const responsibility: Record<string, string> = {};
      const acceptanceState: Record<string, string> = {};

      for (const [workId, candidates] of [...workGroups.entries()].sort(
        ([left], [right]) => compareUnicodeCodePoints(left, right),
      )) {
        let current = profile.initial_state;
        let currentRole =
          candidates.find(
            (candidate) =>
              candidate.object.id === candidate.object.governed_work.primary_carrier_id,
          )?.object.role ?? "";
        let acceptedForWork = false;
        let acceptanceUndeterminedForWork = false;
        const transitions = orderTransitions(
          candidates.filter((candidate) => candidate.object.lifecycle.transition),
        );

        for (const candidate of transitions) {
          const object = candidate.object;
          const node = nodeById.get(object.id);
          if (node?.judgment === "invalid") continue;
          const transition = object.lifecycle.transition;
          if (!transition) continue;
          const definition = transitionMap.get(
            `${transition.from}\u0000${transition.action}\u0000${transition.to}`,
          );
          if (!definition) {
            addIssue(
              "ILLEGAL_TRANSITION",
              candidate.source.source_id,
              object.id,
              "",
              "",
              { ...transition },
            );
            setNodeJudgment(node, "invalid", "rejected");
            continue;
          }
          if (transition.from !== current) {
            addIssue(
              "LIFECYCLE_UNDETERMINED",
              candidate.source.source_id,
              object.id,
              "",
              "",
              { expected_from: current, actual_from: transition.from },
            );
            setNodeJudgment(node, "undetermined", "partial");
            continue;
          }

          const identityAssignments = assignments.filter(
            (assignment) =>
              assignment.identity === object.creator && assignment.role === object.role,
          );
          if (!identityAssignments.length) {
            addIssue(
              "AUTHORITY_UNDETERMINED",
              candidate.source.source_id,
              object.id,
              "",
              "",
              { action: transition.action, identity: object.creator, role: object.role },
            );
            setNodeJudgment(node, "undetermined", "partial");
            continue;
          }
          if (
            !identityAssignments.some(
              (assignment) =>
                assignment.actions.includes("*") ||
                assignment.actions.includes(transition.action),
            ) ||
            !allowedRole(definition.allowed_roles, object.role)
          ) {
            addIssue(
              "AUTHORITY_DENIED",
              candidate.source.source_id,
              object.id,
              "",
              "",
              { action: transition.action, role: object.role },
            );
            setNodeJudgment(node, "invalid", "rejected");
            continue;
          }

          const missingRequired = definition.required_relations.filter(
            (relation) =>
              !object.references.some(
                (reference) =>
                  reference.relation === relation && byId.has(reference.target),
              ),
          );
          if (missingRequired.length) {
            addIssue(
              "LIFECYCLE_UNDETERMINED",
              candidate.source.source_id,
              object.id,
              missingRequired[0] ?? "",
              "",
              { missing_relations: missingRequired },
            );
            const missingAcceptanceRelations = missingRequired.filter((relation) =>
              profile.acceptance.acceptance_relations.includes(relation),
            );
            if (
              missingAcceptanceRelations.length &&
              profile.acceptance.states_requiring_acceptance.includes(transition.to)
            ) {
              acceptanceUndeterminedForWork = true;
              addIssue(
                "ACCEPTANCE_UNDETERMINED",
                candidate.source.source_id,
                object.id,
                missingAcceptanceRelations[0] ?? "",
                "",
                {
                  missing_relations: missingAcceptanceRelations,
                  target_state: transition.to,
                },
              );
            }
            setNodeJudgment(node, "undetermined", "partial");
            continue;
          }

          if (["approve", "approve_review"].includes(transition.action)) {
            const accepted = object.references.find((reference) =>
              profile.acceptance.acceptance_relations.includes(reference.relation),
            );
            const acceptedTarget = accepted ? byId.get(accepted.target)?.object : undefined;
            if (acceptedTarget && acceptedTarget.creator === object.creator) {
              addIssue(
                "SOD_VIOLATION",
                candidate.source.source_id,
                object.id,
                accepted?.relation ?? "accepts",
                acceptedTarget.id,
              );
              setNodeJudgment(node, "invalid", "rejected");
              continue;
            }
          }

          if (
            profile.acceptance.states_requiring_acceptance.includes(transition.to) &&
            !acceptedForWork
          ) {
            const acceptanceReference = object.references.find(
              (reference) =>
                profile.acceptance.acceptance_relations.includes(reference.relation) &&
                byId.has(reference.target),
            );
            const acceptedTarget = acceptanceReference
              ? byId.get(acceptanceReference.target)?.object
              : undefined;
            const roleAllowed = allowedRole(
              profile.acceptance.allowed_roles,
              object.role,
            );
            const independent =
              !profile.acceptance.requires_independent_actor ||
              Boolean(acceptedTarget && acceptedTarget.creator !== object.creator);
            if (!acceptanceReference || !roleAllowed || !independent) {
              acceptanceUndeterminedForWork = true;
              addIssue(
                "ACCEPTANCE_UNDETERMINED",
                candidate.source.source_id,
                object.id,
                acceptanceReference?.relation ?? "",
                acceptanceReference?.target ?? "",
                { role_allowed: roleAllowed, independent, target_state: transition.to },
              );
              setNodeJudgment(node, "undetermined", "partial");
              continue;
            }
            acceptedForWork = true;
          }

          current = transition.to;
          currentRole = object.role;
        }

        workState[workId] = current;
        responsibility[workId] = currentRole;
        acceptanceState[workId] = acceptedForWork
          ? "accepted"
          : acceptanceUndeterminedForWork ||
              profile.acceptance.states_requiring_acceptance.includes(current)
            ? "undetermined"
            : "not_required";
      }

      for (const candidate of accepted.filter(
        (item) => item.object.type === "STATE_OBSERVATION",
      )) {
        const body = candidate.object.content.body;
        const observedState =
          body && typeof body === "object"
            ? (body as Record<string, unknown>).state
            : undefined;
        const workId = candidate.object.governed_work.id;
        if (
          typeof observedState === "string" &&
          workState[workId] !== undefined &&
          observedState !== workState[workId]
        ) {
          addIssue(
            "STATE_EVIDENCE_CONFLICT",
            candidate.source.source_id,
            candidate.object.id,
            "",
            "",
            {
              reconstructed_state: workState[workId],
              observed_state: observedState,
              work_id: workId,
            },
          );
          setNodeJudgment(
            nodeById.get(candidate.object.id),
            "undetermined",
            "disputed",
          );
        }
      }

      const childrenByParent = new Map<string, string[]>();
      for (const [workId, candidates] of workGroups) {
        const parentId = candidates
          .map((candidate) => candidate.object.governed_work.parent_id)
          .find((value): value is string => Boolean(value));
        if (!parentId) continue;
        const children = childrenByParent.get(parentId) ?? [];
        children.push(workId);
        childrenByParent.set(parentId, children);
      }

      for (const [workId, state] of Object.entries(workState)) {
        if (!profile.acceptance.states_requiring_acceptance.includes(state)) continue;
        const candidates = workGroups.get(workId) ?? [];
        const carrier =
          candidates.find(
            (candidate) =>
              candidate.object.id === candidate.object.governed_work.primary_carrier_id,
          ) ?? candidates[0];
        if (!carrier) continue;
        const openChildren = (childrenByParent.get(workId) ?? []).filter(
          (childId) =>
            !profile.work_graph.child_terminal_states.includes(workState[childId] ?? ""),
        );
        if (openChildren.length) {
          addIssue(
            "CHILD_WORK_OPEN",
            carrier.source.source_id,
            carrier.object.id,
            "parent",
            "",
            {
              work_id: workId,
              open_children: openChildren.sort(compareUnicodeCodePoints),
            },
          );
          setNodeJudgment(
            nodeById.get(carrier.object.id),
            "undetermined",
            "partial",
          );
          acceptanceState[workId] = "undetermined";
        }
        const hasResponse = candidates.some(
          (candidate) => candidate.object.type === "REPORT",
        );
        if (!hasResponse) {
          addIssue(
            "RECIPROCITY_MISSING",
            carrier.source.source_id,
            carrier.object.id,
            "",
            "",
            { work_id: workId },
          );
          setNodeJudgment(
            nodeById.get(carrier.object.id),
            "undetermined",
            "partial",
          );
          acceptanceState[workId] = "undetermined";
        }
      }

      const reviews = accepted.filter((candidate) => {
        if (candidate.object.type !== "REVIEW") return false;
        const body = candidate.object.content.body;
        return Boolean(
          body &&
            typeof body === "object" &&
            (body as Record<string, unknown>).decision,
        );
      });
      const reviewGroups = new Map<string, ParsedCandidate[]>();
      for (const review of reviews) {
        const target = review.object.references.find(
          (reference) => reference.relation === "reviews",
        )?.target;
        if (!target) continue;
        const members = reviewGroups.get(target) ?? [];
        members.push(review);
        reviewGroups.set(target, members);
      }
      const decisions = accepted.filter(
        (candidate) => candidate.object.type === "DECISION",
      );
      const configuredConflictResolutionPolicy =
        profile.extensions?.review_conflict_resolution;
      const conflictResolutionPolicy = configuredConflictResolutionPolicy ?? {
        action: "resolve_review_conflict",
        allowed_roles: ["ADMIN", "PM", "QA"],
        resolution_relation: "resolves",
        selection_field: "selects",
      };
      for (const [target, members] of [...reviewGroups.entries()].sort(
        ([left], [right]) => compareUnicodeCodePoints(left, right),
      )) {
        const values = new Set(
          members.map((member) => {
            const body = member.object.content.body as Record<string, unknown>;
            return String(body.decision);
          }),
        );
        if (values.size < 2) continue;
        const memberIds = new Set(members.map((member) => member.object.id));
        const resolved = decisions.some((decision) => {
          const resolutionRelation = conflictResolutionPolicy.resolution_relation;
          const selectionField = conflictResolutionPolicy.selection_field;
          const resolvedIds = decision.object.references
            .filter((reference) => reference.relation === resolutionRelation)
            .map((reference) => reference.target);
          const body = decision.object.content.body;
          const selected =
            body && typeof body === "object"
              ? String((body as Record<string, unknown>)[selectionField] ?? "")
              : "";
          const structurallyResolves =
            [...memberIds].every((id) => resolvedIds.includes(id)) &&
            memberIds.has(selected);
          if (!structurallyResolves) return false;

          const node = nodeById.get(decision.object.id);
          const identityAssignments = assignments.filter(
            (assignment) =>
              assignment.identity === decision.object.creator &&
              assignment.role === decision.object.role,
          );
          if (!identityAssignments.length) {
            addIssue(
              "AUTHORITY_UNDETERMINED",
              decision.source.source_id,
              decision.object.id,
              resolutionRelation,
              target,
              {
                action: conflictResolutionPolicy.action,
                identity: decision.object.creator,
                role: decision.object.role,
              },
            );
            setNodeJudgment(node, "undetermined", "partial");
            return false;
          }

          const roleAllowed = conflictResolutionPolicy.allowed_roles.includes(
            decision.object.role,
          );
          const actionAllowed = identityAssignments.some(
            (assignment) =>
              !configuredConflictResolutionPolicy ||
              assignment.actions.includes("*") ||
              assignment.actions.includes(conflictResolutionPolicy.action),
          );
          if (!roleAllowed || !actionAllowed) {
            addIssue(
              "AUTHORITY_DENIED",
              decision.source.source_id,
              decision.object.id,
              resolutionRelation,
              target,
              {
                action: conflictResolutionPolicy.action,
                role: decision.object.role,
                role_allowed: roleAllowed,
                action_allowed: actionAllowed,
              },
            );
            setNodeJudgment(node, "invalid", "rejected");
            return false;
          }

          return true;
        });
        if (!resolved) {
          const first = members[0];
          if (!first) continue;
          addIssue(
            "UNRESOLVED_CONFLICT",
            first.source.source_id,
            first.object.id,
            "reviews",
            target,
            { reviews: [...memberIds].sort(compareUnicodeCodePoints) },
          );
          for (const member of members) {
            setNodeJudgment(
              nodeById.get(member.object.id),
              "undetermined",
              "disputed",
            );
          }
        }
      }

      nodes.sort((left, right) =>
        compareTuple([left.id, left.source_object_id], [right.id, right.source_object_id]),
      );
      edges.sort((left, right) =>
        compareTuple(
          [left.source_id, left.relation, left.target_id, left.id],
          [right.source_id, right.relation, right.target_id, right.id],
        ),
      );
      issues.sort((left, right) => {
        const severity =
          (SEVERITY_ORDER.get(left.severity) ?? 9) -
          (SEVERITY_ORDER.get(right.severity) ?? 9);
        return (
          severity ||
          compareTuple(
            [
              left.code,
              left.object_id,
              left.relation,
              left.target_id,
              left.issue_id,
            ],
            [
              right.code,
              right.object_id,
              right.relation,
              right.target_id,
              right.issue_id,
            ],
          )
        );
      });

      const hasInvalid = issues.some(
        (issue) => issueJudgment(issue.code) === "invalid",
      );
      const hasUndetermined = issues.some(
        (issue) => issueJudgment(issue.code) === "undetermined",
      );
      const judgment: TmpaJudgment = hasInvalid
        ? "invalid"
        : hasUndetermined
          ? "undetermined"
          : "valid";
      let viewState: TmpaViewState = "authoritative";
      if (issues.some((issue) => QUARANTINE_CODES.has(issue.code))) {
        viewState = "quarantined";
      } else if (issues.some((issue) => DISPUTED_CODES.has(issue.code))) {
        viewState = "disputed";
      } else if (hasInvalid) {
        viewState = "rejected";
      } else if (issues.some((issue) => issue.code === "HUMAN_APPROVAL_REQUIRED")) {
        viewState = "pending_human";
      } else if (hasUndetermined) {
        viewState = "partial";
      }

      const result: TmpaReaderResult = {
        core_version: coreVersion,
        output_version: "1",
        profile: profileBundle,
        reader: {
          id: options.readerId ?? `codeflowmu-tmpa-${coreVersion.toLowerCase()}-reader`,
          version:
            options.readerVersion ??
            (coreVersion === "S1.0"
              ? "1.2.0"
              : coreVersion === "S0.6"
                ? "1.1.0"
                : "1.0.1"),
        },
        source_set_digest: sourceSetDigest,
        judgment,
        view_state: viewState,
        authentication_state: "not_applicable",
        nodes,
        edges,
        issues,
        extensions: {
          summary: {
            work_state: stableValue(workState),
            acceptance_state: stableValue(acceptanceState),
            responsibility: stableValue(responsibility),
          },
          source_manifest: sourceRecords.map(
            ({ source_id, path, media_type, byte_digest }) => ({
              source_id,
              ...(path ? { path } : {}),
              media_type,
              byte_digest,
            }),
          ),
        },
      };

      const outputValidation = validateSync(readerSchemaName, result);
      if (!outputValidation.valid) {
        const errors = (outputValidation.errors ?? [])
          .map((error) => `${error.instancePath}:${error.keyword}`)
          .join(", ");
        throw new Error(
          `TMPA Reader emitted an invalid ${coreVersion} result: ${errors}`,
        );
      }
      return result;
  }

  return {
    read: async (sources) => readSync(sources),
    readSync,
  };
}
