import { createHash } from "node:crypto";

export type HistoricalIncidentType = "runtime-session" | "unresolved";
export type UnresolvedReason =
  | "missing-original-timestamp"
  | "missing-canonical-type"
  | "provenance-conflict";

export interface HistoricalIncidentRecord {
  sourceId: string;
  sourceRecordId: string;
  originalTimestamp: string | null;
  incidentType: HistoricalIncidentType;
  title: string;
  details: string;
  provenance: string;
  unresolvedReasons: UnresolvedReason[];
}

export interface EligibleHistoricalIncident extends HistoricalIncidentRecord {
  originalTimestamp: string;
  incidentType: Exclude<HistoricalIncidentType, "unresolved">;
  dedupKey: string;
}

export interface HistoricalIncidentDestination {
  findProvenance(dedupKey: string): Promise<string | null>;
  create(record: EligibleHistoricalIncident): Promise<void>;
}

type Outcome = "created" | "existing" | "unresolved";
type Count = { discovered: number; eligible: number; created: number; existing: number; unresolved: number };

export interface HistoricalReconciliationReport {
  records: Array<HistoricalIncidentRecord & { dedupKey: string; outcome: Outcome }>;
  counts: {
    bySource: Record<string, Count>;
    byType: Record<string, Count>;
  };
}

interface OffloadStatus {
  reconciler?: { lastAt?: unknown };
  dispatch?: { state?: unknown; detail?: unknown; host?: unknown };
  queue?: { wedge?: { host?: unknown; detail?: unknown } | null };
  jobs?: Array<{ id?: unknown; stage?: unknown; host?: unknown; rc?: unknown; publication?: { reason?: unknown } }>;
}

const SOURCE_ID = "offload-status:2026-07-18";
const unresolvedReasons: UnresolvedReason[] = ["missing-original-timestamp", "missing-canonical-type"];

function text(value: unknown): string {
  return typeof value === "string" ? value : "";
}

function unresolvedRecord(sourceRecordId: string, title: string, details: string, provenance: unknown): HistoricalIncidentRecord {
  return {
    sourceId: SOURCE_ID,
    sourceRecordId,
    originalTimestamp: null,
    incidentType: "unresolved",
    title,
    details,
    provenance: JSON.stringify(provenance),
    unresolvedReasons: [...unresolvedReasons],
  };
}

export function inventoryOffloadStatus(source: OffloadStatus): { sourceId: string; observedAt: string | null; records: HistoricalIncidentRecord[] } {
  const records: HistoricalIncidentRecord[] = [];
  if (source.dispatch?.state === "wedged") {
    records.push(unresolvedRecord("dispatch", "Dispatch wedged", text(source.dispatch.detail), source.dispatch));
  }
  if (source.queue?.wedge) {
    const host = text(source.queue.wedge.host) || "unknown";
    records.push(unresolvedRecord(`queue-wedge:${host}`, "Queue wedged", text(source.queue.wedge.detail), source.queue.wedge));
  }
  for (const job of source.jobs ?? []) {
    if (job.stage !== "failed" || typeof job.id !== "string" || job.id.length === 0) continue;
    records.push(unresolvedRecord(
      `job:${job.id}`,
      `Remote job ${job.id} failed`,
      text(job.publication?.reason) || `exit ${String(job.rc ?? "unknown")}`,
      job,
    ));
  }
  return {
    sourceId: SOURCE_ID,
    observedAt: typeof source.reconciler?.lastAt === "string" ? source.reconciler.lastAt : null,
    records,
  };
}

export function historicalInventoryReport(inventory: { sourceId: string; records: readonly HistoricalIncidentRecord[] }): {
  counts: {
    bySource: Record<string, Pick<Count, "discovered" | "eligible" | "unresolved">>;
    byType: Record<string, Pick<Count, "discovered" | "eligible" | "unresolved">>;
  };
} {
  const counts = { bySource: {} as Record<string, Pick<Count, "discovered" | "eligible" | "unresolved">>, byType: {} as Record<string, Pick<Count, "discovered" | "eligible" | "unresolved">> };
  for (const record of inventory.records) {
    const eligible = record.originalTimestamp !== null && record.incidentType !== "unresolved" && record.unresolvedReasons.length === 0;
    for (const [table, key] of [[counts.bySource, record.sourceId], [counts.byType, record.incidentType]] as const) {
      const count = table[key] ??= { discovered: 0, eligible: 0, unresolved: 0 };
      count.discovered += 1;
      count[eligible ? "eligible" : "unresolved"] += 1;
    }
  }
  return { counts };
}

export function historicalDedupKey(sourceId: string, sourceRecordId: string): string {
  const framed = `${sourceId.length}:${sourceId}${sourceRecordId.length}:${sourceRecordId}`;
  return `incident-history/v1:${createHash("sha256").update(framed).digest("hex")}`;
}

function emptyCount(): Count {
  return { discovered: 0, eligible: 0, created: 0, existing: 0, unresolved: 0 };
}

function countFor(table: Record<string, Count>, key: string): Count {
  return table[key] ??= emptyCount();
}

export async function reconcileHistoricalIncidents(
  records: readonly HistoricalIncidentRecord[],
  destination: HistoricalIncidentDestination,
): Promise<HistoricalReconciliationReport> {
  const report: HistoricalReconciliationReport = { records: [], counts: { bySource: {}, byType: {} } };
  for (const record of records) {
    const sourceCount = countFor(report.counts.bySource, record.sourceId);
    const typeCount = countFor(report.counts.byType, record.incidentType);
    sourceCount.discovered += 1;
    typeCount.discovered += 1;
    const dedupKey = historicalDedupKey(record.sourceId, record.sourceRecordId);
    let outcome: Outcome = "unresolved";
    let reasons = [...record.unresolvedReasons];
    const eligible = record.originalTimestamp !== null && record.incidentType !== "unresolved" && reasons.length === 0;
    if (eligible) {
      sourceCount.eligible += 1;
      typeCount.eligible += 1;
      const existing = await destination.findProvenance(dedupKey);
      if (existing === record.provenance) {
        outcome = "existing";
      } else if (existing !== null) {
        reasons = ["provenance-conflict"];
      } else {
        await destination.create({ ...record, originalTimestamp: record.originalTimestamp!, incidentType: record.incidentType as Exclude<HistoricalIncidentType, "unresolved">, dedupKey });
        outcome = "created";
      }
    }
    sourceCount[outcome] += 1;
    typeCount[outcome] += 1;
    report.records.push({ ...record, unresolvedReasons: reasons, dedupKey, outcome });
  }
  return report;
}
