import { describe, test, expect, mock, beforeEach } from "bun:test";
import { buildMockCompany } 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("companies manager", () => {
  beforeEach(() => {
    mock.restore();
  });

  // -------------------------------------------------------------------
  // count
  // -------------------------------------------------------------------
  describe("count()", () => {
    test("returns company count from Prisma", async () => {
      const countMock = mock(() => Promise.resolve(5));
      mock.module("../../src/constants", () => ({
        prisma: createStablePrismaMock({
          company: {
            count: countMock,
            findFirst: mock(() => Promise.resolve(null)),
          },
        }),
        connectWiseApi: {
          get: mock(() => Promise.resolve({ data: {} })),
        },
      }));

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

  // -------------------------------------------------------------------
  // fetchPages
  // -------------------------------------------------------------------
  describe("fetchPages()", () => {
    test("returns paginated companies", async () => {
      const mockData = [
        buildMockCompany(),
        buildMockCompany({ id: "company-2" }),
      ];
      const findManyMock = mock(() => Promise.resolve(mockData));
      mock.module("../../src/constants", () => ({
        prisma: createStablePrismaMock({
          company: {
            findMany: findManyMock,
            findFirst: mock(() => Promise.resolve(null)),
          },
        }),
        connectWiseApi: {
          get: mock(() => Promise.resolve({ data: {} })),
        },
      }));

      const { companies } = await import("../../src/managers/companies");
      const result = await companies.fetchPages(1, 10);
      expect(result).toBeArrayOfSize(2);
    });

    test("uses correct skip for page 1", async () => {
      const findManyMock = mock(() => Promise.resolve([]));
      mock.module("../../src/constants", () => ({
        prisma: createStablePrismaMock({
          company: {
            findMany: findManyMock,
            findFirst: mock(() => Promise.resolve(null)),
          },
        }),
        connectWiseApi: {
          get: mock(() => Promise.resolve({ data: {} })),
        },
      }));

      const { companies } = await import("../../src/managers/companies");
      await companies.fetchPages(1, 20);
      expect(findManyMock).toHaveBeenCalled();
    });
  });

  // -------------------------------------------------------------------
  // search
  // -------------------------------------------------------------------
  describe("search()", () => {
    test("returns matching companies", async () => {
      const mockData = [buildMockCompany()];
      const findManyMock = mock(() => Promise.resolve(mockData));
      mock.module("../../src/constants", () => ({
        prisma: createStablePrismaMock({
          company: {
            findMany: findManyMock,
            findFirst: mock(() => Promise.resolve(null)),
          },
        }),
        connectWiseApi: {
          get: mock(() => Promise.resolve({ data: {} })),
        },
      }));

      const { companies } = await import("../../src/managers/companies");
      const result = await companies.search("Test", 1, 10);
      expect(result).toBeArrayOfSize(1);
    });
  });

  // -------------------------------------------------------------------
  // fetch
  // -------------------------------------------------------------------
  describe("fetch()", () => {
    test("throws when company not found in DB", async () => {
      mock.module("../../src/constants", () => ({
        prisma: createStablePrismaMock({
          company: { findFirst: mock(() => Promise.resolve(null)) },
        }),
        connectWiseApi: {
          get: mock(() => Promise.resolve({ data: {} })),
        },
      }));

      const { companies } = await import("../../src/managers/companies");
      try {
        await companies.fetch("nonexistent");
        expect(true).toBe(false);
      } catch (e: any) {
        expect(e.message).toBe("Unknown company.");
      }
    });

    test("returns CompanyController on success", async () => {
      const dbCompany = buildMockCompany();
      const cwCompanyData = {
        defaultContact: { _info: { contact_href: "/contacts/1" } },
        _info: { contacts_href: "/contacts?page=1" },
      };

      mock.module("../../src/constants", () => ({
        prisma: createStablePrismaMock({
          company: { findFirst: mock(() => Promise.resolve(dbCompany)) },
        }),
        connectWiseApi: {
          get: mock(() =>
            Promise.resolve({
              data: cwCompanyData,
            }),
          ),
        },
      }));

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