import { SortOrder } from "../dto";
import type { Prisma } from "@prisma/client";

/**
 * Utility class for building Prisma query filters
 * Provides reusable methods for common query operations
 */
export class QueryBuilder {
  /**
   * Build a full-text search filter across multiple fields
   * @param search - Search query string
   * @param fields - Fields to search in
   * @returns Prisma where clause with OR conditions
   */
  static buildSearchFilter(search: string | undefined, fields: string[]): Prisma.JsonObject {
    if (!search || search.trim() === "") {
      return {};
    }

    const trimmedSearch = search.trim();

    return {
      OR: fields.map((field) => ({
        [field]: {
          contains: trimmedSearch,
          mode: "insensitive" as const,
        },
      })),
    };
  }

  /**
   * Build sort order configuration
   * Supports single or multi-field sorting with custom order per field
   * @param sortBy - Field(s) to sort by (comma-separated)
   * @param sortOrder - Default sort order
   * @param defaultSort - Fallback sort order if sortBy is undefined
   * @returns Prisma orderBy clause
   */
  static buildSortOrder(
    sortBy?: string,
    sortOrder?: SortOrder,
    defaultSort?: Record<string, string>
  ): Record<string, string> | Record<string, string>[] {
    if (!sortBy) {
      return defaultSort ?? { createdAt: "desc" };
    }

    const fields = sortBy
      .split(",")
      .map((f) => f.trim())
      .filter(Boolean);

    if (fields.length === 0) {
      // Default to createdAt if no valid fields
      return { createdAt: sortOrder ?? SortOrder.DESC };
    }

    if (fields.length === 1) {
      // Check if field has custom order (e.g., "priority:asc")
      const fieldStr = fields[0];
      if (!fieldStr) {
        return { createdAt: sortOrder ?? SortOrder.DESC };
      }
      const parts = fieldStr.split(":");
      const field = parts[0] ?? "createdAt"; // Default to createdAt if field is empty
      const customOrder = parts[1];
      return {
        [field]: customOrder ?? sortOrder ?? SortOrder.DESC,
      };
    }

    // Multi-field sorting
    return fields.map((field) => {
      const parts = field.split(":");
      const fieldName = parts[0] ?? "createdAt"; // Default to createdAt if field is empty
      const customOrder = parts[1];
      return {
        [fieldName]: customOrder ?? sortOrder ?? SortOrder.DESC,
      };
    });
  }

  /**
   * Build date range filter
   * @param field - Date field name
   * @param from - Start date
   * @param to - End date
   * @returns Prisma where clause with date range
   */
  static buildDateRangeFilter(field: string, from?: Date, to?: Date): Prisma.JsonObject {
    if (!from && !to) {
      return {};
    }

    const filter: Record<string, unknown> = {};

    if (from || to) {
      const dateFilter: Record<string, unknown> = {};
      if (from) {
        dateFilter.gte = from;
      }
      if (to) {
        dateFilter.lte = to;
      }
      filter[field] = dateFilter;
    }

    return filter as Prisma.JsonObject;
  }

  /**
   * Build IN filter for multi-select fields
   * @param field - Field name
   * @param values - Array of values or single value
   * @returns Prisma where clause with IN condition
   */
  static buildInFilter<T extends string | number>(
    field: string,
    values?: T | T[]
  ): Prisma.JsonObject {
    if (!values) {
      return {};
    }

    const valueArray = Array.isArray(values) ? values : [values];

    if (valueArray.length === 0) {
      return {};
    }

    return {
      [field]: { in: valueArray },
    };
  }

  /**
   * Build pagination parameters
   * @param page - Page number (1-indexed)
   * @param limit - Items per page
   * @returns Prisma skip and take parameters
   */
  static buildPaginationParams(page?: number, limit?: number): { skip: number; take: number } {
    const actualPage = page ?? 1;
    const actualLimit = limit ?? 50;

    return {
      skip: (actualPage - 1) * actualLimit,
      take: actualLimit,
    };
  }

  /**
   * Build pagination metadata
   * @param total - Total count of items
   * @param page - Current page
   * @param limit - Items per page
   * @returns Pagination metadata object
   */
  static buildPaginationMeta(
    total: number,
    page: number,
    limit: number
  ): {
    total: number;
    page: number;
    limit: number;
    totalPages: number;
    hasNextPage: boolean;
    hasPrevPage: boolean;
  } {
    const totalPages = Math.ceil(total / limit);

    return {
      total,
      page,
      limit,
      totalPages,
      hasNextPage: page < totalPages,
      hasPrevPage: page > 1,
    };
  }
}
