import { createHash } from "node:crypto";
import {
  chmod,
  constants as fsConstants,
  lstat,
  mkdir,
  open,
  readFile,
  realpath
} from "node:fs/promises";
import path from "node:path";
import { verifyRunrailHunkContinuity } from "./runrail-hunk-continuity-core.mjs";
import { verifyRunrailAuditV4 } from "./runrail-review-session-core.mjs";

export const RUNRAIL_AGENT_ASSURANCE = Object.freeze({
  schemaVersion: 1,
  packetArtifactType: "runrail-agent-assurance-packet",
  packetProfile: "runrail-agent-assurance-packet-v1",
  resultArtifactType: "runrail-agent-assurance-result",
  resultProfile: "runrail-agent-assurance-result-v1",
  product: "Runrail Local v1",
  outputDirectory: "agent-assurance",
  maximumHistoricalFindingIds: 100,
  maximumArtifactBytes: 512 * 1024
});

const FINDING_ID_PATTERN = /^[A-Z][A-Z0-9_]{2,79}$/u;
const SHA256_PATTERN = /^[a-f0-9]{64}$/u;
const PACKET_ID_PATTERN = /^RAP-[A-F0-9]{24}$/u;
const RESULT_ID_PATTERN = /^RAR-[A-F0-9]{24}$/u;
const severityOrder = Object.freeze({ high: 0, medium: 1, info: 2 });
const riskLevels = new Set(["low", "medium", "high"]);

const findingObjectives = Object.freeze({
  REVIEW_CONTRACT_CHANGED: Object.freeze({
    kind: "policy-boundary",
    summary: "Restore the candidate to the base-owned review contract.",
    successEvidence:
      "The next Runrail review must remain bound to the unchanged base contract. A policy change requires a separate human-owned baseline decision."
  }),
  REVIEW_CONTRACT_BUDGET_EXCEEDED: Object.freeze({
    kind: "change-scope",
    summary: "Reduce or split the candidate so it fits the base-owned review budget.",
    successEvidence:
      "The next Runrail review must pass the changed-file and diff-line budgets, or a human must use the repository's explicit exception process."
  }),
  REVIEW_CONTRACT_RULE_MATCH: Object.freeze({
    kind: "required-evidence",
    summary: "Satisfy the review lanes and evidence required by the matched base rule.",
    successEvidence:
      "The required evidence remains a human-reviewed external input. Do not change the base contract to suppress the match."
  }),
  REVIEW_CONTRACT_COMPANION_MISSING: Object.freeze({
    kind: "required-companion",
    summary: "Add the required companion change or leave the finding for a human decision.",
    successEvidence:
      "The next Runrail review must observe the required companion group under the unchanged base-owned contract."
  }),
  SECURITY_SENSITIVE_PATHS: Object.freeze({
    kind: "specialist-review",
    summary: "Keep the security-sensitive change explicit and prepare it for specialist review.",
    successEvidence:
      "Runrail cannot replace a security review. The next revision must not hide or bypass the security-sensitive scope."
  }),
  DEPENDENCY_FILES_CHANGED: Object.freeze({
    kind: "dependency-review",
    summary: "Prepare dependency and supply-chain evidence for the changed dependency definition.",
    successEvidence:
      "A human must inspect the dependency change and run the repository's approved checks outside Runrail."
  }),
  DELIVERY_CONFIGURATION_CHANGED: Object.freeze({
    kind: "operator-review",
    summary: "Prepare the delivery or infrastructure change for operator review.",
    successEvidence:
      "A human operator must review deployment impact and rollback outside Runrail."
  }),
  DATA_MIGRATION_CHANGED: Object.freeze({
    kind: "data-review",
    summary: "Prepare data-impact and rollback evidence for the migration or schema change.",
    successEvidence:
      "A human must review migration safety and rollback outside Runrail."
  }),
  NO_TEST_FILES_CHANGED: Object.freeze({
    kind: "test-evidence",
    summary: "Add or update the tests needed for the changed behavior.",
    successEvidence:
      "Run the repository's approved project tests outside Runrail and attach the result to the human review."
  }),
  LARGE_CHANGESET: Object.freeze({
    kind: "change-scope",
    summary: "Split or reduce the candidate so a human can review it with lower uncertainty.",
    successEvidence:
      "The next Runrail review must show a smaller bounded change or leave the larger scope for explicit human review."
  }),
  STATIC_SYNTAX_PREFLIGHT_FAILED: Object.freeze({
    kind: "preflight-repair",
    summary: "Correct the bounded Python, JavaScript, or JSON syntax failure.",
    successEvidence:
      "The next Runrail safe-static preflight must pass without running repository project code."
  }),
  PYTHON_SYNTAX_PREFLIGHT_FAILED: Object.freeze({
    kind: "preflight-repair",
    summary: "Correct the isolated Python syntax failure.",
    successEvidence:
      "The next Runrail Python syntax preflight must pass. Imports and runtime behavior still require project-context review."
  }),
  PYTHON_FILES_CHANGED: Object.freeze({
    kind: "human-review",
    summary: "Inspect Python imports and runtime behavior in project context.",
    successEvidence:
      "The syntax preflight is not runtime proof. A human must run approved project checks outside Runrail."
  }),
  TEST_FILES_CHANGED: Object.freeze({
    kind: "human-review",
    summary: "Inspect the test intent and run the repository's approved test command.",
    successEvidence:
      "The project test result remains external evidence and is not executed by Runrail."
  }),
  DOCUMENTATION_ONLY_CHANGE: Object.freeze({
    kind: "human-review",
    summary: "Review rendered documentation, links, and product wording.",
    successEvidence:
      "A human must confirm the rendered result and final wording."
  }),
  BOUNDED_CHANGESET: Object.freeze({
    kind: "human-review",
    summary: "Perform the remaining manual review and approved project checks.",
    successEvidence:
      "The bounded deterministic preflight is not proof of correctness or merge approval."
  })
});

export class RunrailAgentAssuranceError extends Error {
  constructor(code, message) {
    super(message);
    this.name = "RunrailAgentAssuranceError";
    this.code = code;
  }
}

function fail(code, message) {
  throw new RunrailAgentAssuranceError(code, message);
}

function isRecord(value) {
  return value !== null && typeof value === "object" && !Array.isArray(value);
}

function stableValue(value) {
  if (Array.isArray(value)) return value.map(stableValue);
  if (isRecord(value)) {
    return Object.fromEntries(
      Object.keys(value)
        .sort()
        .map((key) => [key, stableValue(value[key])])
    );
  }
  return value;
}

export function stableRunrailAgentAssuranceJson(value) {
  return `${JSON.stringify(stableValue(value), null, 2)}\n`;
}

function sha256(value) {
  return createHash("sha256").update(value).digest("hex");
}

export function computeRunrailAgentAssuranceChecksum(value) {
  return sha256(stableRunrailAgentAssuranceJson(value));
}

function exactKeys(value, expected, label, code = "agent-assurance-invalid") {
  if (!isRecord(value)) fail(code, `${label} must be an object.`);
  const actual = Object.keys(value).sort();
  const canonical = [...expected].sort();
  if (
    actual.length !== canonical.length ||
    actual.some((key, index) => key !== canonical[index])
  ) {
    fail(code, `${label} has an unsupported shape.`);
  }
}

function boundedString(value, label, minimum = 1, maximum = 4_096) {
  if (
    typeof value !== "string" ||
    value.length < minimum ||
    value.length > maximum ||
    value.includes("\u0000")
  ) {
    fail("agent-assurance-invalid", `${label} is invalid.`);
  }
  return value;
}

function expectBoolean(value, label) {
  if (typeof value !== "boolean") {
    fail("agent-assurance-invalid", `${label} must be a boolean.`);
  }
}

function expectInteger(value, label, minimum = 0, maximum = 10_000) {
  if (!Number.isSafeInteger(value) || value < minimum || value > maximum) {
    fail("agent-assurance-invalid", `${label} must be a bounded integer.`);
  }
}

function expectSha256(value, label) {
  if (!SHA256_PATTERN.test(String(value || ""))) {
    fail("agent-assurance-invalid", `${label} must be a SHA-256 digest.`);
  }
}

function compareText(left, right) {
  return Buffer.compare(Buffer.from(String(left), "utf8"), Buffer.from(String(right), "utf8"));
}

function validateFindingId(value, label = "Finding ID") {
  if (!FINDING_ID_PATTERN.test(String(value || "")) || !findingObjectives[value]) {
    fail("agent-assurance-finding-invalid", `${label} is not a supported deterministic Runrail finding.`);
  }
  return value;
}

function uniqueSortedFindingIds(value, label, maximum = 100) {
  if (!Array.isArray(value) || value.length > maximum) {
    fail("agent-assurance-finding-invalid", `${label} must be a bounded array.`);
  }
  const normalized = value.map((findingId) => validateFindingId(findingId, label)).sort(compareText);
  if (new Set(normalized).size !== normalized.length) {
    fail("agent-assurance-finding-invalid", `${label} contains duplicate finding IDs.`);
  }
  return normalized;
}

function objectiveForFinding(finding) {
  const findingId = validateFindingId(finding?.id);
  if (!Object.hasOwn(severityOrder, finding?.severity)) {
    fail("agent-assurance-finding-invalid", "A deterministic finding has an invalid severity.");
  }
  const definition = findingObjectives[findingId];
  return {
    findingId,
    severity: finding.severity,
    kind: definition.kind,
    summary: definition.summary,
    successEvidence: definition.successEvidence
  };
}

function compareObjectives(left, right) {
  const severityDelta = severityOrder[left.severity] - severityOrder[right.severity];
  return severityDelta || compareText(left.findingId, right.findingId);
}

function verifyObjective(objective) {
  exactKeys(
    objective,
    ["findingId", "severity", "kind", "summary", "successEvidence"],
    "Agent assurance objective"
  );
  const expected = objectiveForFinding({
    id: objective.findingId,
    severity: objective.severity
  });
  if (
    stableRunrailAgentAssuranceJson(objective) !==
    stableRunrailAgentAssuranceJson(expected)
  ) {
    fail("agent-assurance-finding-invalid", "An agent assurance objective changed its fixed product meaning.");
  }
  return objective;
}

function packetIdentityBody(packet) {
  return {
    sourceEvidence: packet.sourceEvidence,
    objectives: packet.objectives,
    history: packet.history,
    acceptance: packet.acceptance,
    boundaries: packet.boundaries
  };
}

function packetId(packet) {
  return `RAP-${computeRunrailAgentAssuranceChecksum(packetIdentityBody(packet))
    .slice(0, 24)
    .toUpperCase()}`;
}

function renderAgentBrief(packet) {
  const lines = [
    `Runrail Agent Assurance Packet ${packet.packetId}`,
    "",
    "Work only in the repository context already provided to you.",
    "Do not weaken or rewrite the base-owned review contract to bypass a finding.",
    "Do not claim that Runrail ran project tests, proved correctness, approved a merge, or granted execution authority.",
    ""
  ];
  if (packet.objectives.length === 0) {
    lines.push(
      "No high- or medium-severity correction objective remains in this packet.",
      "Prepare the candidate for approved project checks and a human decision."
    );
  } else {
    lines.push("Address these deterministic review objectives:");
    for (const objective of packet.objectives) {
      lines.push(
        `- [${objective.severity.toUpperCase()}] ${objective.findingId}: ${objective.summary}`,
        `  Evidence: ${objective.successEvidence}`
      );
    }
  }
  lines.push(
    "",
    "Exit conditions:",
    "- Create a new candidate revision without mutating the base-owned review contract.",
    "- Rerun Runrail against the new revision.",
    "- Resolve the listed findings without introducing a new high- or medium-severity finding.",
    "- Run approved repository project checks outside Runrail.",
    "- Leave the final decision to a human.",
    "",
    "This packet is source-free and advisory. Authority: none."
  );
  return `${lines.join("\n")}\n`;
}

function packetBoundaries() {
  return {
    localOnly: true,
    pathFree: true,
    repositoryIdentityIncluded: false,
    sourceContentIncluded: false,
    diffContentIncluded: false,
    refOrCommitIncluded: false,
    rawVerifierOutputIncluded: false,
    networkRequired: false,
    providerRequired: false,
    modelAuthority: "none",
    executionAuthority: "none",
    projectTestsExecuted: false,
    policyMutationAllowed: false,
    patchApplyAllowed: false,
    mergeApprovalGranted: false,
    humanDecisionRequired: true
  };
}

export function buildRunrailAgentAssurancePacket({
  audit,
  historicalFindingIds = [],
  historicalReviewCount = 0
}) {
  const verifiedAudit = verifyRunrailAuditV4(audit);
  expectInteger(
    historicalReviewCount,
    "Historical review count",
    0,
    100
  );
  const observedObjectives = verifiedAudit.decision.findings
    .map(objectiveForFinding)
    .sort(compareObjectives);
  const requiredObjectives = observedObjectives.filter(
    (objective) => objective.severity === "high" || objective.severity === "medium"
  );
  const previouslyObservedFindingIds = uniqueSortedFindingIds(
    historicalFindingIds,
    "Historical finding IDs",
    RUNRAIL_AGENT_ASSURANCE.maximumHistoricalFindingIds
  ).filter(
    (findingId) =>
      !observedObjectives.some((objective) => objective.findingId === findingId)
  );
  const sourceEvidence = {
    auditChecksumSha256: verifiedAudit.checksumSha256,
    reviewContractSha256: verifiedAudit.reviewContract.contractSha256,
    preflightPassed: verifiedAudit.preflightPassed,
    contractPassed: verifiedAudit.reviewContract.passed,
    riskLevel: verifiedAudit.decision.riskLevel,
    observedFindingIds: observedObjectives
      .map((objective) => objective.findingId)
      .sort(compareText)
  };
  const history = {
    lineageReviewCount: historicalReviewCount,
    previouslyObservedFindingIds
  };
  const acceptance = {
    sameReviewContractRequired: true,
    preflightMustPass: true,
    contractMustPass: true,
    requiredFindingIds: requiredObjectives
      .map((objective) => objective.findingId)
      .sort(compareText),
    newHighOrMediumFindingAllowed: false,
    projectTestsRemainExternal: true,
    humanDecisionRequired: true
  };
  const draft = {
    sourceEvidence,
    objectives: requiredObjectives,
    history,
    acceptance,
    boundaries: packetBoundaries()
  };
  const identity = packetId(draft);
  const body = {
    schemaVersion: RUNRAIL_AGENT_ASSURANCE.schemaVersion,
    artifactType: RUNRAIL_AGENT_ASSURANCE.packetArtifactType,
    profile: RUNRAIL_AGENT_ASSURANCE.packetProfile,
    product: RUNRAIL_AGENT_ASSURANCE.product,
    packetId: identity,
    ...draft,
    agentBrief: ""
  };
  body.agentBrief = renderAgentBrief(body);
  return verifyRunrailAgentAssurancePacket({
    ...body,
    checksumSha256: computeRunrailAgentAssuranceChecksum(body)
  });
}

export function verifyRunrailAgentAssurancePacket(packet) {
  exactKeys(
    packet,
    [
      "schemaVersion",
      "artifactType",
      "profile",
      "product",
      "packetId",
      "sourceEvidence",
      "objectives",
      "history",
      "acceptance",
      "boundaries",
      "agentBrief",
      "checksumSha256"
    ],
    "Agent assurance packet"
  );
  if (
    packet.schemaVersion !== RUNRAIL_AGENT_ASSURANCE.schemaVersion ||
    packet.artifactType !== RUNRAIL_AGENT_ASSURANCE.packetArtifactType ||
    packet.profile !== RUNRAIL_AGENT_ASSURANCE.packetProfile ||
    packet.product !== RUNRAIL_AGENT_ASSURANCE.product ||
    !PACKET_ID_PATTERN.test(String(packet.packetId || ""))
  ) {
    fail("agent-assurance-invalid", "The agent assurance packet identity is invalid.");
  }
  exactKeys(
    packet.sourceEvidence,
    [
      "auditChecksumSha256",
      "reviewContractSha256",
      "preflightPassed",
      "contractPassed",
      "riskLevel",
      "observedFindingIds"
    ],
    "Agent assurance source evidence"
  );
  expectSha256(packet.sourceEvidence.auditChecksumSha256, "Source audit checksum");
  expectSha256(packet.sourceEvidence.reviewContractSha256, "Source review contract checksum");
  expectBoolean(packet.sourceEvidence.preflightPassed, "Source preflight");
  expectBoolean(packet.sourceEvidence.contractPassed, "Source contract");
  if (!riskLevels.has(packet.sourceEvidence.riskLevel)) {
    fail("agent-assurance-invalid", "The source review priority is invalid.");
  }
  const observedFindingIds = uniqueSortedFindingIds(
    packet.sourceEvidence.observedFindingIds,
    "Observed finding IDs",
    20
  );
  if (!Array.isArray(packet.objectives) || packet.objectives.length > 20) {
    fail("agent-assurance-invalid", "Agent assurance objectives exceed their boundary.");
  }
  packet.objectives.forEach(verifyObjective);
  if (
    packet.objectives.some(
      (objective, index) =>
        index > 0 && compareObjectives(packet.objectives[index - 1], objective) >= 0
    )
  ) {
    fail("agent-assurance-invalid", "Agent assurance objectives must be unique and canonically ordered.");
  }
  if (
    packet.objectives.some(
      (objective) => !observedFindingIds.includes(objective.findingId)
    )
  ) {
    fail("agent-assurance-invalid", "An objective is not bound to the source audit finding set.");
  }
  exactKeys(
    packet.history,
    ["lineageReviewCount", "previouslyObservedFindingIds"],
    "Agent assurance history"
  );
  expectInteger(packet.history.lineageReviewCount, "History lineage review count", 0, 100);
  const historical = uniqueSortedFindingIds(
    packet.history.previouslyObservedFindingIds,
    "Previously observed finding IDs",
    RUNRAIL_AGENT_ASSURANCE.maximumHistoricalFindingIds
  );
  if (
    (packet.history.lineageReviewCount === 0 && historical.length > 0) ||
    historical.some((findingId) => observedFindingIds.includes(findingId))
  ) {
    fail("agent-assurance-invalid", "Agent assurance history is inconsistent.");
  }
  exactKeys(
    packet.acceptance,
    [
      "sameReviewContractRequired",
      "preflightMustPass",
      "contractMustPass",
      "requiredFindingIds",
      "newHighOrMediumFindingAllowed",
      "projectTestsRemainExternal",
      "humanDecisionRequired"
    ],
    "Agent assurance acceptance"
  );
  const requiredFindingIds = uniqueSortedFindingIds(
    packet.acceptance.requiredFindingIds,
    "Required finding IDs",
    20
  );
  const objectiveFindingIds = packet.objectives
    .map((objective) => objective.findingId)
    .sort(compareText);
  if (
    stableRunrailAgentAssuranceJson(requiredFindingIds) !==
    stableRunrailAgentAssuranceJson(objectiveFindingIds) ||
    packet.acceptance.sameReviewContractRequired !== true ||
    packet.acceptance.preflightMustPass !== true ||
    packet.acceptance.contractMustPass !== true ||
    packet.acceptance.newHighOrMediumFindingAllowed !== false ||
    packet.acceptance.projectTestsRemainExternal !== true ||
    packet.acceptance.humanDecisionRequired !== true
  ) {
    fail("agent-assurance-authority", "The packet weakened its acceptance or human-control boundary.");
  }
  exactKeys(
    packet.boundaries,
    [
      "localOnly",
      "pathFree",
      "repositoryIdentityIncluded",
      "sourceContentIncluded",
      "diffContentIncluded",
      "refOrCommitIncluded",
      "rawVerifierOutputIncluded",
      "networkRequired",
      "providerRequired",
      "modelAuthority",
      "executionAuthority",
      "projectTestsExecuted",
      "policyMutationAllowed",
      "patchApplyAllowed",
      "mergeApprovalGranted",
      "humanDecisionRequired"
    ],
    "Agent assurance boundaries"
  );
  if (
    stableRunrailAgentAssuranceJson(packet.boundaries) !==
    stableRunrailAgentAssuranceJson(packetBoundaries())
  ) {
    fail("agent-assurance-authority", "The packet expanded its authority or export boundary.");
  }
  boundedString(packet.agentBrief, "Agent assurance brief", 40, 64 * 1024);
  if (packet.agentBrief !== renderAgentBrief(packet)) {
    fail("agent-assurance-invalid", "The agent brief does not match the deterministic packet.");
  }
  if (packet.packetId !== packetId(packet)) {
    fail("agent-assurance-invalid", "The packet ID does not match its deterministic evidence.");
  }
  expectSha256(packet.checksumSha256, "Agent assurance packet checksum");
  const { checksumSha256, ...body } = packet;
  if (computeRunrailAgentAssuranceChecksum(body) !== checksumSha256) {
    fail("agent-assurance-tampered", "The agent assurance packet checksum is invalid.");
  }
  return packet;
}

function relationship(beforeAudit, afterAudit) {
  if (
    beforeAudit.source.baseCommit === afterAudit.source.baseCommit &&
    beforeAudit.source.candidateCommit === afterAudit.source.candidateCommit
  ) {
    return "same-candidate-rerun";
  }
  if (beforeAudit.source.baseCommit === afterAudit.source.baseCommit) {
    return "same-base";
  }
  if (beforeAudit.source.candidateCommit === afterAudit.source.baseCommit) {
    return "linear-successor";
  }
  fail(
    "agent-assurance-not-related",
    "The current audit must be a same-base revision, a linear successor, or a rerun of the same candidate."
  );
}

function resultRow(findingId, severity = null) {
  const definition = findingObjectives[validateFindingId(findingId)];
  return {
    findingId,
    severity: severity || "info",
    kind: definition.kind,
    summary: definition.summary
  };
}

function resultBoundaries() {
  return {
    localOnly: true,
    pathFree: true,
    repositoryIdentityIncluded: false,
    sourceContentIncluded: false,
    diffContentIncluded: false,
    refOrCommitIncluded: false,
    rawVerifierOutputIncluded: false,
    networkRequired: false,
    providerRequired: false,
    modelAuthority: "none",
    executionAuthority: "none",
    projectTestsExecuted: false,
    sourceMutationPerformed: false,
    policyMutationPerformed: false,
    patchApplied: false,
    mergeApprovalGranted: false,
    reviewCoverageRequiredForReady: true,
    humanDecisionRequired: true
  };
}

function resultTopActions(status, summary, currentEvidence) {
  if (status === "blocked") {
    return [
      {
        id: "INSPECT_BLOCKING_EVIDENCE",
        label: "Inspect blocking evidence",
        detail: "Restore the deterministic preflight and base-owned contract before another assurance decision."
      },
      {
        id: "RERUN_AFTER_REVISION",
        label: "Rerun after a new revision",
        detail: "Runrail must verify the revised candidate before a human decision."
      },
      {
        id: "KEEP_HUMAN_DECISION",
        label: "Keep the decision human",
        detail: "This result grants no apply or merge authority."
      }
    ];
  }
  if (status === "needs-correction") {
    const correctionCount =
      summary.remainingCount +
      summary.newCount +
      summary.reintroducedCount;
    return [
      {
        id: "RETURN_TO_CODING_AGENT",
        label:
          correctionCount > 0
            ? "Return the remaining objectives to the coding agent"
            : "Complete human Review Coverage",
        detail:
          correctionCount > 0
            ? `${correctionCount} high- or medium-severity objective(s) still require attention.`
            : currentEvidence.coverageVerified
              ? "At least one current hunk is unreviewed or marked needs revision."
              : "A verified current Review Coverage artifact is required before Runrail can report ready for human decision."
      },
      {
        id: "RERUN_AFTER_REVISION",
        label: "Rerun after a new revision",
        detail: "Runrail will verify the next candidate without applying it."
      },
      {
        id: "RUN_APPROVED_PROJECT_CHECKS",
        label: "Run approved project checks",
        detail: "Project tests remain external to Runrail."
      }
    ];
  }
  return [
    {
      id: "RUN_APPROVED_PROJECT_CHECKS",
      label: "Run approved project checks",
      detail: "Runrail did not execute the repository's project tests."
    },
    {
      id: "INSPECT_FINAL_EVIDENCE",
      label: "Inspect the final evidence",
      detail: "Confirm the review contract, findings, exact-hunk attention, and Change Passport."
    },
    {
      id: "MAKE_HUMAN_DECISION",
      label: "Make the human decision",
      detail: "Ready for human decision is not correctness proof or merge approval."
    }
  ];
}

function resultIdentityBody(result) {
  return {
    packet: result.packet,
    currentEvidence: result.currentEvidence,
    relationship: result.relationship,
    status: result.status,
    findings: result.findings,
    summary: result.summary,
    topActions: result.topActions,
    boundaries: result.boundaries
  };
}

function resultId(result) {
  return `RAR-${computeRunrailAgentAssuranceChecksum(resultIdentityBody(result))
    .slice(0, 24)
    .toUpperCase()}`;
}

function currentCoverageEvidence(afterAudit, afterCoverage) {
  if (!afterCoverage) {
    return {
      coverageChecksumSha256: null,
      coverageVerified: false,
      hunkCount: 0,
      reviewedHunkCount: 0,
      needsRevisionHunkCount: 0,
      unreviewedHunkCount: 0,
      attentionComplete: false
    };
  }
  const coverage = verifyRunrailHunkContinuity(afterCoverage);
  if (coverage.source.auditChecksumSha256 !== afterAudit.checksumSha256) {
    fail(
      "agent-assurance-coverage-mismatch",
      "The current Review Coverage does not belong to the current audit."
    );
  }
  return {
    coverageChecksumSha256: coverage.checksumSha256,
    coverageVerified: true,
    hunkCount: coverage.summary.hunkCount,
    reviewedHunkCount: coverage.summary.reviewedCount,
    needsRevisionHunkCount: coverage.summary.needsRevisionCount,
    unreviewedHunkCount: coverage.summary.unreviewedCount,
    attentionComplete: coverage.summary.attentionComplete
  };
}

export function evaluateRunrailAgentAssurance({
  packet,
  beforeAudit,
  afterAudit,
  afterCoverage = null
}) {
  const verifiedPacket = verifyRunrailAgentAssurancePacket(packet);
  const before = verifyRunrailAuditV4(beforeAudit);
  const after = verifyRunrailAuditV4(afterAudit);
  const coverageEvidence = currentCoverageEvidence(after, afterCoverage);
  if (verifiedPacket.sourceEvidence.auditChecksumSha256 !== before.checksumSha256) {
    fail("agent-assurance-source-mismatch", "The packet does not belong to the supplied baseline audit.");
  }
  const relationshipType = relationship(before, after);
  const policyBasisContinuous =
    verifiedPacket.sourceEvidence.reviewContractSha256 ===
    after.reviewContract.contractSha256;
  const required = new Set(verifiedPacket.acceptance.requiredFindingIds);
  const observed = new Set(verifiedPacket.sourceEvidence.observedFindingIds);
  const historical = new Set(
    verifiedPacket.history.previouslyObservedFindingIds
  );
  const currentById = new Map(
    after.decision.findings.map((finding) => [finding.id, finding])
  );
  const currentAttentionIds = after.decision.findings
    .filter((finding) => finding.severity === "high" || finding.severity === "medium")
    .map((finding) => finding.id);
  const resolvedIds = [...required]
    .filter((findingId) => !currentById.has(findingId))
    .sort(compareText);
  const remainingIds = [...required]
    .filter((findingId) => currentById.has(findingId))
    .sort(compareText);
  const addedIds = currentAttentionIds
    .filter((findingId) => !observed.has(findingId))
    .sort(compareText);
  const reintroducedIds = addedIds
    .filter((findingId) => historical.has(findingId))
    .sort(compareText);
  const newIds = addedIds
    .filter((findingId) => !historical.has(findingId))
    .sort(compareText);
  const findings = {
    resolved: resolvedIds.map((findingId) => {
      const objective = verifiedPacket.objectives.find(
        (candidate) => candidate.findingId === findingId
      );
      return resultRow(findingId, objective?.severity);
    }),
    remaining: remainingIds.map((findingId) =>
      resultRow(findingId, currentById.get(findingId).severity)
    ),
    new: newIds.map((findingId) =>
      resultRow(findingId, currentById.get(findingId).severity)
    ),
    reintroduced: reintroducedIds.map((findingId) =>
      resultRow(findingId, currentById.get(findingId).severity)
    )
  };
  const summary = {
    resolvedCount: findings.resolved.length,
    remainingCount: findings.remaining.length,
    newCount: findings.new.length,
    reintroducedCount: findings.reintroduced.length
  };
  const blocked =
    !after.preflightPassed ||
    !after.reviewContract.passed ||
    !policyBasisContinuous;
  const needsCorrection =
    relationshipType === "same-candidate-rerun" ||
    summary.remainingCount > 0 ||
    summary.newCount > 0 ||
    summary.reintroducedCount > 0 ||
    !coverageEvidence.coverageVerified ||
    !coverageEvidence.attentionComplete ||
    coverageEvidence.needsRevisionHunkCount > 0 ||
    coverageEvidence.unreviewedHunkCount > 0;
  const status = blocked
    ? "blocked"
    : needsCorrection
      ? "needs-correction"
      : "ready-for-human-decision";
  const body = {
    schemaVersion: RUNRAIL_AGENT_ASSURANCE.schemaVersion,
    artifactType: RUNRAIL_AGENT_ASSURANCE.resultArtifactType,
    profile: RUNRAIL_AGENT_ASSURANCE.resultProfile,
    product: RUNRAIL_AGENT_ASSURANCE.product,
    resultId: "",
    packet: {
      packetId: verifiedPacket.packetId,
      checksumSha256: verifiedPacket.checksumSha256,
      sourceAuditChecksumSha256:
        verifiedPacket.sourceEvidence.auditChecksumSha256
    },
    currentEvidence: {
      auditChecksumSha256: after.checksumSha256,
      reviewContractSha256: after.reviewContract.contractSha256,
      preflightPassed: after.preflightPassed,
      contractPassed: after.reviewContract.passed,
      riskLevel: after.decision.riskLevel,
      ...coverageEvidence
    },
    relationship: {
      type: relationshipType,
      policyBasisContinuous,
      originAuthenticated: false
    },
    status,
    findings,
    summary,
    topActions: [],
    boundaries: resultBoundaries()
  };
  body.topActions = resultTopActions(status, summary, body.currentEvidence);
  body.resultId = resultId(body);
  return verifyRunrailAgentAssuranceResult({
    ...body,
    checksumSha256: computeRunrailAgentAssuranceChecksum(body)
  });
}

function verifyResultRows(rows, label) {
  if (!Array.isArray(rows) || rows.length > 20) {
    fail("agent-assurance-invalid", `${label} must be a bounded array.`);
  }
  const ids = [];
  for (const row of rows) {
    exactKeys(row, ["findingId", "severity", "kind", "summary"], label);
    const findingId = validateFindingId(row.findingId, label);
    if (!Object.hasOwn(severityOrder, row.severity)) {
      fail("agent-assurance-invalid", `${label} has an invalid severity.`);
    }
    const expected = resultRow(findingId, row.severity);
    if (
      stableRunrailAgentAssuranceJson(row) !==
      stableRunrailAgentAssuranceJson(expected)
    ) {
      fail("agent-assurance-invalid", `${label} changed a fixed finding meaning.`);
    }
    ids.push(findingId);
  }
  if (
    new Set(ids).size !== ids.length ||
    ids.some((findingId, index) => index > 0 && compareText(ids[index - 1], findingId) >= 0)
  ) {
    fail("agent-assurance-invalid", `${label} must be unique and sorted.`);
  }
  return ids;
}

export function verifyRunrailAgentAssuranceResult(result) {
  exactKeys(
    result,
    [
      "schemaVersion",
      "artifactType",
      "profile",
      "product",
      "resultId",
      "packet",
      "currentEvidence",
      "relationship",
      "status",
      "findings",
      "summary",
      "topActions",
      "boundaries",
      "checksumSha256"
    ],
    "Agent assurance result"
  );
  if (
    result.schemaVersion !== RUNRAIL_AGENT_ASSURANCE.schemaVersion ||
    result.artifactType !== RUNRAIL_AGENT_ASSURANCE.resultArtifactType ||
    result.profile !== RUNRAIL_AGENT_ASSURANCE.resultProfile ||
    result.product !== RUNRAIL_AGENT_ASSURANCE.product ||
    !RESULT_ID_PATTERN.test(String(result.resultId || "")) ||
    !new Set(["blocked", "needs-correction", "ready-for-human-decision"]).has(result.status)
  ) {
    fail("agent-assurance-invalid", "The agent assurance result identity is invalid.");
  }
  exactKeys(
    result.packet,
    ["packetId", "checksumSha256", "sourceAuditChecksumSha256"],
    "Agent assurance result packet"
  );
  if (!PACKET_ID_PATTERN.test(String(result.packet.packetId || ""))) {
    fail("agent-assurance-invalid", "The result packet ID is invalid.");
  }
  expectSha256(result.packet.checksumSha256, "Result packet checksum");
  expectSha256(
    result.packet.sourceAuditChecksumSha256,
    "Result source audit checksum"
  );
  exactKeys(
    result.currentEvidence,
    [
      "auditChecksumSha256",
      "reviewContractSha256",
      "preflightPassed",
      "contractPassed",
      "riskLevel",
      "coverageChecksumSha256",
      "coverageVerified",
      "hunkCount",
      "reviewedHunkCount",
      "needsRevisionHunkCount",
      "unreviewedHunkCount",
      "attentionComplete"
    ],
    "Agent assurance current evidence"
  );
  expectSha256(result.currentEvidence.auditChecksumSha256, "Current audit checksum");
  expectSha256(
    result.currentEvidence.reviewContractSha256,
    "Current review contract checksum"
  );
  expectBoolean(result.currentEvidence.preflightPassed, "Current preflight");
  expectBoolean(result.currentEvidence.contractPassed, "Current contract");
  if (!riskLevels.has(result.currentEvidence.riskLevel)) {
    fail("agent-assurance-invalid", "The current review priority is invalid.");
  }
  if (result.currentEvidence.coverageChecksumSha256 !== null) {
    expectSha256(
      result.currentEvidence.coverageChecksumSha256,
      "Current coverage checksum"
    );
  }
  expectBoolean(
    result.currentEvidence.coverageVerified,
    "Current coverage verification"
  );
  expectBoolean(
    result.currentEvidence.attentionComplete,
    "Current coverage attention"
  );
  for (const key of [
    "hunkCount",
    "reviewedHunkCount",
    "needsRevisionHunkCount",
    "unreviewedHunkCount"
  ]) {
    expectInteger(result.currentEvidence[key], `Current ${key}`, 0, 20_000);
  }
  if (
    (
      result.currentEvidence.coverageChecksumSha256 !== null
    ) !== result.currentEvidence.coverageVerified ||
    (
      result.currentEvidence.reviewedHunkCount +
      result.currentEvidence.needsRevisionHunkCount +
      result.currentEvidence.unreviewedHunkCount
    ) !== result.currentEvidence.hunkCount ||
    result.currentEvidence.attentionComplete !==
      (
        result.currentEvidence.hunkCount > 0 &&
        result.currentEvidence.unreviewedHunkCount === 0
      )
  ) {
    fail("agent-assurance-invalid", "The current Review Coverage evidence is inconsistent.");
  }
  exactKeys(
    result.relationship,
    ["type", "policyBasisContinuous", "originAuthenticated"],
    "Agent assurance relationship"
  );
  if (
    !new Set(["same-candidate-rerun", "same-base", "linear-successor"]).has(
      result.relationship.type
    ) ||
    typeof result.relationship.policyBasisContinuous !== "boolean" ||
    result.relationship.originAuthenticated !== false
  ) {
    fail("agent-assurance-invalid", "The assurance relationship is invalid.");
  }
  exactKeys(
    result.findings,
    ["resolved", "remaining", "new", "reintroduced"],
    "Agent assurance findings"
  );
  const groups = Object.fromEntries(
    Object.entries(result.findings).map(([key, rows]) => [
      key,
      verifyResultRows(rows, `Agent assurance ${key} findings`)
    ])
  );
  const allIds = Object.values(groups).flat();
  if (new Set(allIds).size !== allIds.length) {
    fail("agent-assurance-invalid", "A finding appears in more than one assurance result group.");
  }
  exactKeys(
    result.summary,
    ["resolvedCount", "remainingCount", "newCount", "reintroducedCount"],
    "Agent assurance summary"
  );
  for (const [key, rows] of [
    ["resolvedCount", groups.resolved],
    ["remainingCount", groups.remaining],
    ["newCount", groups.new],
    ["reintroducedCount", groups.reintroduced]
  ]) {
    expectInteger(result.summary[key], `Agent assurance ${key}`, 0, 20);
    if (result.summary[key] !== rows.length) {
      fail("agent-assurance-invalid", "The assurance summary does not match its finding groups.");
    }
  }
  if (!Array.isArray(result.topActions) || result.topActions.length !== 3) {
    fail("agent-assurance-invalid", "Agent assurance must expose exactly three bounded next actions.");
  }
  for (const action of result.topActions) {
    exactKeys(action, ["id", "label", "detail"], "Agent assurance action");
    boundedString(action.id, "Agent assurance action ID", 3, 80);
    boundedString(action.label, "Agent assurance action label", 3, 160);
    boundedString(action.detail, "Agent assurance action detail", 3, 500);
  }
  if (
    stableRunrailAgentAssuranceJson(result.topActions) !==
    stableRunrailAgentAssuranceJson(
      resultTopActions(
        result.status,
        result.summary,
        result.currentEvidence
      )
    )
  ) {
    fail("agent-assurance-invalid", "Agent assurance next actions are not deterministic.");
  }
  exactKeys(
    result.boundaries,
    [
      "localOnly",
      "pathFree",
      "repositoryIdentityIncluded",
      "sourceContentIncluded",
      "diffContentIncluded",
      "refOrCommitIncluded",
      "rawVerifierOutputIncluded",
      "networkRequired",
      "providerRequired",
      "modelAuthority",
      "executionAuthority",
      "projectTestsExecuted",
      "sourceMutationPerformed",
      "policyMutationPerformed",
      "patchApplied",
      "mergeApprovalGranted",
      "reviewCoverageRequiredForReady",
      "humanDecisionRequired"
    ],
    "Agent assurance result boundaries"
  );
  if (
    stableRunrailAgentAssuranceJson(result.boundaries) !==
    stableRunrailAgentAssuranceJson(resultBoundaries())
  ) {
    fail("agent-assurance-authority", "The assurance result expanded its authority or export boundary.");
  }
  const shouldBlock =
    !result.currentEvidence.preflightPassed ||
    !result.currentEvidence.contractPassed ||
    !result.relationship.policyBasisContinuous;
  const shouldNeedCorrection =
    result.relationship.type === "same-candidate-rerun" ||
    result.summary.remainingCount > 0 ||
    result.summary.newCount > 0 ||
    result.summary.reintroducedCount > 0 ||
    !result.currentEvidence.coverageVerified ||
    !result.currentEvidence.attentionComplete ||
    result.currentEvidence.needsRevisionHunkCount > 0 ||
    result.currentEvidence.unreviewedHunkCount > 0;
  const expectedStatus = shouldBlock
    ? "blocked"
    : shouldNeedCorrection
      ? "needs-correction"
      : "ready-for-human-decision";
  if (result.status !== expectedStatus) {
    fail("agent-assurance-invalid", "The assurance status does not match its evidence.");
  }
  if (result.resultId !== resultId(result)) {
    fail("agent-assurance-invalid", "The result ID does not match its deterministic evidence.");
  }
  expectSha256(result.checksumSha256, "Agent assurance result checksum");
  const { checksumSha256, ...body } = result;
  if (computeRunrailAgentAssuranceChecksum(body) !== checksumSha256) {
    fail("agent-assurance-tampered", "The agent assurance result checksum is invalid.");
  }
  return result;
}

export function renderRunrailAgentAssurancePacketMarkdown(packet) {
  const verified = verifyRunrailAgentAssurancePacket(packet);
  return [
    "# Runrail Agent Assurance Packet",
    "",
    `Packet: \`${verified.packetId}\``,
    `Source audit checksum: \`${verified.sourceEvidence.auditChecksumSha256}\``,
    "",
    "```text",
    verified.agentBrief.trimEnd(),
    "```",
    "",
    "This artifact contains no repository identity, source, diff, path, ref, commit, raw verifier output, provider key, execution authority, patch authority, or merge approval.",
    ""
  ].join("\n");
}

export function renderRunrailAgentAssuranceResultMarkdown(result) {
  const verified = verifyRunrailAgentAssuranceResult(result);
  const group = (label, rows) => [
    `## ${label}`,
    "",
    ...(rows.length
      ? rows.map(
          (row) =>
            `- **${row.severity.toUpperCase()}** \`${row.findingId}\`: ${row.summary}`
        )
      : ["- None"]),
    ""
  ];
  return [
    "# Runrail Agent Assurance Result",
    "",
    `Status: **${verified.status}**`,
    `Result: \`${verified.resultId}\``,
    `Relationship: \`${verified.relationship.type}\``,
    "",
    ...group("Resolved", verified.findings.resolved),
    ...group("Remaining", verified.findings.remaining),
    ...group("New", verified.findings.new),
    ...group("Reintroduced", verified.findings.reintroduced),
    "Project tests and the final decision remain outside Runrail. Authority: none.",
    ""
  ].join("\n");
}

function isInsideOrEqual(candidate, parent) {
  const relative = path.relative(parent, candidate);
  return (
    relative === "" ||
    (
      relative !== ".." &&
      !relative.startsWith(`..${path.sep}`) &&
      !path.isAbsolute(relative)
    )
  );
}

async function ordinaryCanonicalDirectory(directory, label) {
  if (!path.isAbsolute(directory)) {
    fail("agent-assurance-path-invalid", `${label} must be an absolute path.`);
  }
  const normalized = path.resolve(directory);
  const entry = await lstat(normalized).catch(() => null);
  if (!entry || entry.isSymbolicLink() || !entry.isDirectory()) {
    fail("agent-assurance-path-invalid", `${label} must be an ordinary existing directory.`);
  }
  const canonical = await realpath(normalized);
  if (canonical !== normalized) {
    fail("agent-assurance-path-invalid", `${label} must not traverse a symbolic link.`);
  }
  return canonical;
}

async function ensureOwnedDirectory(parent, name) {
  if (!/^[A-Z0-9-]{3,64}$/iu.test(name)) {
    fail("agent-assurance-path-invalid", "The assurance artifact directory name is invalid.");
  }
  const target = path.join(parent, name);
  if (!isInsideOrEqual(target, parent) || target === parent) {
    fail("agent-assurance-path-invalid", "The assurance artifact directory escaped its owner.");
  }
  const existing = await lstat(target).catch(() => null);
  if (!existing) {
    await mkdir(target, { mode: 0o700 });
  } else if (existing.isSymbolicLink() || !existing.isDirectory()) {
    fail("agent-assurance-path-invalid", "An assurance artifact directory is not ordinary.");
  }
  const canonical = await realpath(target);
  if (canonical !== target || !isInsideOrEqual(canonical, parent)) {
    fail("agent-assurance-path-invalid", "An assurance artifact directory traverses a symbolic link.");
  }
  if (process.platform !== "win32") await chmod(canonical, 0o700);
  return canonical;
}

async function writeMatchingPrivateFile(filePath, contents) {
  const bytes = Buffer.from(contents, "utf8");
  const existing = await lstat(filePath).catch(() => null);
  if (existing) {
    if (
      existing.isSymbolicLink() ||
      !existing.isFile() ||
      existing.size !== bytes.length ||
      existing.size > RUNRAIL_AGENT_ASSURANCE.maximumArtifactBytes
    ) {
      fail("agent-assurance-artifact-invalid", "An assurance artifact target is invalid.");
    }
    const canonical = await realpath(filePath);
    if (canonical !== filePath) {
      fail("agent-assurance-artifact-invalid", "An assurance artifact traverses a symbolic link.");
    }
    const stored = await readFile(filePath);
    if (!stored.equals(bytes)) {
      fail("agent-assurance-artifact-exists", "A different assurance artifact already exists.");
    }
    return;
  }
  const flags =
    fsConstants.O_CREAT |
    fsConstants.O_EXCL |
    fsConstants.O_WRONLY |
    (fsConstants.O_NOFOLLOW || 0);
  const handle = await open(filePath, flags, 0o600);
  try {
    await handle.writeFile(bytes);
    await handle.sync();
  } finally {
    await handle.close();
  }
  if (process.platform !== "win32") await chmod(filePath, 0o600);
}

async function artifactRoot(reviewRoot, identity) {
  const canonicalReviewRoot = await ordinaryCanonicalDirectory(
    reviewRoot,
    "Review artifact root"
  );
  const assuranceRoot = await ensureOwnedDirectory(
    canonicalReviewRoot,
    RUNRAIL_AGENT_ASSURANCE.outputDirectory
  );
  return ensureOwnedDirectory(assuranceRoot, identity);
}

export async function writeRunrailAgentAssurancePacket({
  audit,
  reviewRoot,
  historicalFindingIds = [],
  historicalReviewCount = 0
}) {
  const packet = buildRunrailAgentAssurancePacket({
    audit,
    historicalFindingIds,
    historicalReviewCount
  });
  const outputRoot = await artifactRoot(reviewRoot, packet.packetId);
  const packetPath = path.join(outputRoot, "packet.json");
  const briefPath = path.join(outputRoot, "AGENT_CORRECTION.md");
  await writeMatchingPrivateFile(
    packetPath,
    stableRunrailAgentAssuranceJson(packet)
  );
  await writeMatchingPrivateFile(
    briefPath,
    renderRunrailAgentAssurancePacketMarkdown(packet)
  );
  const stored = verifyRunrailAgentAssurancePacket(
    JSON.parse(await readFile(packetPath, "utf8"))
  );
  if (stored.checksumSha256 !== packet.checksumSha256) {
    fail("agent-assurance-artifact-invalid", "The stored assurance packet did not verify.");
  }
  return { packet, outputRoot, packetPath, briefPath };
}

export async function writeRunrailAgentAssuranceResult({
  packet,
  beforeAudit,
  afterAudit,
  afterCoverage = null,
  reviewRoot
}) {
  const result = evaluateRunrailAgentAssurance({
    packet,
    beforeAudit,
    afterAudit,
    afterCoverage
  });
  const outputRoot = await artifactRoot(reviewRoot, result.resultId);
  const resultPath = path.join(outputRoot, "result.json");
  const reviewPath = path.join(outputRoot, "ASSURANCE_RESULT.md");
  await writeMatchingPrivateFile(
    resultPath,
    stableRunrailAgentAssuranceJson(result)
  );
  await writeMatchingPrivateFile(
    reviewPath,
    renderRunrailAgentAssuranceResultMarkdown(result)
  );
  const stored = verifyRunrailAgentAssuranceResult(
    JSON.parse(await readFile(resultPath, "utf8"))
  );
  if (stored.checksumSha256 !== result.checksumSha256) {
    fail("agent-assurance-artifact-invalid", "The stored assurance result did not verify.");
  }
  return { result, outputRoot, resultPath, reviewPath };
}

export async function loadRunrailAgentAssurancePacket(packetPath) {
  if (!path.isAbsolute(packetPath)) {
    fail("agent-assurance-path-invalid", "The assurance packet path must be absolute.");
  }
  const normalized = path.resolve(packetPath);
  const entry = await lstat(normalized).catch(() => null);
  if (
    !entry ||
    entry.isSymbolicLink() ||
    !entry.isFile() ||
    entry.size < 2 ||
    entry.size > RUNRAIL_AGENT_ASSURANCE.maximumArtifactBytes
  ) {
    fail("agent-assurance-artifact-invalid", "The assurance packet must be a bounded ordinary file.");
  }
  const canonical = await realpath(normalized);
  if (canonical !== normalized) {
    fail("agent-assurance-artifact-invalid", "The assurance packet path traverses a symbolic link.");
  }
  try {
    return verifyRunrailAgentAssurancePacket(
      JSON.parse(await readFile(canonical, "utf8"))
    );
  } catch (error) {
    if (error instanceof RunrailAgentAssuranceError) throw error;
    fail("agent-assurance-artifact-invalid", "The assurance packet does not contain valid JSON.");
  }
}
