/**
 * Pagination utilities for data manipulation
 */

import { PaginationConfig, PaginationResult } from './types';

/**
 * Paginate data array
 * @param data - Array to paginate
 * @param config - Pagination configuration
 */
export function paginate<T>(data: T[], config: PaginationConfig): PaginationResult<T> {
  if (!Array.isArray(data)) {
    throw new Error('Data must be an array');
  }

  const { page, pageSize } = config;

  if (page < 1) {
    throw new Error('Page number must be 1 or greater');
  }

  if (pageSize < 1) {
    throw new Error('Page size must be 1 or greater');
  }

  const totalItems = data.length;
  const totalPages = Math.ceil(totalItems / pageSize);
  const startIndex = (page - 1) * pageSize;
  const endIndex = Math.min(startIndex + pageSize - 1, totalItems - 1);
  
  const paginatedData = totalItems > 0 ? data.slice(startIndex, startIndex + pageSize) : [];

  return {
    data: paginatedData,
    page,
    pageSize,
    totalItems,
    totalPages,
    hasPrevious: page > 1,
    hasNext: page < totalPages,
    startIndex: totalItems > 0 ? startIndex : -1,
    endIndex: totalItems > 0 ? endIndex : -1
  };
}

/**
 * Calculate pagination metadata without slicing data
 * @param totalItems - Total number of items
 * @param page - Current page
 * @param pageSize - Items per page
 */
export function calculatePages(totalItems: number, page: number, pageSize: number): Omit<PaginationResult, 'data'> {
  if (totalItems < 0) {
    throw new Error('Total items cannot be negative');
  }

  if (page < 1) {
    throw new Error('Page number must be 1 or greater');
  }

  if (pageSize < 1) {
    throw new Error('Page size must be 1 or greater');
  }

  const totalPages = Math.ceil(totalItems / pageSize);
  const startIndex = (page - 1) * pageSize;
  const endIndex = Math.min(startIndex + pageSize - 1, totalItems - 1);

  return {
    page,
    pageSize,
    totalItems,
    totalPages,
    hasPrevious: page > 1,
    hasNext: page < totalPages,
    startIndex: totalItems > 0 ? startIndex : -1,
    endIndex: totalItems > 0 ? endIndex : -1
  };
}

/**
 * Get page numbers for pagination display (with ellipsis logic)
 * @param currentPage - Current page
 * @param totalPages - Total number of pages
 * @param maxVisible - Maximum number of visible page numbers
 */
export function getPaginationRange(
  currentPage: number, 
  totalPages: number, 
  maxVisible: number = 7
): Array<number | 'ellipsis'> {
  if (currentPage < 1 || currentPage > totalPages) {
    throw new Error('Current page is out of range');
  }

  if (totalPages <= maxVisible) {
    // Show all pages if total is less than max visible
    return Array.from({ length: totalPages }, (_, i) => i + 1);
  }

  const range: Array<number | 'ellipsis'> = [];
  const sidePages = Math.floor((maxVisible - 3) / 2); // Reserve 3 spots for first, last, and current

  // Always show first page
  range.push(1);

  if (currentPage <= sidePages + 2) {
    // Current page is near the beginning
    for (let i = 2; i <= Math.min(maxVisible - 1, totalPages - 1); i++) {
      range.push(i);
    }
    if (totalPages > maxVisible - 1) {
      range.push('ellipsis');
    }
  } else if (currentPage >= totalPages - sidePages - 1) {
    // Current page is near the end
    range.push('ellipsis');
    for (let i = Math.max(2, totalPages - maxVisible + 2); i <= totalPages - 1; i++) {
      range.push(i);
    }
  } else {
    // Current page is in the middle
    range.push('ellipsis');
    for (let i = currentPage - sidePages; i <= currentPage + sidePages; i++) {
      range.push(i);
    }
    range.push('ellipsis');
  }

  // Always show last page (if it's not already included)
  if (totalPages > 1 && !range.includes(totalPages)) {
    range.push(totalPages);
  }

  return range;
}

/**
 * Get item range text for display (e.g., "1-10 of 100")
 * @param startIndex - Start index (0-based)
 * @param endIndex - End index (0-based)
 * @param totalItems - Total number of items
 */
export function getItemRangeText(startIndex: number, endIndex: number, totalItems: number): string {
  if (totalItems === 0) return '0 of 0';
  
  const start = startIndex + 1; // Convert to 1-based
  const end = endIndex + 1;     // Convert to 1-based
  
  return `${start}-${end} of ${totalItems}`;
}

/**
 * Get page information text (e.g., "Page 1 of 10")
 * @param currentPage - Current page
 * @param totalPages - Total number of pages
 */
export function getPageInfoText(currentPage: number, totalPages: number): string {
  if (totalPages === 0) return 'Page 0 of 0';
  return `Page ${currentPage} of ${totalPages}`;
}

/**
 * Calculate optimal page size based on container height and item height
 * @param containerHeight - Height of the container in pixels
 * @param itemHeight - Height of each item in pixels
 * @param minPageSize - Minimum page size
 * @param maxPageSize - Maximum page size
 */
export function calculateOptimalPageSize(
  containerHeight: number,
  itemHeight: number,
  minPageSize: number = 5,
  maxPageSize: number = 100
): number {
  const calculatedSize = Math.floor(containerHeight / itemHeight);
  return Math.max(minPageSize, Math.min(maxPageSize, calculatedSize));
}

/**
 * Navigate to a specific page (with bounds checking)
 * @param currentPage - Current page
 * @param totalPages - Total number of pages
 * @param targetPage - Target page to navigate to
 */
export function navigateToPage(currentPage: number, totalPages: number, targetPage: number): number {
  return Math.max(1, Math.min(totalPages, targetPage));
}

/**
 * Navigate to previous page
 * @param currentPage - Current page
 */
export function navigateToPrevious(currentPage: number): number {
  return Math.max(1, currentPage - 1);
}

/**
 * Navigate to next page
 * @param currentPage - Current page
 * @param totalPages - Total number of pages
 */
export function navigateToNext(currentPage: number, totalPages: number): number {
  return Math.min(totalPages, currentPage + 1);
}

/**
 * Navigate to first page
 */
export function navigateToFirst(): number {
  return 1;
}

/**
 * Navigate to last page
 * @param totalPages - Total number of pages
 */
export function navigateToLast(totalPages: number): number {
  return Math.max(1, totalPages);
}

/**
 * Check if page navigation is valid
 * @param page - Page to check
 * @param totalPages - Total number of pages
 */
export function isValidPage(page: number, totalPages: number): boolean {
  return page >= 1 && page <= totalPages && totalPages > 0;
}

/**
 * Get all possible page sizes (common values)
 */
export function getStandardPageSizes(): number[] {
  return [5, 10, 20, 25, 50, 100];
}

/**
 * Get recommended page size based on total items
 * @param totalItems - Total number of items
 */
export function getRecommendedPageSize(totalItems: number): number {
  if (totalItems <= 25) return 10;
  if (totalItems <= 100) return 25;
  if (totalItems <= 500) return 50;
  return 100;
}

/**
 * Paginate with server-side pagination simulation
 * @param data - Full dataset
 * @param page - Current page
 * @param pageSize - Items per page
 * @param searchTerm - Search term to filter data first
 * @param searchKeys - Keys to search in
 */
export function serverPaginate<T>(
  data: T[],
  page: number,
  pageSize: number,
  searchTerm?: string,
  searchKeys?: (keyof T)[]
): PaginationResult<T> {
  let filteredData = data;

  // Apply search filter first if provided
  if (searchTerm && searchKeys && searchKeys.length > 0) {
    const term = searchTerm.toLowerCase();
    filteredData = data.filter(item => {
      return searchKeys.some(key => {
        const value = item[key];
        return value != null && String(value).toLowerCase().includes(term);
      });
    });
  }

  // Then paginate the filtered data
  return paginate(filteredData, { page, pageSize });
}