import { describe, it, expect, beforeEach, vi } from "vitest";
import { TagsController } from "./tags.controller";
import { TagsService } from "./tags.service";
import type { CreateTagDto, UpdateTagDto } from "./dto";

describe("TagsController", () => {
  let controller: TagsController;
  let service: TagsService;

  const workspaceId = "workspace-123";
  const userId = "user-123";

  const mockTag = {
    id: "tag-123",
    workspaceId,
    name: "Architecture",
    slug: "architecture",
    color: "#FF5733",
    description: "Architecture related topics",
  };

  const mockTagsService = {
    create: vi.fn(),
    findAll: vi.fn(),
    findOne: vi.fn(),
    update: vi.fn(),
    remove: vi.fn(),
    getEntriesWithTag: vi.fn(),
  };

  beforeEach(() => {
    service = mockTagsService as any;
    controller = new TagsController(service);

    vi.clearAllMocks();
  });

  describe("create", () => {
    it("should create a tag", async () => {
      const createDto: CreateTagDto = {
        name: "Architecture",
        color: "#FF5733",
        description: "Architecture related topics",
      };

      mockTagsService.create.mockResolvedValue(mockTag);

      const result = await controller.create(createDto, workspaceId);

      expect(result).toEqual(mockTag);
      expect(mockTagsService.create).toHaveBeenCalledWith(workspaceId, createDto);
    });

    it("should pass undefined workspaceId to service (validation handled by guards)", async () => {
      const createDto: CreateTagDto = {
        name: "Architecture",
        color: "#FF5733",
      };

      mockTagsService.create.mockResolvedValue(mockTag);

      await controller.create(createDto, undefined as any);

      expect(mockTagsService.create).toHaveBeenCalledWith(undefined, createDto);
    });
  });

  describe("findAll", () => {
    it("should return all tags", async () => {
      const mockTags = [
        { ...mockTag, _count: { entries: 5 } },
        {
          id: "tag-456",
          workspaceId,
          name: "Design",
          slug: "design",
          color: "#00FF00",
          description: null,
          _count: { entries: 3 },
        },
      ];

      mockTagsService.findAll.mockResolvedValue(mockTags);

      const result = await controller.findAll(workspaceId);

      expect(result).toEqual(mockTags);
      expect(mockTagsService.findAll).toHaveBeenCalledWith(workspaceId);
    });

    it("should pass undefined workspaceId to service (validation handled by guards)", async () => {
      mockTagsService.findAll.mockResolvedValue([]);

      await controller.findAll(undefined as any);

      expect(mockTagsService.findAll).toHaveBeenCalledWith(undefined);
    });
  });

  describe("findOne", () => {
    it("should return a tag by slug", async () => {
      const mockTagWithCount = { ...mockTag, _count: { entries: 5 } };
      mockTagsService.findOne.mockResolvedValue(mockTagWithCount);

      const result = await controller.findOne("architecture", workspaceId);

      expect(result).toEqual(mockTagWithCount);
      expect(mockTagsService.findOne).toHaveBeenCalledWith("architecture", workspaceId);
    });

    it("should pass undefined workspaceId to service (validation handled by guards)", async () => {
      mockTagsService.findOne.mockResolvedValue(null);

      await controller.findOne("architecture", undefined as any);

      expect(mockTagsService.findOne).toHaveBeenCalledWith("architecture", undefined);
    });
  });

  describe("update", () => {
    it("should update a tag", async () => {
      const updateDto: UpdateTagDto = {
        name: "Updated Architecture",
        color: "#0000FF",
      };

      const updatedTag = {
        ...mockTag,
        name: "Updated Architecture",
        color: "#0000FF",
      };

      mockTagsService.update.mockResolvedValue(updatedTag);

      const result = await controller.update("architecture", updateDto, workspaceId);

      expect(result).toEqual(updatedTag);
      expect(mockTagsService.update).toHaveBeenCalledWith("architecture", workspaceId, updateDto);
    });

    it("should pass undefined workspaceId to service (validation handled by guards)", async () => {
      const updateDto: UpdateTagDto = {
        name: "Updated",
      };

      mockTagsService.update.mockResolvedValue(mockTag);

      await controller.update("architecture", updateDto, undefined as any);

      expect(mockTagsService.update).toHaveBeenCalledWith("architecture", undefined, updateDto);
    });
  });

  describe("remove", () => {
    it("should delete a tag", async () => {
      mockTagsService.remove.mockResolvedValue(undefined);

      await controller.remove("architecture", workspaceId);

      expect(mockTagsService.remove).toHaveBeenCalledWith("architecture", workspaceId);
    });

    it("should pass undefined workspaceId to service (validation handled by guards)", async () => {
      mockTagsService.remove.mockResolvedValue(undefined);

      await controller.remove("architecture", undefined as any);

      expect(mockTagsService.remove).toHaveBeenCalledWith("architecture", undefined);
    });
  });

  describe("getEntries", () => {
    it("should return entries with the tag", async () => {
      const mockEntries = [
        {
          id: "entry-1",
          slug: "entry-one",
          title: "Entry One",
          summary: "Summary",
          status: "PUBLISHED",
          visibility: "WORKSPACE",
          createdAt: new Date(),
          updatedAt: new Date(),
        },
      ];

      mockTagsService.getEntriesWithTag.mockResolvedValue(mockEntries);

      const result = await controller.getEntries("architecture", workspaceId);

      expect(result).toEqual(mockEntries);
      expect(mockTagsService.getEntriesWithTag).toHaveBeenCalledWith("architecture", workspaceId);
    });

    it("should pass undefined workspaceId to service (validation handled by guards)", async () => {
      mockTagsService.getEntriesWithTag.mockResolvedValue([]);

      await controller.getEntries("architecture", undefined as any);

      expect(mockTagsService.getEntriesWithTag).toHaveBeenCalledWith("architecture", undefined);
    });
  });
});
