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

const mockGetDb = vi.fn();

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

import { TextbooksRepository } from "./textbooks.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("TextbooksRepository", () => {
  let repo: TextbooksRepository;

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

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

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

  it("find should apply query filters and pagination", async () => {
    const tbs = [{ id: "tb-1" }];
    mockGetDb.mockReturnValue(createMockDb(tbs));
    const result = await repo.find({
      subjectId: "subj-1",
      gradeId: "g-1",
      page: 2,
      pageSize: 10,
    });
    expect(result).toBe(tbs);
  });

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

  it("create should insert a new textbook", async () => {
    mockGetDb.mockReturnValue(createMockDb(undefined));
    await repo.create({
      id: "tb-1",
      title: "Math",
      subjectId: "subj-1",
      gradeId: "g-1",
      version: "1.0",
      status: "draft",
    });
    expect(mockGetDb).toHaveBeenCalled();
  });

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

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