import { describe, it, expect, vi, beforeEach } from "vitest";
import { KnowledgePointsController } from "./knowledge-points.controller.js";

vi.mock("./knowledge-points.service.js", () => ({
  KnowledgePointsService: vi.fn(),
}));

const mockService = {
  createKnowledgePoint: vi.fn(),
  listByChapter: vi.fn(),
  getPrerequisites: vi.fn(),
  getKnowledgePoint: vi.fn(),
  addPrerequisite: vi.fn(),
  removePrerequisite: vi.fn(),
  updateKnowledgePoint: vi.fn(),
  deleteKnowledgePoint: vi.fn(),
};

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

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

  describe("create", () => {
    it("should parse input and call service.createKnowledgePoint", async () => {
      mockService.createKnowledgePoint.mockResolvedValue({ id: "kp-1" });
      const result = await controller.create({
        chapterId: "ch-1",
        title: "Algebra Basics",
      });
      expect(mockService.createKnowledgePoint).toHaveBeenCalledWith(
        expect.objectContaining({
          chapterId: "ch-1",
          title: "Algebra Basics",
          difficulty: 3,
        }),
      );
      expect(result).toEqual({ success: true, data: { id: "kp-1" } });
    });

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

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

  describe("getPrerequisites", () => {
    it("should call service.getPrerequisites", async () => {
      const data = [{ id: "kp-pre", title: "Pre" }];
      mockService.getPrerequisites.mockResolvedValue(data);
      const result = await controller.getPrerequisites("kp-1");
      expect(mockService.getPrerequisites).toHaveBeenCalledWith("kp-1");
      expect(result).toEqual({ success: true, data });
    });
  });

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

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

    it("should throw on invalid body", async () => {
      await expect(
        controller.addPrerequisite("kp-1", { prerequisiteId: "" }),
      ).rejects.toThrow();
    });
  });

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

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

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