import type { I1FactoryRunDetailReadModel, I1WorkProjection } from "@awp/contracts";
import { escapeHtml } from "./html.js";
import { renderLifecycleShell, renderStateBanner, protectedActionDisabled } from "./shell.js";
import { withQuery } from "./router.js";

export interface FailureProjection {
  readonly failureClass?: string | undefined;
  readonly failureCause?: string | undefined;
  readonly retryDisposition?: string | undefined;
  readonly exactSource?: string | undefined;
}

export function renderFailureFacts(failure: FailureProjection): string {
  const field = (label: string, value: string | undefined): string =>
    `<span><b>${label}:</b> ${escapeHtml(value ?? "Unknown / not provided")}</span>`;
  return `<div class="failure-facts" data-failure-projection>${field("Class", failure.failureClass)}${field("Cause", failure.failureCause)}${field("Retry", failure.retryDisposition)}${field("Source", failure.exactSource)}</div>`;
}

const factoryTabs = [
  ["overview", "Overview"],
  ["tasks", "Tasks"],
  ["agent-runs", "AgentRuns"],
  ["timeline", "Timeline"],
  ["change-set", "ChangeSet"],
] as const;

type FactoryUiState = "active" | "waiting" | "recovery";

const lifecycleState = (model: I1FactoryRunDetailReadModel): FactoryUiState => {
  if (["failed", "retryable", "resolving", "cancelling", "cancelled"].includes(model.status))
    return "recovery";
  if (["waiting", "blocked"].includes(model.status)) return "waiting";
  return "active";
};

const toneForWork = (work: I1WorkProjection): string => {
  if (work.status === "completed") return "green";
  if (["executing", "review", "correction"].includes(work.status)) return "purple";
  if (work.status === "failed") return "red";
  if (["waiting", "blocked"].includes(work.status)) return "amber";
  return "blue";
};

const labelForWork = (work: I1WorkProjection): string => {
  if (work.status === "completed") return "Completed";
  if (work.status === "executing") return "Running";
  if (work.status === "failed") return "Attempt failed";
  if (work.status === "waiting") return "Paused";
  if (work.status === "blocked") return "Blocked";
  if (work.status === "queued") return "Queued";
  return work.status;
};

const shortTime = (value: string | undefined): string =>
  value ? (value.match(/T(\d{2}:\d{2})/)?.[1] ?? value) : "—";

const renderRunHeader = (
  model: I1FactoryRunDetailReadModel,
  state: FactoryUiState,
  surfaceState: string,
  tab: string,
): string => {
  const recovery = state === "recovery";
  const waiting = state === "waiting";
  const disabled = protectedActionDisabled(surfaceState);
  const title = model.reason ?? "FactoryRun orchestration";
  return `<div class="run-head"><div><div class="run-title">${title} <span class="id-badge">${escapeHtml(model.id)}</span></div><div class="context-strip"><span class="pill ${recovery ? "red" : waiting ? "amber" : "green"}">${recovery ? "● Failure / recovery" : waiting ? "◷ Waiting" : "◉ Running"}</span><span>Stage: ${escapeHtml(model.stage)}</span><span class="divider"></span><span>Revision: ${model.revision}</span><span class="divider"></span><span>Last progress: ${escapeHtml(shortTime(model.lastProgressAt))}</span></div></div><div class="actions"><a class="btn purple" href="/projects/${encodeURIComponent(model.projectId)}?tab=plans">↗ View Plan</a><button class="btn" type="button" aria-label="More FactoryRun actions">•••</button><button class="btn danger" type="button" data-command="cancel-run"${disabled ? ' disabled aria-disabled="true"' : ""}>▣ Cancel Run</button></div></div><nav class="tabs" aria-label="FactoryRun tabs">${factoryTabs.map(([key, label]) => `<a class="tab${tab === key ? " active" : ""}" href="${withQuery(`/factory-runs/${model.id}`, { tab: key === "overview" ? undefined : key, state: undefined })}"${tab === key ? ' aria-current="page"' : ""}>${label}</a>`).join("")}</nav>`;
};

const renderTasks = (model: I1FactoryRunDetailReadModel): string =>
  `<section class="panel task-panel"><div class="panel-head"><h2>1. Task Progress &amp; Topology</h2><p>Authoritative Task state; dispatch legality remains server-side.</p></div><div class="panel-body table-pad"><div class="table-wrap"><table class="table"><caption class="sr-only">FactoryRun Task topology</caption><thead><tr><th>Task</th><th>State</th><th>Stage</th><th>AgentRun</th><th>Last progress</th><th>Next</th></tr></thead><tbody>${model.work.map((work) => `<tr><td><a class="task-cell" href="/projects/${encodeURIComponent(model.projectId)}/queue?task=${encodeURIComponent(work.taskId)}"><span class="state-icon ${toneForWork(work)}" aria-hidden="true">${work.status === "completed" ? "✓" : work.status === "failed" ? "!" : "›"}</span><span><strong class="task-title">${escapeHtml(work.title)}</strong><span class="task-sub">${escapeHtml(work.attention.summary)}</span></span></a></td><td><span class="pill ${toneForWork(work)}">${escapeHtml(labelForWork(work))}</span></td><td>${escapeHtml(work.stage)}</td><td>${work.currentAgentRunId ? `<a class="link" href="${withQuery(`/factory-runs/${model.id}`, { tab: "agent-runs", task: work.currentAgentRunId })}">${escapeHtml(work.currentAgentRunId)}</a>` : "—"}</td><td>${escapeHtml(shortTime(work.lastProgressAt))}</td><td>${escapeHtml(work.nextAction.summary)}</td></tr>`).join("")}</tbody></table></div></div></section>`;

const renderAgents = (model: I1FactoryRunDetailReadModel): string =>
  `<section class="panel agents-panel"><div class="panel-head"><h2>2. Active AgentRuns</h2><p>Logical assignments with immutable Attempt provenance.</p></div><div class="panel-body">${
    model.agentRuns.length === 0
      ? '<div class="empty-note">No AgentRun recorded for this FactoryRun.</div>'
      : model.agentRuns
          .map((agent) => {
            const current = agent.currentAttemptId
              ? agent.attempts.find((attempt) => attempt.id === agent.currentAttemptId)
              : agent.attempts.at(-1);
            return `<article class="agent-card"><div class="agent-top"><div><a class="link" href="/factory-runs/${model.id}?tab=agent-runs&task=${agent.id}">${escapeHtml(agent.id)}</a> <span class="pill purple">${escapeHtml(agent.status)}</span></div><span class="subtle">${current ? `Attempt ${agent.attempts.findIndex((attempt) => attempt.id === current.id) + 1} / ${agent.attempts.length}` : "No Attempt"}</span></div><div class="agent-title">${escapeHtml(model.work.find((work) => work.taskId === agent.taskId)?.title ?? agent.taskId)}</div><div class="agent-grid"><div class="kv"><b>Model</b><span>${escapeHtml(current?.model ?? "Unknown")}</span></div><div class="kv"><b>Account</b><span>${escapeHtml(current?.accountId ?? "Unknown")}</span></div><div class="kv"><b>Workspace</b><span>${escapeHtml(current?.workspaceId ?? "Unknown")}</span></div><div class="kv"><b>Selection</b><span>${escapeHtml(current?.selection.reason ?? "Unknown")}</span></div><div class="kv"><b>Attempt state</b><span>${escapeHtml(current?.status ?? "Unknown")}</span></div><div class="kv"><b>Checkpoint</b><span class="saved">● ${escapeHtml(model.wip.checkpointIdentity ?? "not recorded")}</span></div></div></article>`;
          })
          .join("")
  }</div></section>`;

const renderTimeline = (model: I1FactoryRunDetailReadModel): string => {
  const events = [
    model.lastProgressAt
      ? {
          time: shortTime(model.lastProgressAt),
          title: "Latest authoritative progress",
          detail: model.stage,
          tone: "purple",
        }
      : undefined,
    model.wait
      ? {
          time: shortTime(model.wait.since),
          title: "Waiting",
          detail: model.wait.summary,
          tone: "amber",
        }
      : undefined,
    model.failure
      ? {
          time: shortTime(model.failure.occurredAt),
          title: "Attempt failure recorded",
          detail: model.failure.cause,
          tone: "red",
        }
      : undefined,
    model.wip.checkpointIdentity
      ? {
          time: shortTime(model.wip.checkpointedAt),
          title: "WIP checkpoint recorded",
          detail: model.wip.checkpointIdentity,
          tone: "green",
        }
      : undefined,
  ].filter(
    (event): event is { time: string; title: string; detail: string; tone: string } =>
      event !== undefined,
  );
  return `<section class="panel"><div class="panel-head"><h2>3. Latest Activity</h2><p>Meaningful authoritative projection facts before raw provider logs.</p></div><div class="panel-body"><div class="timeline">${events.map((event) => `<div class="event ${event.tone}"><span class="event-time">${escapeHtml(event.time)}</span><div class="event-title">${escapeHtml(event.title)}</div><div class="event-sub">${escapeHtml(event.detail)}</div></div>`).join("")}</div></div></section>`;
};

const renderSafety = (model: I1FactoryRunDetailReadModel): string =>
  `<section class="panel safety-panel"><div class="panel-head"><h2>4. WIP / Checkpoint Safety</h2><p>Durable source state remains distinct from disposable compute.</p></div><div class="panel-body"><div class="callout${model.wip.state === "safe" ? "" : " red"}"><div class="callout-title">${model.wip.state === "safe" ? "✓ WIP is safely persisted" : "WIP safety is not confirmed"}</div><div class="callout-sub">${escapeHtml(model.wip.reason)}</div></div><div class="detail-list"><div class="detail-row"><b>Latest checkpoint</b><a class="link" href="#checkpoint">${escapeHtml(model.wip.checkpointIdentity ?? "Unknown")}</a></div><div class="detail-row"><b>Files preserved</b><span>${model.wip.preservedFileCount ?? "Unknown"}${model.wip.preservedFileCount !== undefined ? " modified · source + test state" : ""}</span></div><div class="detail-row"><b>Cleanup allowed</b><span>${model.wip.cleanupAllowed ? "Yes" : "No — recoverable or active WIP remains"}</span></div><div class="detail-row"><b>Compute</b><span>${model.liveExecutorPresent ? "Live executor present" : "No live executor; durable history retained"}</span></div></div><div class="callout purple"><div class="callout-title">Your work is ${model.wip.state === "safe" ? "safe" : "not yet proven safe"}</div><div class="callout-sub">Retry creates a new Attempt and resumes from preserved WIP. Attempt 1 remains immutable history.</div></div></div></section>`;

const renderPendingChange = (): string =>
  `<section class="right-stack"><div class="panel change-panel"><div class="panel-head"><h2>5. ChangeSet / Review</h2><p>The exact candidate appears here after durable collection.</p></div><div class="panel-body"><div class="pending-change"><div><span class="change-icon" aria-hidden="true">⌘</span><div><strong>ChangeSet (pending)</strong><small>Created only from durably collected work</small></div></div><button class="btn disabled" type="button" disabled aria-disabled="true">View ChangeSet</button></div></div></div></section>`;

const renderCurrentNext = (model: I1FactoryRunDetailReadModel): string => {
  const current =
    model.work.find((work) =>
      ["executing", "waiting", "failed", "review", "correction"].includes(work.status),
    ) ?? model.work[0];
  const next = model.work.find((work) => ["ready", "queued", "blocked"].includes(work.status));
  const attention = model.work.find(
    (work) => work.attention.humanActionRequired || work.attention.kind !== "none",
  );
  return `<section class="panel"><div class="panel-head"><h2>Current / Next / Attention</h2></div><div class="current-next"><div class="cn"><div class="cn-label">Current</div><div class="cn-title">${escapeHtml(current?.title ?? "None")}</div><div class="cn-sub">${escapeHtml(current?.stage ?? model.stage)}</div></div><div class="cn"><div class="cn-label blue">Next</div><div class="cn-title">${escapeHtml(next?.title ?? "No queued Task")}</div><div class="cn-sub">${escapeHtml(next?.nextAction.summary ?? "No next action")}</div></div><div class="cn"><div class="cn-label ${attention ? "amber" : "green"}">Attention</div><div class="cn-title">${escapeHtml(attention?.attention.summary ?? "None")}</div><div class="cn-sub">${attention?.attention.humanActionRequired ? "Owner action required" : "AWP owns recovery"}</div></div></div></section>`;
};

const renderRecoveryAttempts = (model: I1FactoryRunDetailReadModel, disabled: boolean): string =>
  `<section class="panel"><div class="panel-head"><h2>2. Recovery &amp; Attempts</h2><p>Immutable Attempt history for the active logical AgentRun.</p></div><div class="panel-body">${model.agentRuns.flatMap((agent) => agent.attempts.map((attempt, index) => `<article class="attempt-row"><div class="attempt-top"><div><a class="link" href="#${escapeHtml(attempt.id)}">${escapeHtml(agent.id)} · Attempt ${index + 1}</a> <span class="pill ${attempt.id === agent.currentAttemptId ? "amber" : "red"}">${attempt.id === agent.currentAttemptId ? "Ready to retry" : escapeHtml(attempt.status)}</span></div></div><div class="attempt-meta"><span>Model: ${escapeHtml(attempt.model ?? "Unknown")}</span><span>Account: ${escapeHtml(attempt.accountId ?? "Unknown")}</span><span>Workspace: ${escapeHtml(attempt.workspaceId)}</span><span>Selection: ${escapeHtml(attempt.selection.reason)}</span></div></article>`)).join("")}<div class="mini-actions"><button class="btn primary" type="button" data-command="retry-run"${disabled ? ' disabled aria-disabled="true"' : ""}>↻ Retry as new Attempt</button></div></div></section>`;

const renderLoadingFactory = (): string =>
  `<div class="factory-loading" aria-busy="true" aria-label="Loading FactoryRun"><div class="grid-top"><section class="panel skeleton-panel"><div class="panel-head"><h2>1. Task Progress &amp; Topology</h2></div><div class="skeleton-lines"><span></span><span></span><span></span></div></section><section class="panel skeleton-panel"><div class="panel-head"><h2>2. Active AgentRuns</h2></div><div class="skeleton-lines"><span></span><span></span></div></section></div></div>`;

export function renderFactoryRun(
  model: I1FactoryRunDetailReadModel,
  surfaceState = "ready",
  tab = "overview",
  projectName = "Project",
): string {
  const validTab = factoryTabs.some(([key]) => key === tab) ? tab : "overview";
  const effectiveSurface = ["ready", "loading", "error", "stale", "permission"].includes(
    surfaceState,
  )
    ? surfaceState
    : "ready";
  const state = lifecycleState(model);
  const recovery = state === "recovery";
  const waiting = state === "waiting";
  const disabled = protectedActionDisabled(effectiveSurface);
  let content = renderRunHeader(model, state, effectiveSurface, validTab);
  if (effectiveSurface === "loading") {
    content += renderLoadingFactory();
    return renderLifecycleShell({
      active: "factory-runs",
      breadcrumbs: ["AWP", projectName, "FactoryRun"],
      content,
      project: { id: model.projectId, name: projectName },
      latestFactoryRunId: model.id,
    });
  }
  content += renderStateBanner(effectiveSurface, "factory");
  if (!model.liveExecutorPresent && model.historicalAvailable)
    content += `<div class="state-banner warning" role="status"><strong>Historical durable record</strong><span>The live executor/resource projection is gone, but this FactoryRun, Attempt history, failure evidence, and preserved-work facts remain queryable.</span></div>`;
  if (recovery) {
    content += `<div class="recovery-band" role="status"><div><h3>Attempt 1 failed — work preserved</h3><p>${escapeHtml(model.reason ?? model.failure?.cause ?? "Execution failed; authoritative failure detail is partial.")}</p>${renderFailureFacts({ failureClass: model.failure?.failureClass, failureCause: model.failure?.cause, retryDisposition: model.failure?.retryDisposition === "automatic" ? "retryable-new-attempt" : model.failure?.retryDisposition, exactSource: model.failure?.exactSourceIdentity })}</div><span class="pill ${model.wip.state === "safe" ? "green" : "amber"}">${model.wip.state === "safe" ? "✓ WIP safe" : "WIP unknown"}</span></div><div class="grid-top">${renderTasks(model)}${renderRecoveryAttempts(model, disabled)}</div><div class="grid-bottom">${renderTimeline(model)}${renderSafety(model)}${renderCurrentNext(model)}</div>`;
  } else if (waiting) {
    content += `<div class="waiting-band" role="status"><div><h3>FactoryRun is waiting — not failed, not complete</h3><p>${escapeHtml(model.wait?.summary ?? model.reason ?? "Waiting reason is not available")}</p></div><span class="pill ${model.wip.state === "safe" ? "green" : "amber"}">${model.wip.state === "safe" ? "✓ WIP safe" : "WIP unknown"}</span></div><div class="grid-top">${renderTasks(model)}${renderAgents(model)}</div><div class="grid-bottom">${renderTimeline(model)}${renderSafety(model)}${renderCurrentNext(model)}</div>`;
  } else {
    content += `<div class="grid-top">${renderTasks(model)}${renderAgents(model)}</div><div class="grid-bottom">${renderTimeline(model)}${renderSafety(model)}${renderPendingChange()}${renderCurrentNext(model)}</div>`;
  }
  return renderLifecycleShell({
    active: "factory-runs",
    breadcrumbs: ["AWP", projectName, "FactoryRun"],
    content,
    project: { id: model.projectId, name: projectName },
    latestFactoryRunId: model.id,
  });
}
