import { describe, it, expect, vi, beforeEach } from "vitest";

const mockGetDb = vi.fn();

vi.mock("../config/database.js", () => ({
  getDb: () => mockGetDb(),
}));

import { QuestionsRepository } from "./questions.repository.js";

function createMockDb(resolvedValue: unknown): unknown {
  const handler: ProxyHandler<Record<PropertyKey, unknown>> = {
    get: (_target, prop) => {
      if (prop === "then") {
        return (onFulfilled?: (v: unknown) => unknown) =>
          Promise.resolve(
            typeof onFulfilled === "function"
              ? onFulfilled(resolvedValue)
              : resolvedValue,
          );
      }
      return () => new Proxy({}, handler);
    },
  };
  return new Proxy({}, handler);
}

describe("QuestionsRepository", () => {
  let repo: QuestionsRepository;

  beforeEach(() => {
    vi.clearAllMocks();
    repo = new QuestionsRepository();
  });

  it("findById should return first matching question", async () => {
    const q = { id: "q-1", content: "Q" };
    mockGetDb.mockReturnValue(createMockDb([q]));
    const result = await repo.findById("q-1");
    expect(result).toBe(q);
  });

  it("findById should return undefined when no match", async () => {
    mockGetDb.mockReturnValue(createMockDb([]));
    const result = await repo.findById("missing");
    expect(result).toBeUndefined();
  });

  it("findByKnowledgePointId should return questions array", async () => {
    const qs = [{ id: "q-1" }];
    mockGetDb.mockReturnValue(createMockDb(qs));
    const result = await repo.findByKnowledgePointId("kp-1");
    expect(result).toBe(qs);
  });

  it("find should apply query filters and pagination", async () => {
    const qs = [{ id: "q-1" }];
    mockGetDb.mockReturnValue(createMockDb(qs));
    const result = await repo.find({
      knowledgePointId: "kp-1",
      type: "single_choice",
      difficulty: 3,
      status: "published",
      page: 2,
      pageSize: 10,
    });
    expect(result).toBe(qs);
  });

  it("find should use defaults when query is empty", async () => {
    const qs: unknown[] = [];
    mockGetDb.mockReturnValue(createMockDb(qs));
    const result = await repo.find();
    expect(result).toBe(qs);
  });

  it("create should insert a new question", async () => {
    mockGetDb.mockReturnValue(createMockDb(undefined));
    await repo.create({
      id: "q-1",
      knowledgePointId: "kp-1",
      type: "single_choice",
      content: "Q",
      answer: "A",
      difficulty: 3,
      status: "draft",
      source: "manual",
      createdBy: "user-1",
    });
    expect(mockGetDb).toHaveBeenCalled();
  });

  it("update should update a question by id", async () => {
    mockGetDb.mockReturnValue(createMockDb(undefined));
    await repo.update("q-1", { content: "New" });
    expect(mockGetDb).toHaveBeenCalled();
  });

  it("delete should delete a question by id", async () => {
    mockGetDb.mockReturnValue(createMockDb(undefined));
    await repo.delete("q-1");
    expect(mockGetDb).toHaveBeenCalled();
  });
});
