import { describe, it, expect, beforeEach, vi, afterEach } from "vitest";
import { EmbeddingService } from "./embedding.service";
import { PrismaService } from "../../prisma/prisma.service";

// Mock OpenAI with a proper class
const mockEmbeddingsCreate = vi.fn();
vi.mock("openai", () => {
  return {
    default: class MockOpenAI {
      embeddings = {
        create: mockEmbeddingsCreate,
      };
    },
  };
});

describe("EmbeddingService", () => {
  let service: EmbeddingService;
  let prismaService: PrismaService;
  let originalEnv: string | undefined;

  beforeEach(() => {
    // Store original env
    originalEnv = process.env.OPENAI_API_KEY;

    prismaService = {
      $executeRaw: vi.fn(),
      knowledgeEmbedding: {
        deleteMany: vi.fn(),
      },
    } as unknown as PrismaService;

    // Clear mock call history
    vi.clearAllMocks();
  });

  afterEach(() => {
    // Restore original env
    if (originalEnv) {
      process.env.OPENAI_API_KEY = originalEnv;
    } else {
      delete process.env.OPENAI_API_KEY;
    }
  });

  describe("constructor", () => {
    it("should not instantiate OpenAI client when API key is missing", () => {
      delete process.env.OPENAI_API_KEY;

      service = new EmbeddingService(prismaService);

      // Verify service is not configured (client is null)
      expect(service.isConfigured()).toBe(false);
    });

    it("should instantiate OpenAI client when API key is provided", () => {
      process.env.OPENAI_API_KEY = "test-api-key";

      service = new EmbeddingService(prismaService);

      // Verify service is configured (client is not null)
      expect(service.isConfigured()).toBe(true);
    });
  });

  // Default service setup (without API key) for remaining tests
  function createServiceWithoutKey(): EmbeddingService {
    delete process.env.OPENAI_API_KEY;
    return new EmbeddingService(prismaService);
  }

  describe("isConfigured", () => {
    it("should return false when OPENAI_API_KEY is not set", () => {
      service = createServiceWithoutKey();

      expect(service.isConfigured()).toBe(false);
    });

    it("should return true when OPENAI_API_KEY is set", () => {
      process.env.OPENAI_API_KEY = "test-key";
      service = new EmbeddingService(prismaService);

      expect(service.isConfigured()).toBe(true);
    });
  });

  describe("prepareContentForEmbedding", () => {
    beforeEach(() => {
      service = createServiceWithoutKey();
    });

    it("should combine title and content with title weighting", () => {
      const title = "Test Title";
      const content = "Test content goes here";

      const result = service.prepareContentForEmbedding(title, content);

      expect(result).toContain(title);
      expect(result).toContain(content);
      // Title should appear twice for weighting
      expect(result.split(title).length - 1).toBe(2);
    });

    it("should handle empty content", () => {
      const title = "Test Title";
      const content = "";

      const result = service.prepareContentForEmbedding(title, content);

      expect(result).toBe(`${title}\n\n${title}`);
    });
  });

  describe("generateAndStoreEmbedding", () => {
    it("should skip generation when not configured", async () => {
      service = createServiceWithoutKey();

      await service.generateAndStoreEmbedding("test-id", "test content");

      expect(prismaService.$executeRaw).not.toHaveBeenCalled();
    });
  });

  describe("deleteEmbedding", () => {
    beforeEach(() => {
      service = createServiceWithoutKey();
    });

    it("should delete embedding for entry", async () => {
      const entryId = "test-entry-id";

      await service.deleteEmbedding(entryId);

      expect(prismaService.knowledgeEmbedding.deleteMany).toHaveBeenCalledWith({
        where: { entryId },
      });
    });
  });

  describe("batchGenerateEmbeddings", () => {
    it("should return 0 when not configured", async () => {
      service = createServiceWithoutKey();

      const entries = [
        { id: "1", content: "content 1" },
        { id: "2", content: "content 2" },
      ];

      const result = await service.batchGenerateEmbeddings(entries);

      expect(result).toBe(0);
    });
  });

  describe("generateEmbedding", () => {
    it("should throw error when not configured", async () => {
      service = createServiceWithoutKey();

      await expect(service.generateEmbedding("test text")).rejects.toThrow(
        "OPENAI_API_KEY not configured"
      );
    });
  });
});
