// Single frontend source of truth for the canonical job pipeline.
// Mirrors the backend JobPipeline (JobTrackerApi/Services/JobPipeline.cs); keep the two in sync.
// The backend serves the same shape from GET /jobapplications/pipeline (key/order/category/group).

/**
 * Detailed internal stages, in pipeline order. The board does not show ten columns — it groups
 * these via PIPELINE_GROUPS below.
 *
 * Waiting and Ghosted are retained deliberately: Ghosted is where the rules engine parks a job
 * that was never answered (neither Rejected nor Withdrawn), and Waiting carries its own follow-up
 * rule and reminder wording.
 */
export const PIPELINE_STATUSES = [
  "Saved",
  "Interested",
  "Preparing",
  "Applied",
  "Waiting",
  "Interview",
  "Offer",
  "Rejected",
  "Ghosted",
  "Withdrawn",
] as const;

export type PipelineStatus = (typeof PIPELINE_STATUSES)[number];

export type PipelineGroup = "NotApplied" | "Active" | "Closed";

/**
 * How the board collapses stages. A different axis from the backend's analytics "category":
 * Offer is category Success there, but groups under Active here because it is still being worked.
 */
export const PIPELINE_GROUPS: { key: PipelineGroup; labelKey: string; statuses: PipelineStatus[] }[] = [
  { key: "NotApplied", labelKey: "pipelineGroupNotApplied", statuses: ["Saved", "Interested", "Preparing"] },
  { key: "Active", labelKey: "pipelineGroupActive", statuses: ["Applied", "Waiting", "Interview", "Offer"] },
  { key: "Closed", labelKey: "pipelineGroupClosed", statuses: ["Rejected", "Ghosted", "Withdrawn"] },
];

const GROUP_BY_STATUS = new Map<PipelineStatus, PipelineGroup>(
  PIPELINE_GROUPS.flatMap((g) => g.statuses.map((s) => [s, g.key] as const)),
);

/**
 * Stage a job lands on when dragged onto a grouped column. A group is not itself a status, so a
 * coarse drag has to pick one: the group's entry stage. Precise stages stay available on the
 * card menu.
 *
 * Closed deliberately enters on Rejected rather than Ghosted or Withdrawn: Ghosted is something the
 * rules engine concludes, and Withdrawn is a specific claim about the user's own action. Neither
 * should be inferred from a drag.
 */
export const GROUP_ENTRY_STATUS: Record<PipelineGroup, PipelineStatus> = {
  NotApplied: "Saved",
  Active: "Applied",
  Closed: "Rejected",
};

/** The board group a status belongs to. Unknown/custom statuses have no group. */
export function groupOf(status?: string | null): PipelineGroup | null {
  const normalized = normalizeStatus(status);
  return normalized === "Other" ? null : GROUP_BY_STATUS.get(normalized) ?? null;
}

/** True for pre-application stages — nothing has been submitted yet. */
export function isProspect(status?: string | null): boolean {
  return groupOf(status) === "NotApplied";
}

export type StatusTone = "primary" | "info" | "success" | "warning" | "error" | "default";

// Legacy/synonym spellings collapse onto a canonical stage (matches the backend alias map).
const ALIASES: Record<string, PipelineStatus> = {
  bookmarked: "Saved",
  wishlist: "Saved",
  "to apply": "Saved",
  shortlisted: "Interested",
  considering: "Interested",
  "in preparation": "Preparing",
  "preparing application": "Preparing",
  drafting: "Preparing",
  interviewing: "Interview",
  interviews: "Interview",
  interviewed: "Interview",
  "in interview": "Interview",
  declined: "Rejected",
  "no response": "Ghosted",
  "no reply": "Ghosted",
  pending: "Waiting",
  awaiting: "Waiting",
  "in progress": "Waiting",
  "awaiting response": "Waiting",
  withdrew: "Withdrawn",
  cancelled: "Withdrawn",
  canceled: "Withdrawn",
};

/** Canonical status for a raw value, or "Other" for unknown/custom statuses. */
export function normalizeStatus(status?: string | null): PipelineStatus | "Other" {
  const trimmed = (status ?? "").trim();
  if (!trimmed) return "Applied";
  const exact = PIPELINE_STATUSES.find((s) => s.toLowerCase() === trimmed.toLowerCase());
  if (exact) return exact;
  const alias = ALIASES[trimmed.toLowerCase()];
  return alias ?? "Other";
}

/** MUI palette key for a status; both chip color and board accent derive from this. */
export function statusTone(status?: string | null): StatusTone {
  switch (normalizeStatus(status)) {
    case "Offer":
      return "success";
    case "Rejected":
    case "Withdrawn":
      return "error";
    case "Waiting":
    case "Ghosted":
      return "warning";
    case "Interview":
      return "info";
    case "Applied":
      return "primary";
    // Pre-application stages read as neutral: nothing is in flight, so nothing needs attention.
    case "Saved":
    case "Interested":
    case "Preparing":
      return "default";
    default:
      return "default";
  }
}

const LABEL_KEYS: Record<PipelineStatus, string> = {
  Saved: "statusSaved",
  Interested: "statusInterested",
  Preparing: "statusPreparing",
  Applied: "statusApplied",
  Waiting: "statusWaiting",
  Interview: "statusInterview",
  Offer: "statusOffer",
  Rejected: "statusRejected",
  Ghosted: "statusGhosted",
  Withdrawn: "statusWithdrawn",
};

/** Localized label for a status, falling back to the raw value for custom statuses. */
export function statusLabel(t: (key: any, params?: any) => string, status: string): string {
  const normalized = normalizeStatus(status);
  return normalized === "Other" ? status : t(LABEL_KEYS[normalized]);
}
