/**
 * Cache manager for handling multiple cache types with LRU eviction and statistics
 */

import type {
  CacheStats,
  ManifestResponse,
  VideoMetadata,
} from "../types/index.js";

export class CacheManager {
  private segmentCache = new Map<string, ArrayBuffer>();
  private metadataCache = new Map<string, VideoMetadata>();
  private manifestCache = new Map<string, ManifestResponse>();
  private initSegmentCache = new Map<string, ArrayBuffer>();
  private cacheAccessOrder: string[] = [];

  // Cache performance tracking
  private cacheHits = 0;
  private cacheMisses = 0;
  private totalRequests = 0;

  constructor(private maxSize: number) {}

  /**
   * Cache a segment with LRU eviction
   */
  cacheSegment(cacheKey: string, buffer: ArrayBuffer): void {
    // Implement LRU eviction
    if (this.segmentCache.size >= this.maxSize) {
      const firstKey = this.segmentCache.keys().next().value;
      if (firstKey) {
        this.segmentCache.delete(firstKey);
        // Remove from access order tracking
        const index = this.cacheAccessOrder.indexOf(firstKey);
        if (index > -1) {
          this.cacheAccessOrder.splice(index, 1);
        }
      }
    }

    this.segmentCache.set(cacheKey, buffer);

    // Track access order for LRU analytics
    const existingIndex = this.cacheAccessOrder.indexOf(cacheKey);
    if (existingIndex > -1) {
      this.cacheAccessOrder.splice(existingIndex, 1);
    }
    this.cacheAccessOrder.push(cacheKey);
  }

  /**
   * Get a segment from cache
   */
  getSegment(cacheKey: string): ArrayBuffer | undefined {
    const cached = this.segmentCache.get(cacheKey);
    if (cached) {
      this.cacheHits++;
      // Update access order for LRU
      const index = this.cacheAccessOrder.indexOf(cacheKey);
      if (index > -1) {
        this.cacheAccessOrder.splice(index, 1);
        this.cacheAccessOrder.push(cacheKey);
      }
      return cached;
    }
    this.cacheMisses++;
    return undefined;
  }

  /**
   * Check if a segment exists in cache
   */
  hasSegment(cacheKey: string): boolean {
    return this.segmentCache.has(cacheKey);
  }

  /**
   * Cache metadata
   */
  cacheMetadata(url: string, metadata: VideoMetadata): void {
    this.metadataCache.set(url, metadata);
  }

  /**
   * Get metadata from cache
   */
  getMetadata(url: string): VideoMetadata | undefined {
    const cached = this.metadataCache.get(url);
    if (cached) {
      this.cacheHits++;
      return cached;
    }
    this.cacheMisses++;
    return undefined;
  }

  /**
   * Cache manifest
   */
  cacheManifest(url: string, manifest: ManifestResponse): void {
    this.manifestCache.set(url, manifest);
  }

  /**
   * Get manifest from cache
   */
  getManifest(url: string): ManifestResponse | undefined {
    const cached = this.manifestCache.get(url);
    if (cached) {
      this.cacheHits++;
      return cached;
    }
    this.cacheMisses++;
    return undefined;
  }

  /**
   * Cache init segment
   */
  cacheInitSegment(cacheKey: string, buffer: ArrayBuffer): void {
    this.initSegmentCache.set(cacheKey, buffer);
  }

  /**
   * Get init segment from cache
   */
  getInitSegment(cacheKey: string): ArrayBuffer | undefined {
    const cached = this.initSegmentCache.get(cacheKey);
    if (cached) {
      this.cacheHits++;
      return cached;
    }
    this.cacheMisses++;
    return undefined;
  }

  /**
   * Get comprehensive cache statistics
   */
  getCacheStats(): CacheStats {
    this.totalRequests = this.cacheHits + this.cacheMisses;
    const hitRate =
      this.totalRequests > 0 ? this.cacheHits / this.totalRequests : 0;
    const efficiency =
      this.segmentCache.size > 0 ? this.cacheHits / this.segmentCache.size : 0;

    return {
      size: this.segmentCache.size,
      maxSize: this.maxSize,
      hitRate: hitRate,
      efficiency: efficiency,
      totalRequests: this.totalRequests,
      recentKeys: this.cacheAccessOrder.slice(-5), // Last 5 accessed keys
    };
  }

  /**
   * Clear all caches
   */
  clearAll(): void {
    this.segmentCache.clear();
    this.metadataCache.clear();
    this.manifestCache.clear();
    this.initSegmentCache.clear();
    this.cacheAccessOrder = [];
    this.cacheHits = 0;
    this.cacheMisses = 0;
    this.totalRequests = 0;
  }

  /**
   * Get cache sizes for each cache type
   */
  getCacheSizes(): {
    segments: number;
    metadata: number;
    manifests: number;
    initSegments: number;
  } {
    return {
      segments: this.segmentCache.size,
      metadata: this.metadataCache.size,
      manifests: this.manifestCache.size,
      initSegments: this.initSegmentCache.size,
    };
  }

  /**
   * Clear specific cache type
   */
  clearSegmentCache(): void {
    this.segmentCache.clear();
    this.cacheAccessOrder = [];
  }

  clearMetadataCache(): void {
    this.metadataCache.clear();
  }

  clearManifestCache(): void {
    this.manifestCache.clear();
  }

  clearInitSegmentCache(): void {
    this.initSegmentCache.clear();
  }
}
