import { selectRequestDeliveryLineage } from "@overdeck/report-contract";
import type {
  ExecutionReportSection,
  ExecutionRunRecord,
  ObservabilityReportQuery,
  ReportCountBreakdown,
  ReportCoverageGap,
  ReportCoverageStatus,
  RequestStoryV1,
} from "@overdeck/report-contract";
import type { FactoryRunView } from "../adapters/factory";
import type { RequestRow } from "../requests/requests-store";

const EXECUTION_RECORD_LIMIT = 100;

export interface ExecutionReportInput {
  requests?: RequestRow[];
  requestStories?: RequestStoryV1[];
  factoryRuns?: FactoryRunView[];
}

function timestamp(value: string | null | undefined): number | undefined {
  if (!value) return undefined;
  const parsed = Date.parse(value);
  return Number.isFinite(parsed) ? parsed : undefined;
}

function overlaps(start: string | null | undefined, end: string | null | undefined, fromMs: number, toMs: number): boolean {
  const startMs = timestamp(start);
  const endMs = timestamp(end) ?? startMs;
  return startMs !== undefined && startMs <= toMs && (endMs ?? startMs) >= fromMs;
}

function requestInRange(request: RequestRow, fromMs: number, toMs: number): boolean {
  if (overlaps(request.asked_at, request.updated_at, fromMs, toMs)) return true;
  return [...(request.transition_trail ?? []), ...(request.receipt_trail ?? [])]
    .some((entry) => {
      const at = timestamp(entry.at);
      return at !== undefined && at >= fromMs && at <= toMs;
    });
}

function runProject(run: FactoryRunView): string | undefined {
  if (run.repoName?.trim()) return run.repoName.trim();
  const normalized = run.repo?.replace(/\\/g, "/").replace(/\/$/, "");
  return normalized?.split("/").at(-1) || undefined;
}

function matchesProject(project: string | undefined, query: ObservabilityReportQuery): boolean {
  return !query.projects?.length || (project !== undefined && query.projects.includes(project));
}

function matchesSearch(values: Array<string | null | undefined>, query: ObservabilityReportQuery): boolean {
  if (!query.q) return true;
  const needle = query.q.toLocaleLowerCase();
  return values.some((value) => value?.toLocaleLowerCase().includes(needle));
}

function runOutcome(status: string | null): ExecutionRunRecord["outcome"] {
  const normalized = status?.trim().toLocaleLowerCase();
  if (["success", "succeeded", "completed", "passed"].includes(normalized ?? "")) return "succeeded";
  if (["fail", "failed", "error"].includes(normalized ?? "")) return "failed";
  if (["running", "in_flight", "queued", "pending"].includes(normalized ?? "")) return "running";
  if (["cancelled", "canceled", "aborted"].includes(normalized ?? "")) return "cancelled";
  return "unknown";
}

function durationMs(start: string | null | undefined, end: string | null | undefined): number | null {
  const startMs = timestamp(start);
  const endMs = timestamp(end);
  return startMs !== undefined && endMs !== undefined && endMs >= startMs ? endMs - startMs : null;
}

function distinctRecorded(values: Array<string | null | undefined>): string[] {
  return [...new Set(values.map((value) => value?.trim()).filter((value): value is string => Boolean(value)))].sort();
}

function latestStoryLink(story: RequestStoryV1 | undefined, kind: RequestStoryV1["links"][number]["kind"]) {
  return story?.links.filter((link) => link.kind === kind)
    .sort((left, right) => right.occurredAt.localeCompare(left.occurredAt))[0];
}

function safeInternalOwnerUrl(value: string | undefined): string | undefined {
  return value?.startsWith("/") && !value.startsWith("//") && !value.includes("\\") ? value : undefined;
}

function summarizeRun(run: FactoryRunView): ExecutionRunRecord {
  const attempts = run.attempts.length;
  const retries = run.phases.reduce((total, phase) => total + Math.max(0, phase.retries ?? 0), 0);
  const failedGates = run.gates.filter((gate) => gate.passed === false).length;
  const toolCalls = run.attempts.reduce((total, attempt) => total + attempt.toolCalls.length, 0);
  const changedFiles = new Set(run.diffs.flatMap((diff) => diff.files.map((file) => file.path))).size;
  const insertions = run.diffs.some((diff) => diff.insertions === null)
    ? null
    : run.diffs.reduce((total, diff) => total + (diff.insertions ?? 0), 0);
  const deletions = run.diffs.some((diff) => diff.deletions === null)
    ? null
    : run.diffs.reduce((total, diff) => total + (diff.deletions ?? 0), 0);
  const gatesByPhase = new Map<string, { passed: number; failed: number }>();
  for (const gate of run.gates) {
    if (!gate.phaseId) continue;
    const counts = gatesByPhase.get(gate.phaseId) ?? { passed: 0, failed: 0 };
    if (gate.passed === true) counts.passed += 1;
    if (gate.passed === false) counts.failed += 1;
    gatesByPhase.set(gate.phaseId, counts);
  }
  const attemptsByPhase = new Map<string, typeof run.attempts>();
  for (const attempt of run.attempts) {
    if (!attempt.phaseId) continue;
    const rows = attemptsByPhase.get(attempt.phaseId) ?? [];
    rows.push(attempt);
    attemptsByPhase.set(attempt.phaseId, rows);
  }
  return {
    id: run.adwId,
    label: run.runSlug ?? run.adwName ?? "Factory run",
    ...(runProject(run) ? { project: runProject(run) } : {}),
    status: run.status ?? "not recorded",
    outcome: runOutcome(run.status),
    ...(run.startedAt ? { startedAt: run.startedAt } : {}),
    ...(run.endedAt ? { endedAt: run.endedAt } : {}),
    durationMs: durationMs(run.startedAt, run.endedAt),
    attempts,
    retries,
    failedGates,
    toolCalls,
    changedFiles,
    insertions,
    deletions,
    models: distinctRecorded(run.attempts.map((attempt) => attempt.model)),
    accounts: distinctRecorded(run.attempts.map((attempt) => attempt.account)),
    sessions: distinctRecorded(run.attempts.map((attempt) => attempt.sessionId)),
    hosts: distinctRecorded(run.attempts.map((attempt) => attempt.host)),
    phases: run.phases.map((phase) => {
      const phaseAttempts = attemptsByPhase.get(phase.phaseId) ?? [];
      const gateCounts = gatesByPhase.get(phase.phaseId) ?? { passed: 0, failed: 0 };
      return {
        label: phase.name ?? phase.kind ?? "Recorded phase",
        status: phase.status ?? "not recorded",
        durationMs: phase.durationMs,
        attempts: phase.attempt,
        retries: phase.retries,
        gatePassed: gateCounts.passed,
        gateFailed: gateCounts.failed,
        toolCalls: phaseAttempts.reduce((total, attempt) => total + attempt.toolCalls.length, 0),
      };
    }),
    href: `/factory/${encodeURIComponent(run.adwId)}`,
  };
}

function countBreakdown(values: string[], labels: Record<string, string> = {}): ReportCountBreakdown[] {
  const counts = new Map<string, number>();
  for (const value of values) counts.set(value, (counts.get(value) ?? 0) + 1);
  return [...counts].map(([key, count]) => ({ key, label: labels[key] ?? key, count }))
    .sort((left, right) => right.count - left.count || left.label.localeCompare(right.label));
}

export function buildExecutionReportSection(
  query: ObservabilityReportQuery,
  input: ExecutionReportInput,
): ExecutionReportSection {
  const fromMs = Date.parse(query.from);
  const toMs = Date.parse(query.to);
  const allRequests = input.requests;
  const allRuns = input.factoryRuns;
  const filteredRequests = (allRequests ?? []).filter((request) =>
    requestInRange(request, fromMs, toMs)
    && matchesProject(request.project, query)
    && matchesSearch([request.title, request.project, request.worker, request.detail], query))
    .sort((left, right) => (timestamp(right.updated_at) ?? 0) - (timestamp(left.updated_at) ?? 0));
  const filteredRuns = (allRuns ?? []).filter((run) =>
    overlaps(run.startedAt, run.endedAt, fromMs, toMs)
    && matchesProject(runProject(run), query)
    && matchesSearch([run.runSlug, run.adwName, run.request, runProject(run)], query))
    .sort((left, right) => (timestamp(right.startedAt) ?? 0) - (timestamp(left.startedAt) ?? 0));
  const runById = new Map((allRuns ?? []).map((run) => [run.adwId, run]));
  const storyById = new Map((input.requestStories ?? []).map((story) => [story.requestId, story]));
  const gaps: ReportCoverageGap[] = [];

  if (!allRequests) gaps.push({ domain: "execution", reason: "The request registry is unavailable for this report." });
  if (allRequests && !input.requestStories) gaps.push({ domain: "execution", reason: "Exact request evidence is unavailable for this report." });
  if (!allRuns) gaps.push({ domain: "execution", reason: "Factory run records are unavailable for this report." });

  const requestRecords = filteredRequests.map((request) => {
    const story = storyById.get(request.id);
    const runLink = latestStoryLink(story, "factory_run");
    const linkedId = runLink?.targetId;
    const linkedRun = linkedId ? runById.get(linkedId) : undefined;
    const correlation = linkedId ? (linkedRun ? "linked" as const : "missing-run" as const) : "unlinked" as const;
    const correlationReason = correlation === "unlinked"
      ? "No factory run was explicitly linked to this request."
      : correlation === "missing-run"
        ? "The explicitly linked factory run is not retained in the current factory registry."
        : undefined;
    if (correlationReason) gaps.push({ domain: "execution", sourceId: "request-evidence-links", reason: `${request.title}: ${correlationReason}` });
    const delivery = selectRequestDeliveryLineage(story?.links ?? []);
    const landed = delivery.commit;
    const deployed = delivery.deployment;
    const proof = delivery.proof;
    const proofHref = safeInternalOwnerUrl(proof?.ownerUrl);
    return {
      id: request.id,
      title: request.title,
      project: request.project,
      state: request.state,
      askedAt: request.asked_at,
      updatedAt: request.updated_at,
      ...(request.worker ? { worker: request.worker } : {}),
      ...(proofHref ? { proofHref } : {}),
      requestHref: `/requests?request=${encodeURIComponent(request.id)}`,
      correlation,
      ...(correlationReason ? { correlationReason } : {}),
      ...(linkedId ? { factoryRunId: linkedId, factoryRunHref: safeInternalOwnerUrl(runLink?.ownerUrl) ?? `/factory/${encodeURIComponent(linkedId)}` } : {}),
      delivery: {
        ...(landed ? { landedAt: landed.occurredAt } : {}),
        ...(deployed ? { deployedAt: deployed.occurredAt } : {}),
        proofRecorded: Boolean(proof),
      },
    };
  });
  const runRecords = filteredRuns.map(summarizeRun);
  const truncated = requestRecords.length > EXECUTION_RECORD_LIMIT || runRecords.length > EXECUTION_RECORD_LIMIT;
  const visibleRequests = requestRecords.slice(0, EXECUTION_RECORD_LIMIT);
  const visibleRuns = runRecords.slice(0, EXECUTION_RECORD_LIMIT);
  if (truncated) gaps.push({ domain: "execution", reason: `Execution records are limited to the newest ${EXECUTION_RECORD_LIMIT} matching requests and runs.` });

  let status: ReportCoverageStatus = !allRequests && !allRuns ? "unavailable" : !allRequests || !allRuns ? "partial" : "complete";
  if (status === "complete" && gaps.length > 0) status = "partial";
  const failedRuns = filteredRuns.filter((run) => runOutcome(run.status) === "failed");
  const failedPhaseLabels = filteredRuns.flatMap((run) => run.phases
    .filter((phase) => ["fail", "failed", "error"].includes(phase.status?.toLocaleLowerCase() ?? ""))
    .map((phase) => phase.name ?? phase.kind ?? "Recorded phase"));

  return {
    kind: "execution",
    status,
    title: "Work execution and delivery",
    metrics: [
      { id: "requests", label: "Requests in window", value: allRequests ? filteredRequests.length : null, unit: "count", coverage: allRequests ? "complete" : "unavailable" },
      { id: "shipped-requests", label: "Shipped requests", value: allRequests ? filteredRequests.filter((request) => request.state === "shipped").length : null, unit: "count", coverage: allRequests ? "complete" : "unavailable" },
      { id: "factory-runs", label: "Factory runs", value: allRuns ? filteredRuns.length : null, unit: "count", coverage: allRuns ? "complete" : "unavailable" },
      { id: "failed-runs", label: "Failed runs", value: allRuns ? failedRuns.length : null, unit: "count", coverage: allRuns ? "complete" : "unavailable" },
    ],
    requestStates: countBreakdown(filteredRequests.map((request) => request.state), {
      asked: "Asked", in_flight: "In progress", blocked_needs_owner: "Needs owner", shipped: "Shipped",
    }),
    runOutcomes: countBreakdown(filteredRuns.map((run) => runOutcome(run.status)), {
      succeeded: "Succeeded", failed: "Failed", running: "Running", cancelled: "Cancelled", unknown: "Not recorded",
    }),
    failureBreakdown: countBreakdown(failedPhaseLabels),
    durationSeries: runRecords.flatMap((run) => run.durationMs !== null && run.endedAt
      ? [{ ts: run.endedAt, value: run.durationMs, runId: run.id }]
      : []),
    requests: visibleRequests,
    runs: visibleRuns,
    gaps,
    truncated,
    errors: [],
  };
}
