import { describe, it, expect, beforeEach, vi } from "vitest";
import { Test, TestingModule } from "@nestjs/testing";
import { EntryStatus } from "@prisma/client";
import { SearchController } from "./search.controller";
import { SearchService } from "./services/search.service";
import { AuthGuard } from "../auth/guards/auth.guard";
import { WorkspaceGuard, PermissionGuard } from "../common/guards";

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

  const mockWorkspaceId = "550e8400-e29b-41d4-a716-446655440000";

  const mockSearchService = {
    search: vi.fn(),
    searchByTags: vi.fn(),
    recentEntries: vi.fn(),
  };

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      controllers: [SearchController],
      providers: [
        {
          provide: SearchService,
          useValue: mockSearchService,
        },
      ],
    })
      .overrideGuard(AuthGuard)
      .useValue({ canActivate: () => true })
      .overrideGuard(WorkspaceGuard)
      .useValue({ canActivate: () => true })
      .overrideGuard(PermissionGuard)
      .useValue({ canActivate: () => true })
      .compile();

    controller = module.get<SearchController>(SearchController);

    vi.clearAllMocks();
  });

  describe("search", () => {
    it("should call searchService.search with correct parameters", async () => {
      const mockResult = {
        data: [],
        pagination: { page: 1, limit: 20, total: 0, totalPages: 0 },
        query: "test",
      };
      mockSearchService.search.mockResolvedValue(mockResult);

      const result = await controller.search(mockWorkspaceId, {
        q: "test",
        page: 1,
        limit: 20,
      });

      expect(mockSearchService.search).toHaveBeenCalledWith(
        "test",
        mockWorkspaceId,
        {
          status: undefined,
          page: 1,
          limit: 20,
        }
      );
      expect(result).toEqual(mockResult);
    });

    it("should pass status filter to service", async () => {
      mockSearchService.search.mockResolvedValue({
        data: [],
        pagination: { page: 1, limit: 20, total: 0, totalPages: 0 },
        query: "test",
      });

      await controller.search(mockWorkspaceId, {
        q: "test",
        status: EntryStatus.PUBLISHED,
      });

      expect(mockSearchService.search).toHaveBeenCalledWith(
        "test",
        mockWorkspaceId,
        {
          status: EntryStatus.PUBLISHED,
          page: undefined,
          limit: undefined,
        }
      );
    });
  });

  describe("searchByTags", () => {
    it("should call searchService.searchByTags with correct parameters", async () => {
      const mockResult = {
        data: [],
        pagination: { page: 1, limit: 20, total: 0, totalPages: 0 },
      };
      mockSearchService.searchByTags.mockResolvedValue(mockResult);

      const result = await controller.searchByTags(mockWorkspaceId, {
        tags: ["api", "documentation"],
        page: 1,
        limit: 20,
      });

      expect(mockSearchService.searchByTags).toHaveBeenCalledWith(
        ["api", "documentation"],
        mockWorkspaceId,
        {
          status: undefined,
          page: 1,
          limit: 20,
        }
      );
      expect(result).toEqual(mockResult);
    });

    it("should pass status filter to service", async () => {
      mockSearchService.searchByTags.mockResolvedValue({
        data: [],
        pagination: { page: 1, limit: 20, total: 0, totalPages: 0 },
      });

      await controller.searchByTags(mockWorkspaceId, {
        tags: ["api"],
        status: EntryStatus.DRAFT,
      });

      expect(mockSearchService.searchByTags).toHaveBeenCalledWith(
        ["api"],
        mockWorkspaceId,
        {
          status: EntryStatus.DRAFT,
          page: undefined,
          limit: undefined,
        }
      );
    });
  });

  describe("recentEntries", () => {
    it("should call searchService.recentEntries with correct parameters", async () => {
      const mockEntries = [
        {
          id: "entry-1",
          title: "Recent Entry",
          slug: "recent-entry",
          tags: [],
        },
      ];
      mockSearchService.recentEntries.mockResolvedValue(mockEntries);

      const result = await controller.recentEntries(mockWorkspaceId, {
        limit: 10,
      });

      expect(mockSearchService.recentEntries).toHaveBeenCalledWith(
        mockWorkspaceId,
        10,
        undefined
      );
      expect(result).toEqual({
        data: mockEntries,
        count: 1,
      });
    });

    it("should use default limit of 10", async () => {
      mockSearchService.recentEntries.mockResolvedValue([]);

      await controller.recentEntries(mockWorkspaceId, {});

      expect(mockSearchService.recentEntries).toHaveBeenCalledWith(
        mockWorkspaceId,
        10,
        undefined
      );
    });

    it("should pass status filter to service", async () => {
      mockSearchService.recentEntries.mockResolvedValue([]);

      await controller.recentEntries(mockWorkspaceId, {
        status: EntryStatus.PUBLISHED,
        limit: 5,
      });

      expect(mockSearchService.recentEntries).toHaveBeenCalledWith(
        mockWorkspaceId,
        5,
        EntryStatus.PUBLISHED
      );
    });
  });
});
