import { describe, test, expect, mock, beforeEach } from "bun:test";
import { GeneratedQuoteController } from "../../src/controllers/GeneratedQuoteController";
import {
  buildMockGeneratedQuote,
  buildMockOpportunity,
  buildMockUser,
} from "../setup";

/**
 * The Proxy-based prisma mock in setup.ts creates a fresh mock() for every
 * property access, so we cannot use `(prisma.x.findFirst as any).mockReturnValueOnce()`
 * because the manager's import access gets a different mock.
 *
 * Instead, we mock the entire constants module per-test with stable mock functions.
 */

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() {
              return mock(() => Promise.resolve(null));
            },
          },
        );
      },
    },
  );
}

describe("generatedQuotes manager", () => {
  // -------------------------------------------------------------------
  // fetch
  // -------------------------------------------------------------------
  describe("fetch()", () => {
    test("returns a GeneratedQuoteController when found", async () => {
      const mockData = buildMockGeneratedQuote();
      const findFirst = mock(() => Promise.resolve(mockData));
      mock.module("../../src/constants", () => ({
        prisma: createStablePrismaMock({
          generatedQuotes: { findFirst },
        }),
      }));

      // Re-import to pick up the fresh mock
      const { generatedQuotes } =
        await import("../../src/managers/generatedQuotes");
      const result = await generatedQuotes.fetch("quote-1");
      expect(result).toBeInstanceOf(GeneratedQuoteController);
      expect(result.id).toBe("quote-1");
    });

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

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

  // -------------------------------------------------------------------
  // fetchByOpportunity
  // -------------------------------------------------------------------
  describe("fetchByOpportunity()", () => {
    test("returns array of GeneratedQuoteController", async () => {
      const rows = [
        buildMockGeneratedQuote({ id: "q-1" }),
        buildMockGeneratedQuote({ id: "q-2" }),
      ];
      const findMany = mock(() => Promise.resolve(rows));
      mock.module("../../src/constants", () => ({
        prisma: createStablePrismaMock({
          generatedQuotes: { findMany },
        }),
      }));

      const { generatedQuotes } =
        await import("../../src/managers/generatedQuotes");
      const result = await generatedQuotes.fetchByOpportunity("opp-1");
      expect(result).toHaveLength(2);
      expect(result[0]).toBeInstanceOf(GeneratedQuoteController);
      expect(result[0].id).toBe("q-1");
    });

    test("returns empty array when none found", async () => {
      const findMany = mock(() => Promise.resolve([]));
      mock.module("../../src/constants", () => ({
        prisma: createStablePrismaMock({
          generatedQuotes: { findMany },
        }),
      }));

      const { generatedQuotes } =
        await import("../../src/managers/generatedQuotes");
      const result = await generatedQuotes.fetchByOpportunity("opp-999");
      expect(result).toEqual([]);
    });
  });

  // -------------------------------------------------------------------
  // fetchByCreator
  // -------------------------------------------------------------------
  describe("fetchByCreator()", () => {
    test("returns array of GeneratedQuoteController", async () => {
      const rows = [buildMockGeneratedQuote({ id: "q-1" })];
      const findMany = mock(() => Promise.resolve(rows));
      mock.module("../../src/constants", () => ({
        prisma: createStablePrismaMock({
          generatedQuotes: { findMany },
        }),
      }));

      const { generatedQuotes } =
        await import("../../src/managers/generatedQuotes");
      const result = await generatedQuotes.fetchByCreator("user-1");
      expect(result).toHaveLength(1);
      expect(result[0]).toBeInstanceOf(GeneratedQuoteController);
    });
  });

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

      const { generatedQuotes } =
        await import("../../src/managers/generatedQuotes");
      try {
        await generatedQuotes.create({
          quoteRegenData: {},
          quoteFile: Buffer.from("pdf"),
          quoteFileName: "test.pdf",
          opportunityId: "nonexistent-opp",
          createdById: "user-1",
        });
        expect(true).toBe(false);
      } catch (e: any) {
        expect(e.name).toBe("OpportunityNotFound");
        expect(e.status).toBe(404);
      }
    });

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

      const { generatedQuotes } =
        await import("../../src/managers/generatedQuotes");
      try {
        await generatedQuotes.create({
          quoteRegenData: {},
          quoteFile: Buffer.from("pdf"),
          quoteFileName: "test.pdf",
          opportunityId: "opp-1",
          createdById: "nonexistent-user",
        });
        expect(true).toBe(false);
      } catch (e: any) {
        expect(e.name).toBe("UserNotFound");
        expect(e.status).toBe(404);
      }
    });

    test("creates and returns GeneratedQuoteController", async () => {
      const mockQuote = buildMockGeneratedQuote();
      mock.module("../../src/constants", () => ({
        prisma: createStablePrismaMock({
          opportunity: {
            findFirst: mock(() => Promise.resolve({ id: "opp-1" })),
          },
          user: { findFirst: mock(() => Promise.resolve({ id: "user-1" })) },
          generatedQuotes: { create: mock(() => Promise.resolve(mockQuote)) },
        }),
      }));

      const { generatedQuotes } =
        await import("../../src/managers/generatedQuotes");
      const result = await generatedQuotes.create({
        quoteRegenData: { theme: "default" },
        quoteFile: Buffer.from("pdf-content"),
        quoteFileName: "Quote.pdf",
        opportunityId: "opp-1",
        createdById: "user-1",
      });

      expect(result).toBeInstanceOf(GeneratedQuoteController);
      expect(result.id).toBe("quote-1");
    });
  });

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

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

    test("deletes successfully when quote exists", async () => {
      mock.module("../../src/constants", () => ({
        prisma: createStablePrismaMock({
          generatedQuotes: {
            findFirst: mock(() => Promise.resolve({ id: "quote-1" })),
            delete: mock(() => Promise.resolve(undefined)),
          },
        }),
      }));

      const { generatedQuotes } =
        await import("../../src/managers/generatedQuotes");
      await expect(generatedQuotes.delete("quote-1")).resolves.toBeUndefined();
    });
  });
});
