import { describe, expect, it } from "vitest";
import { definePermissions } from "./definePermissions";

describe("definePermissions", () => {
  describe("basic usage (no deps, no aliases)", () => {
    it("creates permissions with moduleName:commandName keys", () => {
      const result = definePermissions("primitives", [
        "createCategory",
        "updateCategory",
        "deleteCategory",
      ] as const);

      expect(result.permissions.createCategory).toBe("primitives:createCategory");
      expect(result.permissions.updateCategory).toBe("primitives:updateCategory");
      expect(result.permissions.deleteCategory).toBe("primitives:deleteCategory");
    });

    it("sets own and all to the same values", () => {
      const result = definePermissions("primitives", [
        "createCategory",
        "activateCategory",
      ] as const);

      expect(result.own).toEqual(["primitives:createCategory", "primitives:activateCategory"]);
      expect(result.all).toEqual(result.own);
    });

    it("does not include aliases", () => {
      const result = definePermissions("primitives", ["createCategory"] as const);
      expect(result).not.toHaveProperty("aliases");
    });
  });

  describe("with deps array shorthand", () => {
    it("combines own and deps into all", () => {
      const result = definePermissions("inventory", ["createItem", "updateItem"] as const, [
        "primitives:createCategory",
        "primitives:updateCategory",
      ]);

      expect(result.own).toEqual(["inventory:createItem", "inventory:updateItem"]);
      expect(result.all).toEqual([
        "inventory:createItem",
        "inventory:updateItem",
        "primitives:createCategory",
        "primitives:updateCategory",
      ]);
    });

    it("deduplicates when deps overlap with own", () => {
      const result = definePermissions("inventory", ["createItem"] as const, [
        "primitives:createCategory",
        "inventory:createItem",
      ]);

      expect(result.all).toEqual(["inventory:createItem", "primitives:createCategory"]);
    });
  });

  describe("with options object", () => {
    it("supports deps without aliases", () => {
      const result = definePermissions("inventory", ["createItem"] as const, {
        deps: ["primitives:createCategory"],
      });

      expect(result.all).toEqual(["inventory:createItem", "primitives:createCategory"]);
      expect(result).not.toHaveProperty("aliases");
    });

    it("supports aliases without deps", () => {
      const result = definePermissions(
        "primitives",
        ["createCategory", "activateCategory", "createUnit", "activateUnit"] as const,
        {
          aliases: {
            uom: ["createCategory", "activateCategory"],
            unit: ["createUnit", "activateUnit"],
          },
        },
      );

      expect(result.aliases.uom).toEqual([
        "primitives:createCategory",
        "primitives:activateCategory",
      ]);
      expect(result.aliases.unit).toEqual(["primitives:createUnit", "primitives:activateUnit"]);
    });

    it("supports both deps and aliases", () => {
      const result = definePermissions(
        "inventory",
        ["createItem", "updateItem", "viewItem"] as const,
        {
          deps: ["primitives:convertQuantity"],
          aliases: {
            write: ["createItem", "updateItem"],
            read: ["viewItem"],
          },
        },
      );

      expect(result.all).toContain("primitives:convertQuantity");
      expect(result.aliases.write).toEqual(["inventory:createItem", "inventory:updateItem"]);
      expect(result.aliases.read).toEqual(["inventory:viewItem"]);
    });
  });

  describe("diamond dependency deduplication", () => {
    it("deduplicates shared transitive deps", () => {
      const primitives = definePermissions("primitives", ["createCategory"] as const);

      const inventory = definePermissions("inventory", ["createItem"] as const, primitives.all);

      const product = definePermissions("product", ["createProduct"] as const, primitives.all);

      // manufacturing depends on both inventory and product
      const manufacturing = definePermissions("manufacturing", ["createWorkOrder"] as const, [
        ...inventory.all,
        ...product.all,
      ]);

      // primitives:createCategory should appear only once
      const primitivesCount = manufacturing.all.filter(
        (k) => k === "primitives:createCategory",
      ).length;
      expect(primitivesCount).toBe(1);

      expect(manufacturing.all).toEqual([
        "manufacturing:createWorkOrder",
        "inventory:createItem",
        "primitives:createCategory",
        "product:createProduct",
      ]);
    });
  });

  describe("type inference", () => {
    it("infers literal types for permission keys", () => {
      const result = definePermissions("primitives", ["createCategory", "updateCategory"] as const);

      // TypeScript verifies these are literal types at compile time
      const key: "primitives:createCategory" = result.permissions.createCategory;
      expect(key).toBe("primitives:createCategory");
    });
  });
});
