import type { ApplicationStage, Role } from "@prisma/client";

// Recruitment pipeline state machine (Crewing-Implementation-Spec §5.1) — mirrors
// po-state-machine / requisition-state-machine. The 7 board stages advance in
// order; ONBOARDED is the terminal system state set at onboarding (Phase 3c);
// REJECTED is an orthogonal branch reachable from any active stage.
//
// Stage advances are modelled here. The within-stage work — recording reference
// checks, capturing bank/EPF, agreeing the salary, recording the interview
// result, requesting a waiver — happens in server actions; this machine governs
// when a candidate may move to the next column and who may move them.
//
// Manager-gated advances (spec §6): SALARY_AGREEMENT → PROPOSED (salary approval)
// and INTERVIEW → SELECTED (final selection) are Manager-only. The interview
// waiver is a separate Manager-approved action (R2), never automatic.

export type ApplicationAction =
  | "start_competency" // SHORTLISTED → COMPETENCY_AND_REFERENCES
  | "verify_competency" // COMPETENCY_AND_REFERENCES → DOC_VERIFICATION
  | "verify_docs" // DOC_VERIFICATION → SALARY_AGREEMENT
  | "approve_salary" // SALARY_AGREEMENT → PROPOSED (Manager)
  | "propose_accepted" // PROPOSED → INTERVIEW
  | "select" // INTERVIEW → SELECTED (Manager)
  | "onboard"; // SELECTED → ONBOARDED (Phase 3c)

interface Transition {
  to: ApplicationStage;
  allowedRoles: Role[];
}

type TransitionMap = Partial<Record<ApplicationAction, Transition>>;

const SOURCING_ROLES: Role[] = ["MANNING", "MANAGER", "SUPERUSER"];
const MANAGER_ROLES: Role[] = ["MANAGER", "SUPERUSER"];

const TRANSITIONS: Partial<Record<ApplicationStage, TransitionMap>> = {
  SHORTLISTED: {
    start_competency: { to: "COMPETENCY_AND_REFERENCES", allowedRoles: SOURCING_ROLES },
  },
  COMPETENCY_AND_REFERENCES: {
    verify_competency: { to: "DOC_VERIFICATION", allowedRoles: SOURCING_ROLES },
  },
  DOC_VERIFICATION: {
    verify_docs: { to: "SALARY_AGREEMENT", allowedRoles: SOURCING_ROLES },
  },
  SALARY_AGREEMENT: {
    // Manager approves the agreed salary structure (spec §6).
    approve_salary: { to: "PROPOSED", allowedRoles: MANAGER_ROLES },
  },
  PROPOSED: {
    propose_accepted: { to: "INTERVIEW", allowedRoles: SOURCING_ROLES },
  },
  INTERVIEW: {
    // Final selection is a Manager approval (spec §6). The action enforces that
    // the interview was accepted or a Manager-approved waiver is in place (R2).
    select: { to: "SELECTED", allowedRoles: MANAGER_ROLES },
  },
  SELECTED: {
    // The onboarding side-effect (Phase 3c) moves SELECTED → ONBOARDED.
    onboard: { to: "ONBOARDED", allowedRoles: SOURCING_ROLES },
  },
};

// The 7 visible board columns, in order (spec §8.4). ONBOARDED/REJECTED are not
// board columns — they are terminal/branch states.
export const BOARD_STAGES: ApplicationStage[] = [
  "SHORTLISTED",
  "COMPETENCY_AND_REFERENCES",
  "DOC_VERIFICATION",
  "SALARY_AGREEMENT",
  "PROPOSED",
  "INTERVIEW",
  "SELECTED",
];

export function getTransition(from: ApplicationStage, action: ApplicationAction): Transition | null {
  return TRANSITIONS[from]?.[action] ?? null;
}

export function canPerformAction(from: ApplicationStage, action: ApplicationAction, role: Role): boolean {
  return getTransition(from, action)?.allowedRoles.includes(role) ?? false;
}

export function getAvailableActions(stage: ApplicationStage, role: Role): ApplicationAction[] {
  const map = TRANSITIONS[stage];
  if (!map) return [];
  return (Object.keys(map) as ApplicationAction[]).filter((a) => canPerformAction(stage, a, role));
}

// ── Rejection (orthogonal) ───────────────────────────────────────────────────
// A candidate may be rejected with remarks from any active stage (not once
// SELECTED/ONBOARDED, and not again if already REJECTED), by MPO or Manager.

export const REJECT_ROLES: Role[] = ["MANNING", "MANAGER", "SUPERUSER"];
const TERMINAL: ApplicationStage[] = ["SELECTED", "ONBOARDED", "REJECTED"];

export function canReject(from: ApplicationStage, role: Role): boolean {
  return !TERMINAL.includes(from) && REJECT_ROLES.includes(role);
}
