import { describe, test, expect, mock, beforeEach } from "bun:test";

// ---------------------------------------------------------------------------
// 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)) });
      },
    },
  );
}

function buildMockCwMemberRecord(overrides: Record<string, any> = {}) {
  return {
    id: "member-1",
    cwMemberId: 42,
    identifier: "jdoe",
    firstName: "John",
    lastName: "Doe",
    officeEmail: "jdoe@example.com",
    inactiveFlag: false,
    apiKey: null,
    cwLastUpdated: new Date("2026-02-01"),
    createdAt: new Date("2026-01-01"),
    updatedAt: new Date("2026-02-01"),
    ...overrides,
  };
}

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

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

  // -------------------------------------------------------------------
  // fetch
  // -------------------------------------------------------------------
  describe("fetch()", () => {
    test("returns CwMemberController by identifier string", async () => {
      const record = buildMockCwMemberRecord();
      mock.module("../../src/constants", () => ({
        prisma: createStablePrismaMock({
          cwMember: {
            findFirst: mock(() => Promise.resolve(record)),
          },
        }),
      }));

      const { cwMembers } = await import("../../src/managers/cwMembers");
      const result = await cwMembers.fetch("jdoe");
      expect(result).toBeDefined();
      expect(result.identifier).toBe("jdoe");
    });

    test("treats numeric string as cwMemberId lookup", async () => {
      const record = buildMockCwMemberRecord();
      const findFirst = mock(() => Promise.resolve(record));
      mock.module("../../src/constants", () => ({
        prisma: createStablePrismaMock({
          cwMember: { findFirst },
        }),
      }));

      const { cwMembers } = await import("../../src/managers/cwMembers");
      await cwMembers.fetch("42");
      const where = findFirst.mock.calls[0]?.[0]?.where;
      expect(where).toHaveProperty("cwMemberId", 42);
    });

    test("throws 404 when not found", async () => {
      mock.module("../../src/constants", () => ({
        prisma: createStablePrismaMock({
          cwMember: {
            findFirst: mock(() => Promise.resolve(null)),
          },
        }),
      }));

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

  // -------------------------------------------------------------------
  // fetchAll
  // -------------------------------------------------------------------
  describe("fetchAll()", () => {
    test("returns active members by default", async () => {
      const records = [buildMockCwMemberRecord()];
      const findMany = mock(() => Promise.resolve(records));
      mock.module("../../src/constants", () => ({
        prisma: createStablePrismaMock({
          cwMember: {
            findMany,
            findFirst: mock(() => Promise.resolve(null)),
          },
        }),
      }));

      const { cwMembers } = await import("../../src/managers/cwMembers");
      const result = await cwMembers.fetchAll();
      expect(result).toBeArrayOfSize(1);
      // Should filter inactive by default
      const where = findMany.mock.calls[0]?.[0]?.where;
      expect(where).toHaveProperty("inactiveFlag", false);
    });

    test("includes inactive when requested", async () => {
      const findMany = mock(() => Promise.resolve([]));
      mock.module("../../src/constants", () => ({
        prisma: createStablePrismaMock({
          cwMember: {
            findMany,
            findFirst: mock(() => Promise.resolve(null)),
          },
        }),
      }));

      const { cwMembers } = await import("../../src/managers/cwMembers");
      await cwMembers.fetchAll({ includeInactive: true });
      const where = findMany.mock.calls[0]?.[0]?.where;
      expect(where).toEqual({});
    });
  });

  // -------------------------------------------------------------------
  // count
  // -------------------------------------------------------------------
  describe("count()", () => {
    test("returns count of active members by default", async () => {
      const countMock = mock(() => Promise.resolve(10));
      mock.module("../../src/constants", () => ({
        prisma: createStablePrismaMock({
          cwMember: {
            count: countMock,
            findFirst: mock(() => Promise.resolve(null)),
          },
        }),
      }));

      const { cwMembers } = await import("../../src/managers/cwMembers");
      const result = await cwMembers.count();
      expect(result).toBe(10);
    });
  });

  // -------------------------------------------------------------------
  // updateApiKey
  // -------------------------------------------------------------------
  describe("updateApiKey()", () => {
    test("updates API key for a member", async () => {
      const record = buildMockCwMemberRecord();
      const updatedRecord = { ...record, apiKey: "new-key" };
      mock.module("../../src/constants", () => ({
        prisma: createStablePrismaMock({
          cwMember: {
            findFirst: mock(() => Promise.resolve(record)),
            update: mock(() => Promise.resolve(updatedRecord)),
          },
        }),
      }));

      const { cwMembers } = await import("../../src/managers/cwMembers");
      const result = await cwMembers.updateApiKey("jdoe", "new-key");
      expect(result.apiKey).toBe("new-key");
    });
  });
});
