import { describe, it, expect, vi, beforeEach } from "vitest";
import { KnowledgePointsService } from "./knowledge-points.service.js";
import {
  NotFoundError,
  ValidationError,
} from "../shared/errors/application-error.js";

vi.mock("./knowledge-points.repository.js", () => ({
  knowledgePointsRepository: {
    findById: vi.fn(),
    findByChapterId: vi.fn(),
    create: vi.fn(),
    update: vi.fn(),
    delete: vi.fn(),
  },
}));

vi.mock("../config/neo4j.js", () => ({
  getNeo4jSession: vi.fn().mockReturnValue(null),
}));

import { knowledgePointsRepository } from "./knowledge-points.repository.js";

const mockOutbox = {
  publish: vi.fn().mockResolvedValue("event-id"),
};

describe("KnowledgePointsService", () => {
  let service: KnowledgePointsService;

  beforeEach(() => {
    vi.clearAllMocks();
    service = new KnowledgePointsService(mockOutbox as never);
  });

  describe("createKnowledgePoint", () => {
    it("should create a KP and publish event (no direct Neo4j write)", async () => {
      const input = {
        chapterId: "ch-1",
        title: "Algebra Basics",
        description: "Intro to algebra",
        difficulty: 3,
      };
      const result = await service.createKnowledgePoint(input);

      expect(result.id).toBeDefined();
      expect(knowledgePointsRepository.create).toHaveBeenCalledWith(
        expect.objectContaining({
          chapterId: "ch-1",
          title: "Algebra Basics",
          difficulty: 3,
        }),
      );
      expect(mockOutbox.publish).toHaveBeenCalledWith(
        "knowledge_point.created",
        "KnowledgePoint",
        result.id,
        expect.objectContaining({
          chapter_id: "ch-1",
          title: "Algebra Basics",
        }),
      );
    });

    it("should default difficulty to 3", async () => {
      await service.createKnowledgePoint({
        chapterId: "ch-1",
        title: "Test KP",
      });
      expect(knowledgePointsRepository.create).toHaveBeenCalledWith(
        expect.objectContaining({ difficulty: 3 }),
      );
    });
  });

  describe("getKnowledgePoint", () => {
    it("should throw NotFoundError when not found", async () => {
      vi.mocked(knowledgePointsRepository.findById).mockResolvedValue(
        undefined,
      );
      await expect(service.getKnowledgePoint("nope")).rejects.toThrow(
        NotFoundError,
      );
    });
  });

  describe("addPrerequisite", () => {
    it("should throw ValidationError when id === prerequisiteId", async () => {
      await expect(service.addPrerequisite("kp-1", "kp-1")).rejects.toThrow(
        ValidationError,
      );
    });

    it("should publish prerequisite_added event", async () => {
      const mockKp = {
        id: "kp-1",
        chapterId: "ch-1",
        title: "KP1",
        description: null,
        difficulty: 3,
        metadata: null,
        createdAt: new Date(),
        updatedAt: new Date(),
      };
      vi.mocked(knowledgePointsRepository.findById).mockResolvedValue(mockKp);

      await service.addPrerequisite("kp-1", "kp-2");

      expect(mockOutbox.publish).toHaveBeenCalledWith(
        "knowledge_point.prerequisite_added",
        "KnowledgePoint",
        "kp-1",
        { prerequisite_id: "kp-2" },
      );
    });
  });

  describe("removePrerequisite", () => {
    it("should publish prerequisite_removed event", async () => {
      const mockKp = {
        id: "kp-1",
        chapterId: "ch-1",
        title: "KP1",
        description: null,
        difficulty: 3,
        metadata: null,
        createdAt: new Date(),
        updatedAt: new Date(),
      };
      vi.mocked(knowledgePointsRepository.findById).mockResolvedValue(mockKp);

      await service.removePrerequisite("kp-1", "kp-2");

      expect(mockOutbox.publish).toHaveBeenCalledWith(
        "knowledge_point.prerequisite_removed",
        "KnowledgePoint",
        "kp-1",
        { prerequisite_id: "kp-2" },
      );
    });
  });

  describe("getPrerequisites", () => {
    it("should return empty array when Neo4j not available", async () => {
      const result = await service.getPrerequisites("kp-1");
      expect(result).toEqual([]);
    });
  });
});
