import {
  createEmptyTaskAuditSummary,
  type TaskAuditCode,
  type TaskAuditFinding,
  type TaskAuditSeverity,
  type TaskAuditSummary,
} from "./task-registry.audit.shared.js";
import type { TaskRecord } from "./task-registry.types.js";

export type TaskAuditOptions = {
  now?: number;
  tasks?: TaskRecord[];
  staleQueuedMs?: number;
  staleRunningMs?: number;
};

const DEFAULT_STALE_QUEUED_MS = 10 * 60_000;
const DEFAULT_STALE_RUNNING_MS = 30 * 60_000;
export { createEmptyTaskAuditSummary };
export type { TaskAuditCode, TaskAuditFinding, TaskAuditSeverity, TaskAuditSummary };

let taskAuditTaskProvider: () => TaskRecord[] = () => [];

export function configureTaskAuditTaskProvider(provider: () => TaskRecord[]): void {
  taskAuditTaskProvider = provider;
}

function createFinding(params: {
  severity: TaskAuditSeverity;
  code: TaskAuditCode;
  task: TaskRecord;
  detail: string;
  ageMs?: number;
}): TaskAuditFinding {
  return {
    severity: params.severity,
    code: params.code,
    task: params.task,
    detail: params.detail,
    ...(typeof params.ageMs === "number" ? { ageMs: params.ageMs } : {}),
  };
}

function taskReferenceAt(task: TaskRecord): number {
  return task.lastEventAt ?? task.startedAt ?? task.createdAt;
}

function findTimestampInconsistency(task: TaskRecord): TaskAuditFinding | null {
  if (task.startedAt && task.startedAt < task.createdAt) {
    return createFinding({
      severity: "warn",
      code: "inconsistent_timestamps",
      task,
      detail: "startedAt is earlier than createdAt",
    });
  }
  if (task.endedAt && task.startedAt && task.endedAt < task.startedAt) {
    return createFinding({
      severity: "warn",
      code: "inconsistent_timestamps",
      task,
      detail: "endedAt is earlier than startedAt",
    });
  }
  if ((task.status === "queued" || task.status === "running") && task.endedAt) {
    return createFinding({
      severity: "warn",
      code: "inconsistent_timestamps",
      task,
      detail: `${task.status} task should not already have endedAt`,
    });
  }
  return null;
}

function compareFindings(left: TaskAuditFinding, right: TaskAuditFinding): number {
  const severityRank = (severity: TaskAuditSeverity) => (severity === "error" ? 0 : 1);
  const severityDiff = severityRank(left.severity) - severityRank(right.severity);
  if (severityDiff !== 0) {
    return severityDiff;
  }
  const leftAge = left.ageMs ?? -1;
  const rightAge = right.ageMs ?? -1;
  if (leftAge !== rightAge) {
    return rightAge - leftAge;
  }
  return left.task.createdAt - right.task.createdAt;
}

export function listTaskAuditFindings(options: TaskAuditOptions = {}): TaskAuditFinding[] {
  const tasks = options.tasks ?? taskAuditTaskProvider();
  const now = options.now ?? Date.now();
  const staleQueuedMs = options.staleQueuedMs ?? DEFAULT_STALE_QUEUED_MS;
  const staleRunningMs = options.staleRunningMs ?? DEFAULT_STALE_RUNNING_MS;
  const findings: TaskAuditFinding[] = [];

  for (const task of tasks) {
    const referenceAt = taskReferenceAt(task);
    const ageMs = Math.max(0, now - referenceAt);

    if (task.status === "queued" && ageMs >= staleQueuedMs) {
      findings.push(
        createFinding({
          severity: "warn",
          code: "stale_queued",
          task,
          ageMs,
          detail: "queued task has not advanced recently",
        }),
      );
    }

    if (task.status === "running" && ageMs >= staleRunningMs) {
      findings.push(
        createFinding({
          severity: "error",
          code: "stale_running",
          task,
          ageMs,
          detail: "running task appears stuck",
        }),
      );
    }

    if (task.status === "lost") {
      findings.push(
        createFinding({
          severity: "error",
          code: "lost",
          task,
          ageMs,
          detail: task.error?.trim() || "task lost its backing session",
        }),
      );
    }

    if (task.deliveryStatus === "failed" && task.notifyPolicy !== "silent") {
      findings.push(
        createFinding({
          severity: "warn",
          code: "delivery_failed",
          task,
          ageMs,
          detail: "terminal update delivery failed",
        }),
      );
    }

    if (
      task.status !== "lost" &&
      task.status !== "queued" &&
      task.status !== "running" &&
      typeof task.cleanupAfter !== "number"
    ) {
      findings.push(
        createFinding({
          severity: "warn",
          code: "missing_cleanup",
          task,
          ageMs,
          detail: "terminal task is missing cleanupAfter",
        }),
      );
    }

    const inconsistency = findTimestampInconsistency(task);
    if (inconsistency) {
      findings.push(inconsistency);
    }
  }

  return findings.toSorted(compareFindings);
}

export function summarizeTaskAuditFindings(findings: Iterable<TaskAuditFinding>): TaskAuditSummary {
  const summary = createEmptyTaskAuditSummary();
  for (const finding of findings) {
    summary.total += 1;
    summary.byCode[finding.code] += 1;
    if (finding.severity === "error") {
      summary.errors += 1;
    } else {
      summary.warnings += 1;
    }
  }
  return summary;
}
