import { describe, it, expect, beforeEach, vi } from "vitest";
import { Test, TestingModule } from "@nestjs/testing";
import { KnowledgeGraphController } from "./graph.controller";
import { GraphService } from "./services/graph.service";
import { PrismaService } from "../prisma/prisma.service";
import { AuthGuard } from "../auth/guards/auth.guard";
import { WorkspaceGuard } from "../common/guards/workspace.guard";
import { PermissionGuard } from "../common/guards/permission.guard";

describe("KnowledgeGraphController", () => {
  let controller: KnowledgeGraphController;
  let graphService: GraphService;
  let prismaService: PrismaService;

  const mockGraphService = {
    getFullGraph: vi.fn(),
    getGraphStats: vi.fn(),
    getEntryGraph: vi.fn(),
    getEntryGraphBySlug: vi.fn(),
  };

  const mockPrismaService = {
    knowledgeEntry: {
      findUnique: vi.fn(),
    },
  };

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      controllers: [KnowledgeGraphController],
      providers: [
        {
          provide: GraphService,
          useValue: mockGraphService,
        },
        {
          provide: PrismaService,
          useValue: mockPrismaService,
        },
      ],
    })
      .overrideGuard(AuthGuard)
      .useValue({ canActivate: vi.fn(() => true) })
      .overrideGuard(WorkspaceGuard)
      .useValue({ canActivate: vi.fn(() => true) })
      .overrideGuard(PermissionGuard)
      .useValue({ canActivate: vi.fn(() => true) })
      .compile();

    controller = module.get<KnowledgeGraphController>(KnowledgeGraphController);
    graphService = module.get<GraphService>(GraphService);
    prismaService = module.get<PrismaService>(PrismaService);

    vi.clearAllMocks();
  });

  it("should be defined", () => {
    expect(controller).toBeDefined();
  });

  describe("getFullGraph", () => {
    it("should return full graph without filters", async () => {
      const mockGraph = {
        nodes: [],
        edges: [],
        stats: { totalNodes: 0, totalEdges: 0, orphanCount: 0 },
      };
      mockGraphService.getFullGraph.mockResolvedValue(mockGraph);

      const result = await controller.getFullGraph("workspace-1", {});

      expect(graphService.getFullGraph).toHaveBeenCalledWith("workspace-1", {});
      expect(result).toEqual(mockGraph);
    });

    it("should pass filters to service", async () => {
      const mockGraph = {
        nodes: [],
        edges: [],
        stats: { totalNodes: 0, totalEdges: 0, orphanCount: 0 },
      };
      mockGraphService.getFullGraph.mockResolvedValue(mockGraph);

      const filters = {
        tags: ["tag-1"],
        status: "PUBLISHED",
        limit: 100,
      };

      await controller.getFullGraph("workspace-1", filters);

      expect(graphService.getFullGraph).toHaveBeenCalledWith("workspace-1", filters);
    });
  });

  describe("getGraphStats", () => {
    it("should return graph statistics", async () => {
      const mockStats = {
        totalEntries: 10,
        totalLinks: 15,
        orphanEntries: 2,
        averageLinks: 1.5,
        mostConnectedEntries: [],
        tagDistribution: [],
      };
      mockGraphService.getGraphStats.mockResolvedValue(mockStats);

      const result = await controller.getGraphStats("workspace-1");

      expect(graphService.getGraphStats).toHaveBeenCalledWith("workspace-1");
      expect(result).toEqual(mockStats);
    });
  });

  describe("getEntryGraph", () => {
    it("should return entry-centered graph", async () => {
      const mockEntry = {
        id: "entry-1",
        slug: "test-entry",
        title: "Test Entry",
      };

      const mockGraph = {
        centerNode: mockEntry,
        nodes: [mockEntry],
        edges: [],
        stats: { totalNodes: 1, totalEdges: 0, maxDepth: 1 },
      };

      mockGraphService.getEntryGraphBySlug.mockResolvedValue(mockGraph);

      const result = await controller.getEntryGraph("workspace-1", "test-entry", { depth: 2 });

      expect(graphService.getEntryGraphBySlug).toHaveBeenCalledWith("workspace-1", "test-entry", 2);
      expect(result).toEqual(mockGraph);
    });

    it("should use default depth if not provided", async () => {
      mockGraphService.getEntryGraphBySlug.mockResolvedValue({});

      await controller.getEntryGraph("workspace-1", "test-entry", {});

      expect(graphService.getEntryGraphBySlug).toHaveBeenCalledWith("workspace-1", "test-entry", 1);
    });

    it("should throw error if entry not found", async () => {
      mockGraphService.getEntryGraphBySlug.mockRejectedValue(new Error("Entry not found"));

      await expect(controller.getEntryGraph("workspace-1", "non-existent", {})).rejects.toThrow(
        "Entry not found"
      );
    });
  });
});
