/**
 * @fileoverview Pagination helpers for the DeepSource MCP Server
 * This module provides utilities for working with pagination.
 */

import { createLogger } from '../logging/logger.js';
import { PageInfo, PaginationParams, PaginatedResponse, PaginationMetadata } from './types.js';

// Logger for pagination utilities
const logger = createLogger('PaginationUtils');

/**
 * Creates an empty paginated response with no items
 * @template T The type of items in the response
 * @returns {PaginatedResponse<T>} Empty paginated response with consistent structure
 * @public
 */
export function createEmptyPaginatedResponse<T>(): PaginatedResponse<T> {
  return {
    items: [],
    pageInfo: {
      hasNextPage: false,
      hasPreviousPage: false,
    },
    totalCount: 0,
  };
}

/**
 * Logs a warning message about non-standard pagination usage
 * @param message Optional custom warning message to use instead of the default
 * @private
 */
function logPaginationWarning(message?: string): void {
  const warningMessage =
    message ||
    'Non-standard pagination: Using "last" without "before" is not recommended in Relay pagination';
  logger.warn(warningMessage);
}

/**
 * Normalizes pagination parameters for GraphQL queries
 * Ensures consistency in pagination parameters following Relay pagination best practices
 *
 * Normalization rules:
 * 1. If 'before' is provided (backward pagination):
 *    - Use 'last' as the count parameter (default: 10)
 *    - Remove any 'first' parameter to avoid ambiguity
 * 2. If 'last' is provided without 'before' (non-standard but supported):
 *    - Keep 'last' as is
 *    - Remove any 'first' parameter to avoid ambiguity
 *    - Log a warning about non-standard usage
 * 3. Otherwise (forward pagination or defaults):
 *    - Use 'first' as the count parameter (default: 10)
 *    - Remove any 'last' parameter to avoid ambiguity
 *
 * @template T Type that extends PaginationParams
 * @param {T} params - Original pagination parameters
 * @returns {T} Normalized pagination parameters with consistent values
 * @public
 */
export function normalizePaginationParams<T extends PaginationParams>(params: T): T {
  const normalizedParams = { ...params };

  // Validate and normalize numerical parameters
  if (normalizedParams.offset !== undefined) {
    normalizedParams.offset = Math.max(0, Math.floor(Number(normalizedParams.offset)));
  }

  if (normalizedParams.first !== undefined) {
    // Ensure first is a positive integer or undefined
    normalizedParams.first = Math.max(1, Math.floor(Number(normalizedParams.first)));
  }

  if (normalizedParams.last !== undefined) {
    // Ensure last is a positive integer or undefined
    normalizedParams.last = Math.max(1, Math.floor(Number(normalizedParams.last)));
  }

  // Validate cursor parameters (ensure they're valid strings)
  if (normalizedParams.after !== undefined && typeof normalizedParams.after !== 'string') {
    normalizedParams.after = String(normalizedParams.after ?? '');
  }

  if (normalizedParams.before !== undefined && typeof normalizedParams.before !== 'string') {
    normalizedParams.before = String(normalizedParams.before ?? '');
  }

  // Apply Relay pagination rules
  if (normalizedParams.before) {
    // When fetching backwards with 'before', prioritize 'last'
    normalizedParams.last = normalizedParams.last ?? normalizedParams.first ?? 10;
    delete normalizedParams.first;
  } else if (normalizedParams.last) {
    // If 'last' is provided without 'before', log a warning but still use 'last'
    logPaginationWarning(
      `Non-standard pagination: Using "last=${normalizedParams.last}" without "before" cursor is not recommended`
    );
    // Keep normalizedParams.last as is
    delete normalizedParams.first;
  } else {
    // Default or forward pagination with 'after', prioritize 'first'
    normalizedParams.first = normalizedParams.first ?? 10;
    delete normalizedParams.last;
  }

  return normalizedParams;
}

/**
 * Creates a formatted pagination help object for API responses
 * @param pageInfo The page information to create help documentation for
 * @returns Pagination help documentation object
 * @public
 */
export function createPaginationHelp(pageInfo: PageInfo): Record<string, unknown> {
  return {
    description: 'This API uses Relay-style cursor-based pagination',
    forward_pagination: `To get the next page, use 'first: 10, after: "${
      pageInfo.endCursor || 'cursor_value'
    }"'`,
    backward_pagination: `To get the previous page, use 'last: 10, before: "${
      pageInfo.startCursor || 'cursor_value'
    }"'`,
    page_status: {
      has_next_page: pageInfo.hasNextPage,
      has_previous_page: pageInfo.hasPreviousPage,
    },
  };
}

/**
 * Creates a detailed pagination help object for API responses with more examples
 * @param pageInfo The page information to create help documentation for
 * @param itemCount The number of items in the current page
 * @returns Enhanced pagination help documentation object
 * @public
 */
export function createEnhancedPaginationHelp(
  pageInfo: PageInfo,
  itemCount: number
): Record<string, unknown> {
  return {
    description: 'This API uses Relay-style cursor-based pagination for efficient data retrieval',
    current_page: {
      size: itemCount,
      has_next_page: pageInfo.hasNextPage,
      has_previous_page: pageInfo.hasPreviousPage,
    },
    next_page: pageInfo.hasNextPage
      ? {
          example: `{"first": 10, "after": "${pageInfo.endCursor}"}`,
          description: 'Use these parameters to fetch the next page of results',
        }
      : null,
    previous_page: pageInfo.hasPreviousPage
      ? {
          example: `{"last": 10, "before": "${pageInfo.startCursor}"}`,
          description: 'Use these parameters to fetch the previous page of results',
        }
      : null,
    pagination_types: {
      forward: 'For forward pagination, use "first" with optional "after" cursor',
      backward: 'For backward pagination, use "last" with optional "before" cursor',
      legacy: 'Legacy offset-based pagination is also supported via the "offset" parameter',
    },
  };
}

/**
 * Handles page_size parameter as an alias for first
 * @param params Pagination parameters that may include page_size
 * @returns Normalized params with first set appropriately
 * @public
 */
export function handlePageSizeAlias(params: PaginationParams): PaginationParams {
  const { page_size, ...restParams } = params;

  // Use page_size as an alias for first if first is not already set
  if (page_size !== undefined && restParams.first === undefined && !restParams.before) {
    restParams.first = page_size;
  }

  return restParams;
}

/**
 * Determines if multi-page fetching is needed based on parameters
 * @param params Pagination parameters
 * @returns Whether multiple pages should be fetched
 * @public
 */
export function shouldFetchMultiplePages(params: PaginationParams): boolean {
  return params.max_pages !== undefined && params.max_pages > 1;
}

/**
 * Creates pagination metadata from a standard response
 * @param response Paginated response
 * @param pagesFetched Number of pages fetched (for multi-page operations)
 * @returns User-friendly pagination metadata
 * @public
 */
export function createPaginationMetadata<T>(
  response: PaginatedResponse<T>,
  pagesFetched = 1
): PaginationMetadata {
  const metadata: PaginationMetadata = {
    has_more_pages: response.pageInfo.hasNextPage,
    page_size: response.items.length,
  };

  if (response.pageInfo.endCursor) {
    metadata.next_cursor = response.pageInfo.endCursor;
  }
  if (response.pageInfo.startCursor) {
    metadata.previous_cursor = response.pageInfo.startCursor;
  }
  if (response.totalCount !== undefined) {
    metadata.total_count = response.totalCount;
  }
  if (pagesFetched > 1) {
    metadata.pages_fetched = pagesFetched;
  }

  return metadata;
}
