import { describe, expect, it } from "vitest";
import { QueryBuilder } from "./query-builder";
import { SortOrder } from "../dto";

describe("QueryBuilder", () => {
  describe("buildSearchFilter", () => {
    it("should return empty object when search is undefined", () => {
      const result = QueryBuilder.buildSearchFilter(undefined, ["title", "description"]);
      expect(result).toEqual({});
    });

    it("should return empty object when search is empty string", () => {
      const result = QueryBuilder.buildSearchFilter("", ["title", "description"]);
      expect(result).toEqual({});
    });

    it("should build OR filter for multiple fields", () => {
      const result = QueryBuilder.buildSearchFilter("test", ["title", "description"]);
      expect(result).toEqual({
        OR: [
          { title: { contains: "test", mode: "insensitive" } },
          { description: { contains: "test", mode: "insensitive" } },
        ],
      });
    });

    it("should handle single field", () => {
      const result = QueryBuilder.buildSearchFilter("test", ["title"]);
      expect(result).toEqual({
        OR: [{ title: { contains: "test", mode: "insensitive" } }],
      });
    });

    it("should trim search query", () => {
      const result = QueryBuilder.buildSearchFilter("  test  ", ["title"]);
      expect(result).toEqual({
        OR: [{ title: { contains: "test", mode: "insensitive" } }],
      });
    });
  });

  describe("buildSortOrder", () => {
    it("should return default sort when sortBy is undefined", () => {
      const result = QueryBuilder.buildSortOrder(undefined, undefined, { createdAt: "desc" });
      expect(result).toEqual({ createdAt: "desc" });
    });

    it("should build single field sort", () => {
      const result = QueryBuilder.buildSortOrder("title", SortOrder.ASC);
      expect(result).toEqual({ title: "asc" });
    });

    it("should build multi-field sort", () => {
      const result = QueryBuilder.buildSortOrder("priority,dueDate", SortOrder.DESC);
      expect(result).toEqual([{ priority: "desc" }, { dueDate: "desc" }]);
    });

    it("should handle mixed sorting with custom order per field", () => {
      const result = QueryBuilder.buildSortOrder("priority:asc,dueDate:desc");
      expect(result).toEqual([{ priority: "asc" }, { dueDate: "desc" }]);
    });

    it("should use default order when not specified per field", () => {
      const result = QueryBuilder.buildSortOrder("priority,dueDate", SortOrder.ASC);
      expect(result).toEqual([{ priority: "asc" }, { dueDate: "asc" }]);
    });
  });

  describe("buildDateRangeFilter", () => {
    it("should return empty object when both dates are undefined", () => {
      const result = QueryBuilder.buildDateRangeFilter("createdAt", undefined, undefined);
      expect(result).toEqual({});
    });

    it("should build gte filter when only from date is provided", () => {
      const date = new Date("2024-01-01");
      const result = QueryBuilder.buildDateRangeFilter("createdAt", date, undefined);
      expect(result).toEqual({
        createdAt: { gte: date },
      });
    });

    it("should build lte filter when only to date is provided", () => {
      const date = new Date("2024-12-31");
      const result = QueryBuilder.buildDateRangeFilter("createdAt", undefined, date);
      expect(result).toEqual({
        createdAt: { lte: date },
      });
    });

    it("should build both gte and lte filters when both dates provided", () => {
      const fromDate = new Date("2024-01-01");
      const toDate = new Date("2024-12-31");
      const result = QueryBuilder.buildDateRangeFilter("createdAt", fromDate, toDate);
      expect(result).toEqual({
        createdAt: {
          gte: fromDate,
          lte: toDate,
        },
      });
    });
  });

  describe("buildInFilter", () => {
    it("should return empty object when values is undefined", () => {
      const result = QueryBuilder.buildInFilter("status", undefined);
      expect(result).toEqual({});
    });

    it("should return empty object when values is empty array", () => {
      const result = QueryBuilder.buildInFilter("status", []);
      expect(result).toEqual({});
    });

    it("should build in filter for single value", () => {
      const result = QueryBuilder.buildInFilter("status", ["ACTIVE"]);
      expect(result).toEqual({
        status: { in: ["ACTIVE"] },
      });
    });

    it("should build in filter for multiple values", () => {
      const result = QueryBuilder.buildInFilter("status", ["ACTIVE", "PENDING"]);
      expect(result).toEqual({
        status: { in: ["ACTIVE", "PENDING"] },
      });
    });

    it("should handle single value as string", () => {
      const result = QueryBuilder.buildInFilter("status", "ACTIVE" as any);
      expect(result).toEqual({
        status: { in: ["ACTIVE"] },
      });
    });
  });

  describe("buildPaginationParams", () => {
    it("should use default values when not provided", () => {
      const result = QueryBuilder.buildPaginationParams(undefined, undefined);
      expect(result).toEqual({
        skip: 0,
        take: 50,
      });
    });

    it("should calculate skip based on page and limit", () => {
      const result = QueryBuilder.buildPaginationParams(2, 20);
      expect(result).toEqual({
        skip: 20,
        take: 20,
      });
    });

    it("should handle page 1", () => {
      const result = QueryBuilder.buildPaginationParams(1, 25);
      expect(result).toEqual({
        skip: 0,
        take: 25,
      });
    });

    it("should handle large page numbers", () => {
      const result = QueryBuilder.buildPaginationParams(10, 50);
      expect(result).toEqual({
        skip: 450,
        take: 50,
      });
    });
  });
});
