/**
 * Pagination utilities for consistent pagination across all list endpoints
 * Supports both offset-based and cursor-based pagination
 */

import { NextRequest } from 'next/server';

export interface PaginationParams {
  page?: number;
  limit?: number;
  cursor?: string;
}

export interface PaginationResult<T> {
  data: T[];
  pagination: {
    total: number;
    page: number;
    limit: number;
    totalPages: number;
    hasNext: boolean;
    hasPrev: boolean;
    cursor?: string;
  };
}

export interface CursorPaginationResult<T> {
  data: T[];
  pagination: {
    nextCursor: string | null;
    prevCursor: string | null;
    hasNext: boolean;
    hasPrev: boolean;
    limit: number;
  };
}

/**
 * Default pagination limits
 */
export const DEFAULT_PAGE_SIZE = 20;
export const MAX_PAGE_SIZE = 100;
export const DEFAULT_PAGE = 1;

/**
 * Extract pagination parameters from request URL
 */
export function getPaginationParams(request: NextRequest): PaginationParams {
  const { searchParams } = new URL(request.url);

  const page = Math.max(1, parseInt(searchParams.get('page') || '1', 10));
  const limit = Math.min(
    MAX_PAGE_SIZE,
    Math.max(1, parseInt(searchParams.get('limit') || String(DEFAULT_PAGE_SIZE), 10))
  );
  const cursor = searchParams.get('cursor') || undefined;

  return { page, limit, cursor };
}

/**
 * Calculate skip value for offset-based pagination
 */
export function getSkip(page: number, limit: number): number {
  return (page - 1) * limit;
}

/**
 * Build pagination metadata for offset-based pagination
 */
export function buildPaginationMeta(
  total: number,
  page: number,
  limit: number
) {
  const totalPages = Math.ceil(total / limit);

  return {
    total,
    page,
    limit,
    totalPages,
    hasNext: page < totalPages,
    hasPrev: page > 1,
  };
}

/**
 * Create paginated response for offset-based pagination
 */
export function createPaginatedResponse<T>(
  data: T[],
  total: number,
  page: number,
  limit: number
): PaginationResult<T> {
  return {
    data,
    pagination: buildPaginationMeta(total, page, limit),
  };
}

/**
 * Build cursor pagination metadata
 */
export function buildCursorPaginationMeta<T extends { id: string | number }>(
  data: T[],
  limit: number,
  hasMore: boolean = false
): CursorPaginationResult<T>['pagination'] {
  const nextCursor = hasMore && data.length > 0
    ? String(data[data.length - 1].id)
    : null;
  const prevCursor = data.length > 0
    ? String(data[0].id)
    : null;

  return {
    nextCursor,
    prevCursor,
    hasNext: hasMore,
    hasPrev: false, // Set based on your logic
    limit,
  };
}

/**
 * Create cursor-based paginated response
 */
export function createCursorPaginatedResponse<T extends { id: string | number }>(
  data: T[],
  limit: number,
  hasMore: boolean = false
): CursorPaginationResult<T> {
  return {
    data,
    pagination: buildCursorPaginationMeta(data, limit, hasMore),
  };
}

/**
 * Prisma offset-based pagination helper
 */
export interface PrismaOffsetPagination {
  skip: number;
  take: number;
}

export function getPrismaOffsetPagination(
  page: number,
  limit: number
): PrismaOffsetPagination {
  return {
    skip: getSkip(page, limit),
    take: limit,
  };
}

/**
 * Prisma cursor-based pagination helper
 */
export interface PrismaCursorPagination {
  cursor?: { id: string | number };
  take: number;
  skip?: number;
}

export function getPrismaCursorPagination(
  cursor: string | undefined,
  limit: number
): PrismaCursorPagination {
  const pagination: PrismaCursorPagination = {
    take: limit + 1, // Fetch one extra to check if there's more
  };

  if (cursor) {
    pagination.cursor = { id: cursor };
    pagination.skip = 1; // Skip the cursor itself
  }

  return pagination;
}

/**
 * Extract query parameters with defaults
 */
export function getQueryParams(searchParams: URLSearchParams) {
  return {
    page: Math.max(1, parseInt(searchParams.get('page') || '1', 10)),
    limit: Math.min(
      MAX_PAGE_SIZE,
      Math.max(1, parseInt(searchParams.get('limit') || String(DEFAULT_PAGE_SIZE), 10))
    ),
    search: searchParams.get('search') || undefined,
    sortBy: searchParams.get('sortBy') || undefined,
    sortOrder: (searchParams.get('sortOrder') || 'desc') as 'asc' | 'desc',
  };
}
