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

  // -------------------------------------------------------------------
  // fetch
  // -------------------------------------------------------------------
  describe("fetch()", () => {
    test("returns CredentialController when found", async () => {
      const mockData = buildMockCredential();
      mock.module("../../src/constants", () =>
        buildMockConstants({
          prisma: createStablePrismaMock({
            credential: {
              findFirst: mock(() => Promise.resolve(mockData)),
            },
          }),
        }),
      );

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

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

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

  // -------------------------------------------------------------------
  // fetchByCompany
  // -------------------------------------------------------------------
  describe("fetchByCompany()", () => {
    test("returns array of CredentialControllers", async () => {
      const mockData = [buildMockCredential()];
      mock.module("../../src/constants", () =>
        buildMockConstants({
          prisma: createStablePrismaMock({
            credential: {
              findMany: mock(() => Promise.resolve(mockData)),
              findFirst: mock(() => Promise.resolve(null)),
            },
          }),
        }),
      );

      const { credentials } = await import("../../src/managers/credentials");
      const result = await credentials.fetchByCompany("company-1");
      expect(result).toBeArrayOfSize(1);
    });

    test("returns empty array when no credentials exist", async () => {
      mock.module("../../src/constants", () =>
        buildMockConstants({
          prisma: createStablePrismaMock({
            credential: {
              findMany: mock(() => Promise.resolve([])),
              findFirst: mock(() => Promise.resolve(null)),
            },
          }),
        }),
      );

      const { credentials } = await import("../../src/managers/credentials");
      const result = await credentials.fetchByCompany("company-x");
      expect(result).toBeArrayOfSize(0);
    });
  });

  // -------------------------------------------------------------------
  // fetchSubCredentials
  // -------------------------------------------------------------------
  describe("fetchSubCredentials()", () => {
    test("returns sub-credentials for parent", async () => {
      const mockData = [
        buildMockCredential({ id: "sub-1", subCredentialOfId: "cred-1" }),
      ];
      mock.module("../../src/constants", () =>
        buildMockConstants({
          prisma: createStablePrismaMock({
            credential: {
              findMany: mock(() => Promise.resolve(mockData)),
              findFirst: mock(() => Promise.resolve(null)),
            },
          }),
        }),
      );

      const { credentials } = await import("../../src/managers/credentials");
      const result = await credentials.fetchSubCredentials("cred-1");
      expect(result).toBeArrayOfSize(1);
    });
  });

  // -------------------------------------------------------------------
  // delete
  // -------------------------------------------------------------------
  describe("delete()", () => {
    test("deletes credential by id", async () => {
      const deleteMock = mock(() => Promise.resolve({}));
      mock.module("../../src/constants", () =>
        buildMockConstants({
          prisma: createStablePrismaMock({
            credential: {
              delete: deleteMock,
              findFirst: mock(() => Promise.resolve(null)),
            },
          }),
        }),
      );

      const { credentials } = await import("../../src/managers/credentials");
      await credentials.delete("cred-1");
      expect(deleteMock).toHaveBeenCalledWith({ where: { id: "cred-1" } });
    });
  });

  // -------------------------------------------------------------------
  // removeSubCredential
  // -------------------------------------------------------------------
  describe("removeSubCredential()", () => {
    test("throws when sub-credential not found", async () => {
      mock.module("../../src/constants", () =>
        buildMockConstants({
          prisma: createStablePrismaMock({
            credential: {
              findFirst: mock(() => Promise.resolve(null)),
            },
          }),
        }),
      );

      const { credentials } = await import("../../src/managers/credentials");
      try {
        await credentials.removeSubCredential("parent-1", "sub-999");
        expect(true).toBe(false);
      } catch (e: any) {
        expect(e.name).toBe("SubCredentialNotFound");
        expect(e.status).toBe(404);
      }
    });
  });
});
