import type {
  AgentRunId,
  AttemptId,
  ChangeSetId,
  FactoryRunId,
  I1AgentRunStatus,
  I1AttemptStatus,
  I1ChangeSetReviewReadModel,
  I1FactoryRunDetailReadModel,
  I1FactoryRunStatus,
  I1ProtectedActionAvailability,
  I1ReviewDisposition,
  I1ReviewStatus,
  PlanRevisionId,
  PrincipalId,
  ProjectId,
  ProviderId,
  ProviderReference,
  ReviewId,
  TaskId,
  WorkspaceId,
} from "@awp/contracts";
import type { C1ProjectOverviewPayload, C1TaskFixturePayload, QueueReadiness } from "./model.js";
import { renderProjectOverview, type ProjectConfigurationViewItem } from "./render-project.js";
import { renderQueuePage } from "./render-queue.js";
import { renderFactoryRun } from "./render-factory.js";
import { renderReviewPage } from "./render-review.js";
import { renderAccountsPage, type AccountListItem } from "./render-accounts.js";
import { renderProjectsPage, type ProjectListItem } from "./render-projects.js";
import { escapeHtml } from "./html.js";

interface LivePlanRevision {
  readonly id: PlanRevisionId;
  readonly planId: C1ProjectOverviewPayload["plans"][number]["id"];
  readonly projectId: ProjectId;
  readonly sequence: number;
  readonly title: string;
  readonly projectVisionVersionId?: string;
  readonly goalIds: C1ProjectOverviewPayload["plans"][number]["goalIds"];
}

interface LiveFactoryRun {
  readonly id: FactoryRunId;
  readonly projectId: ProjectId;
  readonly planRevisionId: PlanRevisionId;
  readonly taskId?: TaskId;
  readonly status: I1FactoryRunStatus;
  readonly reason?: string;
  readonly revision: number;
}

interface LiveAgentRun {
  readonly id: AgentRunId;
  readonly factoryRunId: FactoryRunId;
  readonly taskId: TaskId;
  readonly agentPrincipalId: PrincipalId;
  readonly role: "coder" | "reviewer";
  readonly status: I1AgentRunStatus;
  readonly reason?: string;
  readonly revision: number;
}

interface LiveAttempt {
  readonly id: AttemptId;
  readonly agentRunId: AgentRunId;
  readonly workspaceId: WorkspaceId;
  readonly status: I1AttemptStatus;
  readonly providerId?: ProviderId;
  readonly accountId?: string;
  readonly model?: string;
  readonly reason?: string;
  readonly toolCalls?: readonly { readonly name: string; readonly summary: string }[];
  readonly selection: {
    readonly kind: "initial" | "retry" | "fallback";
    readonly reason: string;
    readonly previousAttemptId?: AttemptId;
  };
  readonly providerReference?: ProviderReference;
  readonly revision: number;
}

interface LiveChangeSet {
  readonly id: ChangeSetId;
  readonly projectId: ProjectId;
  readonly taskId: TaskId;
  readonly producerAttemptId: AttemptId;
  readonly baseIdentity: string;
  readonly candidateDigest: string;
  readonly candidateManifest: {
    readonly treeDigest: string;
    readonly patchDigest: string;
    readonly changedPaths: readonly string[];
    readonly changes: readonly {
      readonly path: string;
      readonly kind: "add" | "modify" | "delete";
    }[];
  };
  readonly diff: string;
  readonly repositoryKey?: string;
  readonly publicationReference?: ProviderReference;
  readonly targetReference?: string;
  readonly targetRevision?: string;
  readonly mergeReference?: ProviderReference;
  readonly resultingRevision?: string;
  readonly revision: number;
  readonly status: I1ChangeSetReviewReadModel["status"];
}

interface LiveReview {
  readonly id: ReviewId;
  readonly changeSetId: ChangeSetId;
  readonly candidateDigest: string;
  readonly reviewerPrincipalId: PrincipalId;
  readonly reviewerAgentRunId?: AgentRunId;
  readonly status: I1ReviewStatus;
  readonly disposition?: I1ReviewDisposition;
}
interface LiveVerificationEvidence {
  readonly id: string;
  readonly changeSetId: ChangeSetId;
  readonly candidateDigest: string;
  readonly name: string;
  readonly state: "passed" | "failed" | "stale" | "missing";
  readonly source: string;
  readonly observedAt: string;
  readonly required: boolean;
  readonly details?: Readonly<Record<string, unknown>>;
}

interface LiveWorkspace {
  readonly id: WorkspaceId;
  readonly projectId: ProjectId;
  readonly revision: number;
  readonly checkpointDigest?: string;
  readonly checkpointedAt?: string;
  readonly checkpointSource?: string;
  readonly checkpointCollectedAt?: string;
  readonly cleanedAt?: string;
}

interface LiveBusinessEvent {
  readonly id: string;
  readonly type: string;
  readonly occurredAt: string;
  readonly aggregateType: string;
  readonly aggregateId: string;
  readonly payload: unknown;
}

interface LiveReviewFinding {
  readonly id: string;
  readonly changeSetId: ChangeSetId;
  readonly candidateDigest: string;
  readonly severity: "blocking" | "warning" | "recommendation" | "info";
  readonly summary: string;
  readonly source?: string;
  readonly resolved: boolean;
}

export interface LiveHierarchy {
  readonly acceptance?: { readonly checked: number; readonly total: number };
  readonly project: C1ProjectOverviewPayload["project"] & { readonly revision?: number };
  readonly vision?: { readonly id: string; readonly sequence: number; readonly summary: string };
  readonly goals: C1ProjectOverviewPayload["goals"];
  readonly plans: readonly {
    readonly id: C1ProjectOverviewPayload["plans"][number]["id"];
    readonly title: string;
    readonly status: C1ProjectOverviewPayload["plans"][number]["status"];
    readonly revision?: number;
  }[];
  readonly planRevisions: readonly LivePlanRevision[];
  readonly tasks: readonly (C1TaskFixturePayload & { readonly position?: number })[];
  readonly factoryRuns: readonly LiveFactoryRun[];
  readonly agentRuns: readonly LiveAgentRun[];
  readonly attempts: readonly LiveAttempt[];
  readonly workspaces?: readonly LiveWorkspace[];
  readonly changeSets: readonly LiveChangeSet[];
  readonly reviews: readonly LiveReview[];
  readonly verificationEvidence: readonly LiveVerificationEvidence[];
  readonly reviewFindings: readonly LiveReviewFinding[];
  readonly events: readonly LiveBusinessEvent[];
}

function readiness(
  task: LiveHierarchy["tasks"][number],
  tasks: LiveHierarchy["tasks"],
): { state: QueueReadiness; unsatisfied: C1TaskFixturePayload["dependencyIds"] } {
  const unsatisfied = task.dependencyIds.filter(
    (dependencyId) =>
      tasks.find((candidate) => candidate.id === dependencyId)?.status !== "completed",
  );
  if (task.status === "completed") return { state: "DONE", unsatisfied };
  if (task.status === "cancelled") return { state: "CANCELLED", unsatisfied };
  if (["dispatched", "queued", "executing"].includes(task.status))
    return { state: "RUNNING", unsatisfied };
  if (task.status === "ready") return { state: "READY", unsatisfied };
  if (task.status === "blocked")
    return { state: unsatisfied.length ? "BLOCKED_DEPENDENCY" : "BLOCKED_POLICY", unsatisfied };
  if (["review", "correction"].includes(task.status)) return { state: "RUNNING", unsatisfied };
  if (task.status === "waiting") return { state: "WAITING_USER", unsatisfied };
  if (task.status === "failed") return { state: "BLOCKED_POLICY", unsatisfied };
  return { state: "PAUSED", unsatisfied };
}

export function toOverview(hierarchy: LiveHierarchy): {
  readonly model: C1ProjectOverviewPayload;
  readonly tasks: readonly C1TaskFixturePayload[];
} {
  const tasks = hierarchy.tasks.map((task) => ({ ...task, position: task.position ?? 0 }));
  const revisionByPlan = new Map(
    hierarchy.planRevisions.map((revision) => [revision.planId, revision]),
  );
  const model: C1ProjectOverviewPayload = {
    project: hierarchy.project,
    goals: hierarchy.goals,
    plans: hierarchy.plans.map((plan) => ({
      id: plan.id,
      title: plan.title,
      status: plan.status,
      goalIds: revisionByPlan.get(plan.id)?.goalIds ?? [],
    })),
    queue: tasks.map((task) => {
      const current = readiness(task, tasks);
      return {
        taskId: task.id,
        planRevisionId: task.planRevisionId,
        goalIds: task.goalIds ?? [],
        position: task.position ?? 0,
        readinessState: current.state,
        unsatisfiedDependencyIds: current.unsatisfied,
      };
    }),
    factoryRuns: hierarchy.factoryRuns.map((run) => ({
      id: run.id,
      status: run.status,
      ...(run.reason === undefined ? {} : { reason: run.reason }),
    })),
  };
  return { model, tasks };
}

function document(title: string, body: string, acceptance = ""): string {
  return `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="color-scheme" content="dark"><title>${escapeHtml(title)} — AWP</title><link rel="stylesheet" href="/u1.css"><link rel="stylesheet" href="/lifecycle.css"><link rel="stylesheet" href="/common.css"></head><body>${body}${acceptance}<div class="toast-region" aria-live="polite" aria-atomic="true"></div><script type="module" src="/client.js"></script></body></html>`;
}

function acceptanceMarkup(hierarchy: LiveHierarchy): string {
  return hierarchy.acceptance
    ? `<div class="awp-acceptance" data-golive-progress><span>I1 acceptance</span><strong data-golive-fraction>${hierarchy.acceptance.checked} / ${hierarchy.acceptance.total}</strong><progress value="${hierarchy.acceptance.checked}" max="${hierarchy.acceptance.total}" aria-label="I1 acceptance completion"></progress></div>`
    : "";
}

const journeyStageLabels = new Map<string, string>([
  ["TaskDispatched", "Task dispatched"],
  ["FactoryRunStarted", "FactoryRun started"],
  ["AgentRunStarted", "AgentRun started"],
  ["ChangeSetCreated", "ChangeSet created"],
  ["ReviewAgentAssigned", "Review recorded"],
  ["MergeCompleted", "Merge completed"],
]);

function journeyActivityMarkup(hierarchy: LiveHierarchy): string {
  const events = hierarchy.events.filter((event) => journeyStageLabels.has(event.type));
  if (events.length === 0) return "";
  return `<section class="panel" data-live-stage-timeline aria-label="Journey stage timeline"><div class="panel-head"><h2>Journey activity</h2><p>Durable authoritative stage transitions.</p></div><div class="panel-body"><div class="timeline">${events
    .map(
      (event) =>
        `<div class="event" data-live-stage-event="${escapeHtml(event.type)}" data-event-aggregate-id="${escapeHtml(event.aggregateId)}" data-event-occurred-at="${escapeHtml(event.occurredAt)}"><span class="event-time">${escapeHtml(event.occurredAt)}</span><div class="event-title">${escapeHtml(journeyStageLabels.get(event.type)!)}</div><div class="event-sub">${escapeHtml(event.aggregateType)} ${escapeHtml(event.aggregateId)}</div></div>`,
    )
    .join("")}</div></div></section>`;
}

export function renderApprovedLiveProjectList(projects: readonly ProjectListItem[]): string {
  return document("Projects", renderProjectsPage(projects));
}

export function renderApprovedLiveProject(
  hierarchy: LiveHierarchy,
  tab = "overview",
  runAccounts: readonly AccountListItem[] = [],
  settings: readonly ProjectConfigurationViewItem[] = [],
): string {
  const { model, tasks } = toOverview(hierarchy);
  const body = renderProjectOverview(
    model,
    tasks,
    tab === "overview" && model.goals.length === 0 && model.plans.length === 0 ? "empty" : "ready",
    tab,
    runAccounts,
    hierarchy.vision?.summary,
    settings,
  );
  const activity = journeyActivityMarkup(hierarchy);
  const liveBody = activity ? body.replace("</main>", `${activity}</main>`) : body;
  return document(model.project.name, liveBody, acceptanceMarkup(hierarchy));
}

export function renderApprovedLiveAccounts(
  hierarchy: LiveHierarchy,
  accounts: readonly AccountListItem[],
): string {
  return document(
    `${hierarchy.project.name} Accounts`,
    renderAccountsPage({ id: hierarchy.project.id, name: hierarchy.project.name }, accounts),
  );
}

export function renderApprovedLiveQueue(hierarchy: LiveHierarchy, url: URL): string {
  const { model, tasks } = toOverview(hierarchy);
  return document(
    `${model.project.name} Queue`,
    renderQueuePage(
      model,
      tasks,
      url.searchParams.get("view") ?? "queue",
      url.searchParams.get("state") ?? "ready",
      url.searchParams.get("task") ?? undefined,
    ),
  );
}

function taskAttention(task: LiveHierarchy["tasks"][number]) {
  if (task.status === "review")
    return {
      kind: "none" as const,
      code: "independent-review",
      summary: "Independent reviewer AgentRun is handling the candidate",
      humanActionRequired: false,
    };
  if (task.status === "correction")
    return {
      kind: "transient-recovery" as const,
      code: "correction",
      summary: "AWP correction work is in progress",
      humanActionRequired: false,
    };
  if (task.status === "failed")
    return {
      kind: "failure" as const,
      code: "failed",
      summary: "Execution failure recorded",
      humanActionRequired: false,
    };
  if (task.status === "blocked")
    return {
      kind: "policy" as const,
      code: "blocked",
      summary: "Task is blocked",
      humanActionRequired: false,
    };
  return {
    kind: "none" as const,
    code: "none",
    summary: "No attention required",
    humanActionRequired: false,
  };
}

function taskNextAction(task: LiveHierarchy["tasks"][number]) {
  if (task.status === "review")
    return {
      kind: "automatic" as const,
      code: "independent-review",
      summary: "Wait for the independent reviewer AgentRun",
    };
  if (task.status === "correction")
    return { kind: "automatic" as const, code: "correction", summary: "Correction work continues" };
  if (task.status === "blocked")
    return { kind: "automatic" as const, code: "dependency", summary: "Wait for prerequisites" };
  if (task.status === "completed" || task.status === "cancelled")
    return { kind: "none" as const, code: "terminal", summary: "No further action" };
  return {
    kind: "automatic" as const,
    code: "continue",
    summary: "Continue authoritative execution",
  };
}

export function factoryDetail(
  hierarchy: LiveHierarchy,
  factoryRunId: string,
): I1FactoryRunDetailReadModel {
  const factoryRun = hierarchy.factoryRuns.find((run) => run.id === factoryRunId);
  if (!factoryRun) throw new Error(`Unknown FactoryRun ${factoryRunId}`);
  const tasks = hierarchy.tasks.filter((task) => task.planRevisionId === factoryRun.planRevisionId);
  const agentRuns = hierarchy.agentRuns.filter((run) => run.factoryRunId === factoryRun.id);
  const attemptsByAgent = new Map<AgentRunId, LiveAttempt[]>();
  for (const attempt of hierarchy.attempts) {
    const list = attemptsByAgent.get(attempt.agentRunId) ?? [];
    list.push(attempt);
    attemptsByAgent.set(attempt.agentRunId, list);
  }
  const projectedAgents = agentRuns.map((run) => {
    const attempts = attemptsByAgent.get(run.id) ?? [];
    const current = attempts.at(-1);
    return {
      id: run.id,
      taskId: run.taskId,
      role: run.role,
      status: run.status,
      ...(run.reason === undefined ? {} : { reason: run.reason }),
      ...(current === undefined ? {} : { currentAttemptId: current.id }),
      attempts: attempts.map((attempt) => ({
        id: attempt.id,
        workspaceId: attempt.workspaceId,
        status: attempt.status,
        ...(attempt.providerId === undefined ? {} : { providerId: attempt.providerId }),
        ...(attempt.accountId === undefined ? {} : { accountId: attempt.accountId }),
        ...(attempt.model === undefined ? {} : { model: attempt.model }),
        selection: attempt.selection,
        ...(attempt.providerReference === undefined
          ? {}
          : { providerReference: attempt.providerReference }),
        ...(attempt.reason === undefined ? {} : { reason: attempt.reason }),
        ...(attempt.toolCalls === undefined ? {} : { toolCalls: attempt.toolCalls }),
      })),
    };
  });
  const activeAttempt = projectedAgents.flatMap((run) => run.attempts).at(-1);
  const activeWorkspace = activeAttempt
    ? hierarchy.workspaces?.find((workspace) => workspace.id === activeAttempt.workspaceId)
    : undefined;
  const failedAttempt = hierarchy.attempts
    .filter((attempt) => agentRuns.some((run) => run.id === attempt.agentRunId) && attempt.reason)
    .at(-1);
  const liveExecutorPresent = projectedAgents.some((run) =>
    run.attempts.some((attempt) =>
      ["provisioning", "running", "checkpointing"].includes(attempt.status),
    ),
  );
  return {
    id: factoryRun.id,
    revision: factoryRun.revision,
    projectId: factoryRun.projectId,
    planRevisionId: factoryRun.planRevisionId,
    status: factoryRun.status,
    ...(factoryRun.reason === undefined ? {} : { reason: factoryRun.reason }),
    stage: factoryRun.status,
    work: tasks.map((task) => {
      const currentAgent = [...agentRuns].reverse().find((run) => run.taskId === task.id);
      return {
        taskId: task.id,
        planRevisionId: task.planRevisionId,
        title: task.title,
        status: task.status,
        ...(currentAgent === undefined
          ? {}
          : { currentActorId: currentAgent.agentPrincipalId, currentAgentRunId: currentAgent.id }),
        stage: task.status,
        attention: taskAttention(task),
        nextAction: taskNextAction(task),
      };
    }),
    agentRuns: projectedAgents,
    ...(failedAttempt === undefined
      ? {}
      : {
          failure: {
            failureClass: "attempt",
            cause: failedAttempt.reason ?? "Attempt failed",
            retryDisposition: "unknown",
            exactSourceIdentity: failedAttempt.id,
            partial: true,
          },
        }),
    retryDisposition: failedAttempt ? "unknown" : "not-applicable",
    wip:
      activeWorkspace?.cleanedAt !== undefined
        ? {
            state: "safe",
            workspaceId: activeWorkspace.id,
            ...(activeWorkspace.checkpointDigest === undefined
              ? {}
              : { checkpointIdentity: activeWorkspace.checkpointDigest }),
            ...(activeWorkspace.checkpointedAt === undefined
              ? {}
              : { checkpointedAt: activeWorkspace.checkpointedAt }),
            cleanupAllowed: false,
            reason: "Collected Workspace checkpoint was cleaned after terminal trusted merge.",
          }
        : activeWorkspace?.checkpointDigest !== undefined
          ? {
              state: "safe",
              workspaceId: activeWorkspace.id,
              checkpointIdentity: activeWorkspace.checkpointDigest,
              ...(activeWorkspace.checkpointedAt === undefined
                ? {}
                : { checkpointedAt: activeWorkspace.checkpointedAt }),
              cleanupAllowed:
                factoryRun.status === "completed" &&
                activeWorkspace.checkpointCollectedAt !== undefined,
              reason:
                activeWorkspace.checkpointCollectedAt === undefined
                  ? "Workspace WIP is content-attested on persistent storage and retained for recovery."
                  : factoryRun.status === "completed"
                    ? "Workspace checkpoint is durably collected and terminal cleanup is permitted."
                    : "Workspace checkpoint is durably collected but retained until terminal trusted merge.",
            }
          : {
              state: "unknown",
              ...(activeAttempt === undefined ? {} : { workspaceId: activeAttempt.workspaceId }),
              cleanupAllowed: false,
              reason:
                "Workspace checkpoint content has not yet been attested by the canonical I1 checkpoint contract.",
            },
    liveExecutorPresent,
    historicalAvailable: true,
    continuation: {
      factoryRunId: factoryRun.id,
      reattachable: liveExecutorPresent,
      continuationKey: `factory-run:${factoryRun.id}`,
      reason: liveExecutorPresent ? "Live execution is present" : "Durable historical state only",
    },
  };
}

export function renderApprovedLiveFactory(
  hierarchy: LiveHierarchy,
  factoryRunId: string,
  url: URL,
): string {
  const detail = factoryDetail(hierarchy, factoryRunId);
  return document(
    `FactoryRun ${factoryRunId}`,
    renderFactoryRun(
      detail,
      url.searchParams.get("state") ?? "ready",
      url.searchParams.get("tab") ?? "overview",
      hierarchy.project.name,
    ),
  );
}

function gateAction(
  status: I1ChangeSetReviewReadModel["status"],
  conditions: I1ChangeSetReviewReadModel["mergeGate"]["conditions"],
): I1ProtectedActionAvailability {
  if (status === "merged")
    return { available: false, reasonCode: "terminal", summary: "ChangeSet is already merged" };
  const failed = conditions.find((condition) => !condition.satisfied);
  if (!failed)
    return {
      available: true,
      reasonCode: "ready",
      summary: "All protected conditions are satisfied",
    };
  const reasonCode = (
    [
      "candidate-state",
      "review",
      "evidence",
      "publication",
      "policy",
      "authority",
      "target-revision",
    ] as const
  ).includes(failed.code as never)
    ? (failed.code as I1ProtectedActionAvailability["reasonCode"])
    : "candidate-state";
  return { available: false, reasonCode, summary: failed.summary };
}

export function reviewDetail(
  hierarchy: LiveHierarchy,
  changeSetId: string,
): {
  readonly model: I1ChangeSetReviewReadModel;
  readonly factoryRunId?: FactoryRunId;
  readonly diff: string;
} {
  const changeSet = hierarchy.changeSets.find((item) => item.id === changeSetId);
  if (!changeSet) throw new Error(`Unknown ChangeSet ${changeSetId}`);
  const reviews = hierarchy.reviews.filter((review) => review.changeSetId === changeSet.id);
  const evidence = hierarchy.verificationEvidence.filter(
    (item) =>
      item.changeSetId === changeSet.id && item.candidateDigest === changeSet.candidateDigest,
  );
  const findings = hierarchy.reviewFindings.filter(
    (item) =>
      item.changeSetId === changeSet.id && item.candidateDigest === changeSet.candidateDigest,
  );
  const blockingFindingsClear = !findings.some(
    (finding) => finding.severity === "blocking" && !finding.resolved,
  );
  const reviewSatisfied =
    blockingFindingsClear &&
    reviews.some((review) => review.status === "submitted" && review.disposition === "approved");
  const latestEvidence = new Map<string, LiveVerificationEvidence>();
  for (const item of evidence) {
    const key = `${item.source}\u0000${item.name}`;
    const previous = latestEvidence.get(key);
    if (!previous || Date.parse(item.observedAt) >= Date.parse(previous.observedAt)) {
      latestEvidence.set(key, item);
    }
  }
  const requiredEvidence = [...latestEvidence.values()].filter((item) => item.required);
  const evidenceSatisfied =
    requiredEvidence.length > 0 && requiredEvidence.every((item) => item.state === "passed");
  const providerEvidence = [...latestEvidence.values()].filter((item) =>
    item.source.startsWith("repository-required-checks:"),
  );
  const providerPolicy = providerEvidence.find((item) => item.name === "policy");
  const providerChecksSatisfied =
    providerPolicy?.state === "passed" &&
    providerEvidence.filter((item) => item.required).every((item) => item.state === "passed");
  const publicationSatisfied =
    changeSet.repositoryKey !== undefined &&
    changeSet.publicationReference !== undefined &&
    changeSet.targetReference !== undefined &&
    changeSet.targetRevision !== undefined;
  const targetHeadSatisfied =
    changeSet.targetReference !== undefined && changeSet.targetRevision !== undefined;
  const candidateStateSatisfied = ["ready-to-merge", "merged"].includes(changeSet.status);
  const conditions = [
    {
      code: "candidate-state",
      satisfied: candidateStateSatisfied,
      summary: "Exact ChangeSet candidate is ready to merge",
    },
    { code: "review", satisfied: reviewSatisfied, summary: "Independent Review is approved" },
    {
      code: "evidence",
      satisfied: evidenceSatisfied,
      summary: "Required VerificationEvidence is current and passed for this candidate",
    },
    {
      code: "provider-checks",
      satisfied: providerChecksSatisfied,
      summary:
        "Repository-required provider checks are current and passed for the publication commit",
    },
    {
      code: "publication",
      satisfied: publicationSatisfied,
      summary: "Trusted publication identity is durably bound to this ChangeSet candidate",
    },
    {
      code: "policy",
      satisfied: reviewSatisfied,
      summary: "Default full-chain policy authorizes trusted merge without another owner approval",
    },
    {
      code: "authority",
      satisfied: publicationSatisfied,
      summary:
        "Project-scoped trusted repository authority is represented by durable publication identity",
    },
    {
      code: "target-revision",
      satisfied: targetHeadSatisfied,
      summary: "Expected target revision identity is durably recorded",
    },
  ] as const;
  const model: I1ChangeSetReviewReadModel = {
    id: changeSet.id,
    revision: changeSet.revision,
    projectId: changeSet.projectId,
    taskId: changeSet.taskId,
    status: changeSet.status,
    baseIdentity: changeSet.baseIdentity,
    candidateDigest: changeSet.candidateDigest,
    candidate: changeSet.candidateManifest,
    reviews: reviews.map((review) => ({
      id: review.id,
      reviewerPrincipalId: review.reviewerPrincipalId,
      ...(review.reviewerAgentRunId === undefined
        ? {}
        : { reviewerAgentRunId: review.reviewerAgentRunId }),
      status: review.status,
      ...(review.disposition === undefined ? {} : { disposition: review.disposition }),
    })),
    findings: findings.map((finding) => ({
      id: finding.id,
      severity: finding.severity,
      summary: finding.summary,
      ...(finding.source === undefined ? {} : { source: finding.source }),
      resolved: finding.resolved,
    })),
    evidence: evidence.map((item) => ({
      id: item.id,
      name: item.name,
      state: item.state,
      source: item.source,
      observedAt: item.observedAt,
      candidateDigest: item.candidateDigest,
      required: item.required,
      ...(item.details === undefined ? {} : { details: item.details }),
    })),
    expectedTarget: {
      reference: changeSet.targetReference ?? hierarchy.project.repositoryUrl,
      revision: changeSet.targetRevision ?? changeSet.baseIdentity,
    },
    mergeGate: { conditions, action: gateAction(changeSet.status, conditions) },
  };
  const attempt = hierarchy.attempts.find((item) => item.id === changeSet.producerAttemptId);
  const agent = attempt
    ? hierarchy.agentRuns.find((item) => item.id === attempt.agentRunId)
    : undefined;
  return {
    model,
    ...(agent === undefined ? {} : { factoryRunId: agent.factoryRunId }),
    diff: changeSet.diff,
  };
}

export function renderApprovedLiveReview(
  hierarchy: LiveHierarchy,
  changeSetId: string,
  url: URL,
): string {
  const detail = reviewDetail(hierarchy, changeSetId);
  return document(
    `ChangeSet ${changeSetId}`,
    renderReviewPage(
      detail.model,
      url.searchParams.get("state") ?? "ready",
      url.searchParams.get("tab") ?? (detail.model.status === "merged" ? "outcome" : "review"),
      url.searchParams.get("file") ?? undefined,
      url.searchParams.get("finding") ?? undefined,
      {
        projectName: hierarchy.project.name,
        ...(detail.factoryRunId === undefined ? {} : { factoryRunId: detail.factoryRunId }),
        diff: detail.diff,
      },
    ),
  );
}
