import { describe, it, expect } from "vitest";
import {
  canCancel,
  canPerformAction,
  getAvailableActions,
  getTransition,
} from "@/lib/requisition-state-machine";

// The requisition lifecycle (Crewing-Implementation-Spec §5.2):
// OPEN → SHORTLISTING → PROPOSING → INTERVIEWING → SELECTED → FILLED,
// CANCELLED reachable from OPEN/SHORTLISTING (Manager). Selection is Manager-only.
describe("Requisition state machine", () => {
  describe("forward transitions", () => {
    it("MPO can start shortlisting an OPEN requisition", () => {
      expect(canPerformAction("OPEN", "start_shortlisting", "MANNING")).toBe(true);
      expect(getTransition("OPEN", "start_shortlisting")?.to).toBe("SHORTLISTING");
    });

    it("MPO advances through proposing and interviewing", () => {
      expect(canPerformAction("SHORTLISTING", "mark_proposing", "MANNING")).toBe(true);
      expect(canPerformAction("PROPOSING", "start_interviewing", "MANNING")).toBe(true);
    });

    it("final selection is Manager-only (spec §6)", () => {
      expect(canPerformAction("INTERVIEWING", "mark_selected", "MANAGER")).toBe(true);
      expect(canPerformAction("INTERVIEWING", "mark_selected", "SUPERUSER")).toBe(true);
      expect(canPerformAction("INTERVIEWING", "mark_selected", "MANNING")).toBe(false);
    });

    it("onboarding fills the vacancy from SELECTED", () => {
      expect(getTransition("SELECTED", "mark_filled")?.to).toBe("FILLED");
      expect(canPerformAction("SELECTED", "mark_filled", "MANNING")).toBe(true);
    });

    it("rejects actions on the wrong source state", () => {
      expect(canPerformAction("OPEN", "mark_selected", "MANAGER")).toBe(false);
      expect(getTransition("FILLED", "mark_filled")).toBeNull();
      expect(getTransition("CANCELLED", "start_shortlisting")).toBeNull();
    });

    it("site staff and accounts can perform no transitions", () => {
      for (const status of ["OPEN", "SHORTLISTING", "INTERVIEWING", "SELECTED"] as const) {
        expect(getAvailableActions(status, "SITE_STAFF")).toHaveLength(0);
        expect(getAvailableActions(status, "ACCOUNTS")).toHaveLength(0);
      }
    });
  });

  describe("getAvailableActions", () => {
    it("offers shortlisting on OPEN to the MPO", () => {
      expect(getAvailableActions("OPEN", "MANNING")).toEqual(["start_shortlisting"]);
    });

    it("offers nothing once FILLED", () => {
      expect(getAvailableActions("FILLED", "MANAGER")).toHaveLength(0);
    });
  });

  describe("cancellation (orthogonal)", () => {
    it("MPO and Manager can withdraw from OPEN or SHORTLISTING (matrix §6)", () => {
      expect(canCancel("OPEN", "MANAGER")).toBe(true);
      expect(canCancel("SHORTLISTING", "SUPERUSER")).toBe(true);
      expect(canCancel("OPEN", "MANNING")).toBe(true);
    });

    it("cannot be withdrawn once past shortlisting", () => {
      expect(canCancel("PROPOSING", "MANAGER")).toBe(false);
      expect(canCancel("INTERVIEWING", "MANAGER")).toBe(false);
      expect(canCancel("FILLED", "MANAGER")).toBe(false);
      expect(canCancel("CANCELLED", "MANAGER")).toBe(false);
    });

    it("site staff and accounts may never withdraw", () => {
      expect(canCancel("OPEN", "SITE_STAFF")).toBe(false);
      expect(canCancel("OPEN", "ACCOUNTS")).toBe(false);
    });
  });
});
