/**
 * @fileoverview Tab history pruning utilities with multiple strategies for managing navigation history size.
 *
 * This module provides sophisticated history management for tab navigation:
 * - Three pruning strategies: oldest, balanced, and smart
 * - Forward history limiting when adding new locations
 * - Index validation and correction utilities
 * - Configurable pruning behavior per tab or globally
 *
 * The pruning system prevents unlimited memory growth while preserving
 * navigation functionality and user experience. Each strategy offers
 * different trade-offs between memory usage and history preservation.
 */

import { TabLocation, TabLocationHistory } from './schemas';
import { TabConfig } from './tab-config';

/**
 * Result of a history pruning operation.
 *
 * Contains the pruned location array, updated current index,
 * and metadata about the pruning operation performed.
 */
export interface HistoryPruningResult {
  /** Array of locations after pruning */
  locations: TabLocation[];
  /** Updated current index after pruning */
  newCurrent: number;
  /** Number of entries removed during pruning */
  entriesRemoved: number;
  /** Name of the pruning strategy used */
  strategy: string;
}

/**
 * Static utility class for managing tab navigation history pruning.
 *
 * Provides multiple strategies for reducing history size while maintaining
 * navigation functionality. All methods are static and stateless.
 */
export class TabHistoryPruner {

  /**
   * Prunes history based on the configured strategy.
   *
   * Automatically selects and applies the appropriate pruning strategy
   * based on configuration. Supports per-tab metadata overrides.
   *
   * @param locationHistory - Current tab location history to prune
   * @param config - Global tab configuration
   * @param tabMetadata - Optional per-tab configuration overrides
   * @returns Pruning result with updated locations and metadata
   *
   * @example
   * ```typescript
   * const result = TabHistoryPruner.pruneHistory(
   *   tab.location,
   *   globalConfig,
   *   { maxHistorySize: 25 }
   * );
   * if (result.entriesRemoved > 0) {
   *   console.log(`Pruned ${result.entriesRemoved} entries using ${result.strategy}`);
   * }
   * ```
   */
  static pruneHistory(
    locationHistory: TabLocationHistory,
    config: TabConfig,
    tabMetadata?: { maxHistorySize?: number; maxForwardHistory?: number }
  ): HistoryPruningResult {
    const maxSize = tabMetadata?.maxHistorySize ?? config.maxHistorySize;
    const { locations, current } = locationHistory;

    if (locations.length <= maxSize) {
      return {
        locations: [...locations],
        newCurrent: current,
        entriesRemoved: 0,
        strategy: 'no-pruning'
      };
    }

    const strategy = config.pruningStrategy;

    switch (strategy) {
      case 'oldest':
        return this.pruneOldestFirst(locations, current, maxSize);
      case 'balanced':
        return this.pruneBalanced(locations, current, maxSize);
      case 'smart':
        return this.pruneSmart(locations, current, maxSize, config);
      default:
        return this.pruneBalanced(locations, current, maxSize);
    }
  }

  /**
   * Removes oldest entries first, adjusting current index.
   *
   * Simple FIFO (First In, First Out) pruning strategy that removes
   * the oldest history entries when the size limit is exceeded.
   * Preserves recent navigation while maintaining current position.
   *
   * @param locations - Array of tab locations to prune
   * @param current - Current index in the locations array
   * @param maxSize - Maximum number of locations to keep
   * @returns Pruning result with updated locations and index
   *
   * @example
   * ```typescript
   * // With locations [A, B, C, D, E] and current=2 (C), maxSize=3
   * // Result: [C, D, E] with newCurrent=0 (still pointing to C)
   * ```
   */
  private static pruneOldestFirst(
    locations: TabLocation[],
    current: number,
    maxSize: number
  ): HistoryPruningResult {
    const removeCount = locations.length - maxSize;
    const prunedLocations = locations.slice(removeCount);
    const newCurrent = Math.max(-1, current - removeCount);

    return {
      locations: prunedLocations,
      newCurrent,
      entriesRemoved: removeCount,
      strategy: 'oldest-first'
    };
  }

  /**
   * Keeps entries balanced around the current position.
   *
   * Maintains a balanced window around the current location, preserving
   * 70% of entries before current and 30% after. This strategy provides
   * good back/forward navigation while respecting size limits.
   *
   * @param locations - Array of tab locations to prune
   * @param current - Current index in the locations array
   * @param maxSize - Maximum number of locations to keep
   * @returns Pruning result with maintained current position
   *
   * @example
   * ```typescript
   * // With current=5 in 20 locations, maxSize=10
   * // Keeps ~7 entries before current, current entry, ~2 entries after
   * // Result preserves navigation context around current position
   * ```
   */
  private static pruneBalanced(
    locations: TabLocation[],
    current: number,
    maxSize: number
  ): HistoryPruningResult {
    // Preserve 70% of entries before current, 30% after
    const backwardRatio = 0.7;
    const maxBackward = Math.floor(maxSize * backwardRatio);
    const maxForward = maxSize - maxBackward - 1; // -1 for current item

    const keepStart = Math.max(0, current - maxBackward);
    const keepEnd = Math.min(locations.length, current + 1 + maxForward);

    const prunedLocations = locations.slice(keepStart, keepEnd);
    const newCurrent = current - keepStart;
    const entriesRemoved = locations.length - prunedLocations.length;

    return {
      locations: prunedLocations,
      newCurrent,
      entriesRemoved,
      strategy: 'balanced'
    };
  }

  /**
   * Intelligent pruning based on usage patterns and recency.
   *
   * Uses a scoring algorithm that considers both recency (how recently
   * a location was visited) and proximity (how close to current position).
   * Recent locations and those near the current position get higher scores
   * and are more likely to be preserved.
   *
   * Scoring factors:
   * - Recent (< 1 hour): 100 points base
   * - Medium (< 1 day): 60 points base
   * - Old (> 1 day): 20 points base
   * - Proximity: 100 - (distance_from_current * 10) points
   *
   * @param locations - Array of tab locations to prune
   * @param current - Current index in the locations array
   * @param maxSize - Maximum number of locations to keep
   * @param config - Tab configuration (unused but kept for consistency)
   * @returns Pruning result with intelligently selected locations
   *
   * @example
   * ```typescript
   * // Preserves recently visited pages and those near current position
   * // while removing old, distant entries first
   * ```
   */
  private static pruneSmart(
    locations: TabLocation[],
    current: number,
    maxSize: number,
    config: TabConfig
  ): HistoryPruningResult {
    const now = Date.now();
    const oneHour = 60 * 60 * 1000;
    const oneDay = 24 * oneHour;

    // Score each location based on recency and distance from current
    const scoredLocations = locations.map((location, index) => {
      const age = now - location.timestamp;
      const distanceFromCurrent = Math.abs(index - current);

      // Recent locations get higher scores
      let recencyScore = 100;
      if (age > oneDay) recencyScore = 20;
      else if (age > oneHour) recencyScore = 60;

      // Locations near current position get higher scores
      const proximityScore = Math.max(0, 100 - (distanceFromCurrent * 10));

      return {
        location,
        index,
        score: recencyScore + proximityScore,
        isCurrent: index === current
      };
    });

    // Always keep current location and sort others by score
    const currentItem = scoredLocations.find(item => item.isCurrent);
    const otherItems = scoredLocations
      .filter(item => !item.isCurrent)
      .sort((a, b) => b.score - a.score);

    // Take top scoring items
    const itemsToKeep = Math.min(maxSize - 1, otherItems.length); // -1 for current
    const keptItems = otherItems.slice(0, itemsToKeep);

    if (currentItem) {
      keptItems.push(currentItem);
    }

    // Sort by original index to maintain order
    keptItems.sort((a, b) => a.index - b.index);

    const prunedLocations = keptItems.map(item => item.location);
    const newCurrent = keptItems.findIndex(item => item.isCurrent);
    const entriesRemoved = locations.length - prunedLocations.length;

    return {
      locations: prunedLocations,
      newCurrent: newCurrent === -1 ? Math.max(0, prunedLocations.length - 1) : newCurrent,
      entriesRemoved,
      strategy: 'smart'
    };
  }

  /**
   * Prunes forward history when adding a new location.
   *
   * Limits the number of forward history entries that are preserved
   * when navigating to a new location. This prevents unlimited
   * forward history accumulation while maintaining reasonable redo depth.
   *
   * @param locations - Current array of tab locations
   * @param current - Current index in the locations array
   * @param maxForwardHistory - Maximum forward entries to preserve
   * @returns Object with pruned locations and unchanged current index
   *
   * @example
   * ```typescript
   * // With current=2, maxForwardHistory=2
   * // [A, B, C, D, E, F] becomes [A, B, C, D, E]
   * // Preserves current position while limiting forward entries
   * ```
   */
  static pruneForwardHistory(
    locations: TabLocation[],
    current: number,
    maxForwardHistory: number
  ): { locations: TabLocation[]; newCurrent: number } {
    if (current < 0 || current >= locations.length) {
      return { locations: [...locations], newCurrent: current };
    }

    const beforeCurrent = locations.slice(0, current + 1);
    const afterCurrent = locations.slice(current + 1);

    const limitedAfter = afterCurrent.slice(0, maxForwardHistory);
    const prunedLocations = [...beforeCurrent, ...limitedAfter];

    return {
      locations: prunedLocations,
      newCurrent: current // Current position unchanged
    };
  }

  /**
   * Validates and corrects location history index.
   *
   * Ensures the current index is within valid bounds for the locations array.
   * Corrects invalid indices to the nearest valid value and reports whether
   * correction was needed. Essential for maintaining data integrity after
   * history modifications.
   *
   * @param locations - Array of tab locations to validate against
   * @param current - Current index to validate
   * @returns Object with corrected index and validation status
   *
   * @example
   * ```typescript
   * const { index, wasInvalid } = TabHistoryPruner.validateLocationIndex(
   *   locations,
   *   currentIndex
   * );
   * if (wasInvalid) {
   *   console.warn(`Invalid index ${currentIndex} corrected to ${index}`);
   * }
   * ```
   */
  static validateLocationIndex(
    locations: TabLocation[],
    current: number
  ): { index: number; wasInvalid: boolean } {
    if (locations.length === 0) {
      return { index: -1, wasInvalid: current !== -1 };
    }

    if (current < -1 || current >= locations.length) {
      // Invalid index, correct to last valid position
      const correctedIndex = Math.max(-1, Math.min(locations.length - 1, current));
      return { index: correctedIndex, wasInvalid: true };
    }

    return { index: current, wasInvalid: false };
  }
}