import type {
  DispatchAttempt,
  ExecutionLease,
} from "./DispatchAttemptStore.js";

export type TaskDispatchStallState =
  | "waiting_dependency"
  | "lifecycle_split"
  | "task_unclaimed"
  | "session_lost"
  | "waiting_capacity"
  | "settling"
  | "waiting_report"
  | "recoverable"
  | "failed"
  | "blocked"
  | "done"
  | "projection_conflict"
  | "waiting_admin"
  | "running"
  | "settled"
  | "unknown";

export interface TaskDispatchStallInput {
  lifecycleBucket?: string | null;
  fmState?: string | null;
  dependencyAllowed?: boolean;
  waitingAdmin?: boolean;
  activeLease?: ExecutionLease | null;
  latestAttempt?: DispatchAttempt | null;
  hasLiveSession?: boolean;
  canonicalState?: string | null;
  nowMs?: number;
  startupGraceMs?: number;
}

export interface TaskDispatchStallClassification {
  state: TaskDispatchStallState;
  actionable: boolean;
  recommended_action:
    | "wait_dependency"
    | "repair_retry"
    | "claim_task"
    | "restart_session"
    | "inspect_conflict"
    | "wait_admin"
    | "none";
  reason: string;
}

/**
 * One deterministic classifier shared by PM diagnostics, Panel and Mobile.
 * It never mutates lifecycle state; recovery still goes through TaskDispatcher.
 */
export function classifyTaskDispatchStall(
  input: TaskDispatchStallInput,
): TaskDispatchStallClassification {
  const bucket = String(input.lifecycleBucket ?? "").trim().toLowerCase();
  const fmState = String(input.fmState ?? "").trim().toLowerCase();
  const canonicalState = String(input.canonicalState ?? "").trim().toLowerCase();

  if (canonicalState === "running") {
    return { state: "running", actionable: false, recommended_action: "none", reason: "canonical runtime state=running" };
  }
  if (canonicalState === "settling") {
    return { state: "settling", actionable: false, recommended_action: "none", reason: "canonical runtime state=settling" };
  }
  if (canonicalState === "projection_conflict") {
    return {
      state: "projection_conflict",
      actionable: false,
      recommended_action: "inspect_conflict",
      reason: "canonical runtime evidence conflicts; automatic recovery is forbidden",
    };
  }
  if (canonicalState === "recoverable") {
    return { state: "recoverable", actionable: true, recommended_action: "restart_session", reason: "canonical runtime state=recoverable" };
  }
  if (canonicalState === "waiting_capacity") {
    return { state: "waiting_capacity", actionable: false, recommended_action: "none", reason: "canonical runtime state=waiting_capacity" };
  }
  if (canonicalState === "waiting_report") {
    return { state: "waiting_report", actionable: false, recommended_action: "none", reason: "canonical runtime state=waiting_report" };
  }
  if (canonicalState === "failed") {
    return { state: "failed", actionable: false, recommended_action: "none", reason: "canonical runtime state=failed; PM/ADMIN decision required" };
  }
  if (canonicalState === "blocked") {
    return { state: "blocked", actionable: false, recommended_action: "none", reason: "canonical runtime state=blocked; PM/ADMIN decision required" };
  }
  if (canonicalState === "done") {
    return { state: "done", actionable: false, recommended_action: "none", reason: "canonical runtime state=done" };
  }

  if (input.dependencyAllowed === false) {
    return {
      state: "waiting_dependency",
      actionable: false,
      recommended_action: "wait_dependency",
      reason: "task dependencies are not settled",
    };
  }
  if (bucket === "inbox" && ["dispatched", "running"].includes(fmState)) {
    return {
      state: "lifecycle_split",
      actionable: true,
      recommended_action: "repair_retry",
      reason: `physical bucket=inbox but frontmatter state=${fmState}`,
    };
  }
  if (input.waitingAdmin) {
    return {
      state: "waiting_admin",
      actionable: false,
      recommended_action: "wait_admin",
      reason: "dispatch retry requires ADMIN decision",
    };
  }
  if (["done", "review", "archive"].includes(bucket)) {
    return {
      state: "settled",
      actionable: false,
      recommended_action: "none",
      reason: `task lifecycle bucket=${bucket}`,
    };
  }
  if ((bucket === "inbox" || bucket === "tasks") && !input.activeLease) {
    return {
      state: "task_unclaimed",
      actionable: true,
      recommended_action: "claim_task",
      reason: "canonical task is available in the recipient inbox",
    };
  }
  if (bucket === "active") {
    if (input.activeLease && input.hasLiveSession) {
      return {
        state: "running",
        actionable: false,
        recommended_action: "none",
        reason: "task has one active lease and a live session",
      };
    }
    if (input.activeLease) {
      const nowMs = input.nowMs ?? Date.now();
      const acquiredMs = Date.parse(input.activeLease.acquired_at);
      const elapsedMs = Number.isFinite(acquiredMs) ? Math.max(0, nowMs - acquiredMs) : Number.POSITIVE_INFINITY;
      if (elapsedMs <= Math.max(0, input.startupGraceMs ?? 30_000)) {
        return {
          state: "settling",
          actionable: false,
          recommended_action: "none",
          reason: "execution lease exists and Session is within startup grace",
        };
      }
      return {
        state: "projection_conflict",
        actionable: false,
        recommended_action: "inspect_conflict",
        reason: "valid execution lease has no live Session after startup grace",
      };
    }
    return {
      state: "recoverable",
      actionable: true,
      recommended_action: "restart_session",
      reason: "active task has no execution lease or live Session",
    };
  }
  if (input.latestAttempt?.status === "settled") {
    return {
      state: "settled",
      actionable: false,
      recommended_action: "none",
      reason: "latest dispatch attempt is settled",
    };
  }
  return {
    state: "unknown",
    actionable: false,
    recommended_action: "none",
    reason: `unclassified lifecycle bucket=${bucket || "unknown"}`,
  };
}
