import { describe, it, expect, vi, beforeEach } from "vitest";
import { TextbooksController } from "./textbooks.controller.js";

vi.mock("./textbooks.service.js", () => ({
  TextbooksService: vi.fn(),
}));

const mockService = {
  create: vi.fn(),
  list: vi.fn(),
  getById: vi.fn(),
  update: vi.fn(),
  delete: vi.fn(),
};

describe("TextbooksController", () => {
  let controller: TextbooksController;

  beforeEach(() => {
    vi.clearAllMocks();
    controller = new TextbooksController(mockService as never);
  });

  describe("create", () => {
    it("should parse input and call service.create", async () => {
      mockService.create.mockResolvedValue({ id: "tb-1" });
      const result = await controller.create({
        title: "Math",
        subjectId: "subj-1",
        gradeId: "g-1",
      });
      expect(mockService.create).toHaveBeenCalledWith(
        expect.objectContaining({
          title: "Math",
          subjectId: "subj-1",
          gradeId: "g-1",
        }),
      );
      expect(result).toEqual({ success: true, data: { id: "tb-1" } });
    });

    it("should throw on invalid body", async () => {
      await expect(controller.create({ title: "" })).rejects.toThrow();
    });
  });

  describe("list", () => {
    it("should parse query and call service.list", async () => {
      const data = [{ id: "tb-1" }];
      mockService.list.mockResolvedValue(data);
      const result = await controller.list({ page: "2", pageSize: "10" });
      expect(mockService.list).toHaveBeenCalledWith(
        expect.objectContaining({ page: 2, pageSize: 10 }),
      );
      expect(result).toEqual({ success: true, data });
    });
  });

  describe("getById", () => {
    it("should call service.getById", async () => {
      const data = { id: "tb-1", title: "T" };
      mockService.getById.mockResolvedValue(data);
      const result = await controller.getById("tb-1");
      expect(mockService.getById).toHaveBeenCalledWith("tb-1");
      expect(result).toEqual({ success: true, data });
    });
  });

  describe("update", () => {
    it("should parse input and call service.update", async () => {
      const result = await controller.update("tb-1", { title: "New" });
      expect(mockService.update).toHaveBeenCalledWith("tb-1", { title: "New" });
      expect(result).toEqual({ success: true, data: { success: true } });
    });

    it("should throw on invalid status", async () => {
      await expect(
        controller.update("tb-1", { status: "invalid" }),
      ).rejects.toThrow();
    });
  });

  describe("remove", () => {
    it("should call service.delete", async () => {
      const result = await controller.remove("tb-1");
      expect(mockService.delete).toHaveBeenCalledWith("tb-1");
      expect(result).toEqual({ success: true, data: { success: true } });
    });
  });
});
