import { describe, it, expect, vi, afterEach } from "vitest";
import {
  hasPermission,
  requirePermission,
  isSubmitterRole,
  submitterCanViewAll,
  canViewAllPos,
} from "@/lib/permissions";

describe("Permissions", () => {
  describe("hasPermission", () => {
    // ── Original cases ─────────────────────────────────────────────────────

    it("TECHNICAL can create POs", () => {
      expect(hasPermission("TECHNICAL", "create_po")).toBe(true);
    });

    it("TECHNICAL cannot approve POs", () => {
      expect(hasPermission("TECHNICAL", "approve_po")).toBe(false);
    });

    it("MANAGER can approve POs", () => {
      expect(hasPermission("MANAGER", "approve_po")).toBe(true);
    });

    // MANAGER was intentionally granted process_payment in commit e1340b9
    // ("chore(perm): manager permissions fix 2").
    it("MANAGER can process payment", () => {
      expect(hasPermission("MANAGER", "process_payment")).toBe(true);
    });

    it("ACCOUNTS can process payment", () => {
      expect(hasPermission("ACCOUNTS", "process_payment")).toBe(true);
    });

    it("SUPERUSER has all operational permissions", () => {
      expect(hasPermission("SUPERUSER", "create_po")).toBe(true);
      expect(hasPermission("SUPERUSER", "approve_po")).toBe(true);
      expect(hasPermission("SUPERUSER", "process_payment")).toBe(true);
      expect(hasPermission("SUPERUSER", "confirm_receipt")).toBe(true);
    });

    it("ADMIN can manage users", () => {
      expect(hasPermission("ADMIN", "manage_users")).toBe(true);
    });

    it("AUDITOR has read-only access", () => {
      expect(hasPermission("AUDITOR", "view_all_pos")).toBe(true);
      expect(hasPermission("AUDITOR", "approve_po")).toBe(false);
      expect(hasPermission("AUDITOR", "create_po")).toBe(false);
    });

    // ── New permissions: MANAGER and ACCOUNTS expansions ──────────────────

    it("MANAGER can create POs", () => {
      expect(hasPermission("MANAGER", "create_po")).toBe(true);
    });

    it("MANAGER can submit POs", () => {
      expect(hasPermission("MANAGER", "submit_po")).toBe(true);
    });

    it("MANAGER can manage vendors", () => {
      expect(hasPermission("MANAGER", "manage_vendors")).toBe(true);
    });

    it("ACCOUNTS can manage vendors", () => {
      expect(hasPermission("ACCOUNTS", "manage_vendors")).toBe(true);
    });

    it("ACCOUNTS cannot create POs", () => {
      expect(hasPermission("ACCOUNTS", "create_po")).toBe(false);
    });

    it("ACCOUNTS cannot approve POs", () => {
      expect(hasPermission("ACCOUNTS", "approve_po")).toBe(false);
    });

    it("TECHNICAL cannot manage vendors", () => {
      expect(hasPermission("TECHNICAL", "manage_vendors")).toBe(false);
    });

    it("MANNING cannot manage vendors", () => {
      expect(hasPermission("MANNING", "manage_vendors")).toBe(false);
    });

    it("AUDITOR cannot create, submit, or approve POs", () => {
      expect(hasPermission("AUDITOR", "create_po")).toBe(false);
      expect(hasPermission("AUDITOR", "submit_po")).toBe(false);
      expect(hasPermission("AUDITOR", "approve_po")).toBe(false);
    });

    it("AUDITOR cannot manage vendors or products", () => {
      expect(hasPermission("AUDITOR", "manage_vendors")).toBe(false);
      expect(hasPermission("AUDITOR", "manage_products")).toBe(false);
    });

    it("ADMIN cannot approve or process payments", () => {
      expect(hasPermission("ADMIN", "approve_po")).toBe(false);
      expect(hasPermission("ADMIN", "process_payment")).toBe(false);
    });

    it("SUPERUSER does not have manage_vendors (admin-only permission)", () => {
      expect(hasPermission("SUPERUSER", "manage_vendors")).toBe(false);
    });
  });

  // ── Submitter view-all (feature-flagged) ──────────────────────────────────
  describe("isSubmitterRole", () => {
    it("is true for the two submitter roles", () => {
      expect(isSubmitterRole("TECHNICAL")).toBe(true);
      expect(isSubmitterRole("MANNING")).toBe(true);
    });

    it("is false for every other role", () => {
      for (const role of ["ACCOUNTS", "MANAGER", "SUPERUSER", "AUDITOR", "ADMIN"] as const) {
        expect(isSubmitterRole(role)).toBe(false);
      }
    });
  });

  describe("canViewAllPos / submitterCanViewAll — flag OFF (default)", () => {
    it("submitters cannot view all POs", () => {
      expect(canViewAllPos("TECHNICAL")).toBe(false);
      expect(canViewAllPos("MANNING")).toBe(false);
      expect(submitterCanViewAll("TECHNICAL")).toBe(false);
    });

    it("view_all_pos holders can still view all POs", () => {
      for (const role of ["ACCOUNTS", "MANAGER", "SUPERUSER", "AUDITOR", "ADMIN"] as const) {
        expect(canViewAllPos(role)).toBe(true);
      }
    });
  });

  describe("canViewAllPos / submitterCanViewAll — flag ON", () => {
    afterEach(() => {
      vi.unstubAllEnvs();
      vi.resetModules();
    });

    it("submitters gain view-all when NEXT_PUBLIC_SUBMITTER_VIEW_ALL_ENABLED=true", async () => {
      vi.resetModules();
      vi.stubEnv("NEXT_PUBLIC_SUBMITTER_VIEW_ALL_ENABLED", "true");
      const perms = await import("@/lib/permissions");

      expect(perms.submitterCanViewAll("TECHNICAL")).toBe(true);
      expect(perms.submitterCanViewAll("MANNING")).toBe(true);
      expect(perms.canViewAllPos("TECHNICAL")).toBe(true);
      expect(perms.canViewAllPos("MANNING")).toBe(true);
    });

    it("does not widen non-submitter roles, and is read-only (no approve/edit)", async () => {
      vi.resetModules();
      vi.stubEnv("NEXT_PUBLIC_SUBMITTER_VIEW_ALL_ENABLED", "true");
      const perms = await import("@/lib/permissions");

      expect(perms.submitterCanViewAll("MANAGER")).toBe(false);
      expect(perms.canViewAllPos("ACCOUNTS")).toBe(true); // unchanged
      // The flag grants read access only — no approval or edit rights.
      expect(perms.hasPermission("TECHNICAL", "approve_po")).toBe(false);
      expect(perms.hasPermission("TECHNICAL", "view_all_pos")).toBe(false);
    });
  });

  describe("requirePermission", () => {
    it("does not throw when permission is granted", () => {
      expect(() => requirePermission("MANAGER", "approve_po")).not.toThrow();
    });

    it("throws when permission is denied", () => {
      expect(() => requirePermission("TECHNICAL", "approve_po")).toThrow();
    });

    it("throws with a message containing the role name", () => {
      expect(() => requirePermission("ACCOUNTS", "approve_po")).toThrow(/ACCOUNTS/);
    });
  });
});
