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

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

const mockService = {
  createChapter: vi.fn(),
  listChaptersByTextbook: vi.fn(),
  getChapter: vi.fn(),
  updateChapter: vi.fn(),
  deleteChapter: vi.fn(),
};

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

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

  describe("create", () => {
    it("should parse input and call service.createChapter", async () => {
      mockService.createChapter.mockResolvedValue({ id: "ch-1" });
      const result = await controller.create({
        textbookId: "tb-1",
        title: "Chapter One",
      });
      expect(mockService.createChapter).toHaveBeenCalledWith(
        expect.objectContaining({
          textbookId: "tb-1",
          title: "Chapter One",
          order: 0,
        }),
      );
      expect(result).toEqual({ success: true, data: { id: "ch-1" } });
    });

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

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

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

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

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

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