import { describe, test, expect, mock, beforeEach } from "bun:test";
import { buildMockRole, buildMockUser, buildMockConstants } from "../setup";

// ---------------------------------------------------------------------------
// Stable mock factory
// ---------------------------------------------------------------------------

function createStablePrismaMock(
  overrides: Record<string, Record<string, any>> = {},
) {
  return new Proxy(
    {},
    {
      get(_target, model: string) {
        if (model === "$connect" || model === "$disconnect")
          return mock(() => Promise.resolve());
        if (overrides[model]) return overrides[model];
        return new Proxy({}, { get: () => mock(() => Promise.resolve(null)) });
      },
    },
  );
}

// ---------------------------------------------------------------------------
// Tests
// ---------------------------------------------------------------------------

describe("roles manager", () => {
  beforeEach(() => {
    mock.restore();
  });

  // -------------------------------------------------------------------
  // fetch
  // -------------------------------------------------------------------
  describe("fetch()", () => {
    test("returns RoleController when found by id", async () => {
      const mockData = { ...buildMockRole(), users: [] };
      mock.module("../../src/constants", () =>
        buildMockConstants({
          prisma: createStablePrismaMock({
            role: {
              findFirst: mock(() => Promise.resolve(mockData)),
            },
          }),
          permissionsPrivateKey: "test-key",
        }),
      );

      const { roles } = await import("../../src/managers/roles");
      const result = await roles.fetch("role-1");
      expect(result).toBeDefined();
      expect(result.id).toBe("role-1");
    });

    test("throws UnknownRole when not found", async () => {
      mock.module("../../src/constants", () =>
        buildMockConstants({
          prisma: createStablePrismaMock({
            role: {
              findFirst: mock(() => Promise.resolve(null)),
            },
          }),
          permissionsPrivateKey: "test-key",
        }),
      );

      const { roles } = await import("../../src/managers/roles");
      try {
        await roles.fetch("nonexistent");
        expect(true).toBe(false);
      } catch (e: any) {
        expect(e.name).toBe("UnknownRole");
        expect(e.status).toBe(404);
      }
    });
  });

  // -------------------------------------------------------------------
  // fetchAllRoles
  // -------------------------------------------------------------------
  describe("fetchAllRoles()", () => {
    test("returns a Collection of role controllers", async () => {
      const roles1 = [
        { ...buildMockRole(), users: [] },
        { ...buildMockRole({ id: "role-2", moniker: "admin" }), users: [] },
      ];
      mock.module("../../src/constants", () =>
        buildMockConstants({
          prisma: createStablePrismaMock({
            role: {
              findMany: mock(() => Promise.resolve(roles1)),
              findFirst: mock(() => Promise.resolve(null)),
            },
          }),
          permissionsPrivateKey: "test-key",
        }),
      );

      const { roles } = await import("../../src/managers/roles");
      const collection = await roles.fetchAllRoles();
      expect(collection.size).toBe(2);
    });
  });

  // -------------------------------------------------------------------
  // create
  // -------------------------------------------------------------------
  describe("create()", () => {
    test("throws when moniker is already taken", async () => {
      mock.module("../../src/constants", () =>
        buildMockConstants({
          prisma: createStablePrismaMock({
            role: {
              findFirst: mock(() => Promise.resolve(buildMockRole())),
            },
          }),
          permissionsPrivateKey: "test-key",
        }),
      );

      const { roles } = await import("../../src/managers/roles");
      try {
        await roles.create({
          title: "Test Role",
          moniker: "test-role",
        });
        expect(true).toBe(false);
      } catch (e: any) {
        expect(e.message).toContain("Moniker is already taken");
      }
    });

    test("validates input with Zod", async () => {
      mock.module("../../src/constants", () =>
        buildMockConstants({
          prisma: createStablePrismaMock({
            role: {
              findFirst: mock(() => Promise.resolve(null)),
            },
          }),
          permissionsPrivateKey: "test-key",
        }),
      );

      const { roles } = await import("../../src/managers/roles");
      try {
        await roles.create({
          title: "",
          moniker: "test",
        });
        expect(true).toBe(false);
      } catch (e: any) {
        // Zod should reject empty title
        expect(e).toBeDefined();
      }
    });
  });

  // -------------------------------------------------------------------
  // _buildPermissionNode
  // -------------------------------------------------------------------
  describe("_buildPermissionNode()", () => {
    test("builds correct permission node format", async () => {
      mock.module("../../src/constants", () =>
        buildMockConstants({
          prisma: createStablePrismaMock(),
          permissionsPrivateKey: "test-key",
        }),
      );

      const { roles } = await import("../../src/managers/roles");
      expect(roles._buildPermissionNode("role-1", "read")).toBe(
        "roles.role-1.read",
      );
      expect(roles._buildPermissionNode("role-2", "write")).toBe(
        "roles.role-2.write",
      );
    });
  });
});
