import { useRef, useCallback } from 'react';
import { supabase } from '@/integrations/supabase/client';
import { createTableQuery } from '@/lib/supabaseHelpers';
import { logger } from '@/lib/logger';
import { getErrorMessage } from '@/lib/errorHandler';
import { MODERATION_CONSTANTS } from '@/lib/moderation/constants';
import type { Database } from '@/integrations/supabase/types';

/**
 * Entity types supported by the cache
 */
type EntityType = 'rides' | 'parks' | 'companies';

/**
 * Type definitions for cached entities (can be partial)
 */
type Ride = Database['public']['Tables']['rides']['Row'];
type Park = Database['public']['Tables']['parks']['Row'];
type Company = Database['public']['Tables']['companies']['Row'];

/**
 * Discriminated union for all cached entity types
 */
type CachedEntity = Ride | Park | Company;

/**
 * Map entity type strings to their corresponding types
 * Cache stores partial entities with at least id and name
 */
interface EntityTypeMap {
  rides: Partial<Ride> & { id: string; name: string };
  parks: Partial<Park> & { id: string; name: string };
  companies: Partial<Company> & { id: string; name: string };
}

/**
 * Cache structure for entities with flexible typing
 */
interface EntityCacheStructure {
  rides: Map<string, Partial<Ride> & { id: string; name: string }>;
  parks: Map<string, Partial<Park> & { id: string; name: string }>;
  companies: Map<string, Partial<Company> & { id: string; name: string }>;
}

/**
 * Hook for managing entity name caching (rides, parks, companies)
 * 
 * Uses ref-based storage to avoid triggering re-renders while providing
 * efficient caching for entity lookups during moderation.
 * 
 * @example
 * ```tsx
 * const entityCache = useEntityCache();
 * 
 * // Get cached entity
 * const ride = entityCache.getCached('rides', rideId);
 * 
 * // Bulk fetch and cache entities
 * await entityCache.bulkFetch('rides', [id1, id2, id3]);
 * 
 * // Clear specific cache
 * entityCache.clear('rides');
 * 
 * // Clear all caches
 * entityCache.clearAll();
 * ```
 */
export function useEntityCache() {
  // Use ref to prevent re-renders on cache updates
  const cacheRef = useRef<EntityCacheStructure>({
    rides: new Map(),
    parks: new Map(),
    companies: new Map(),
  });

  /**
   * Get a cached entity by ID with type safety
   */
  const getCached = useCallback(<T extends EntityType>(
    type: T, 
    id: string
  ): EntityTypeMap[T] | undefined => {
    return cacheRef.current[type].get(id) as EntityTypeMap[T] | undefined;
  }, []);

  /**
   * Check if an entity is cached
   */
  const has = useCallback((type: EntityType, id: string): boolean => {
    return cacheRef.current[type].has(id);
  }, []);

  /**
   * Set a cached entity with LRU eviction and type safety
   */
  const setCached = useCallback(<T extends EntityType>(
    type: T, 
    id: string, 
    data: EntityTypeMap[T]
  ): void => {
    const cache = cacheRef.current[type];
    
    // LRU eviction: remove oldest entry if cache is full
    if (cache.size >= MODERATION_CONSTANTS.MAX_ENTITY_CACHE_SIZE) {
      const firstKey = cache.keys().next().value;
      if (firstKey) {
        cache.delete(firstKey);
        logger.log(`♻️ [EntityCache] Evicted ${type}/${firstKey} (LRU)`);
      }
    }
    
    cache.set(id, data);
  }, []);

  /**
   * Get uncached IDs from a list
   */
  const getUncachedIds = useCallback((type: EntityType, ids: string[]): string[] => {
    return ids.filter(id => !cacheRef.current[type].has(id));
  }, []);

  /**
   * Bulk fetch entities from the database and cache them
   * Only fetches entities that aren't already cached
   */
  const bulkFetch = useCallback(async <T extends EntityType>(
    type: T,
    ids: string[]
  ): Promise<EntityTypeMap[T][]> => {
    if (ids.length === 0) return [];

    // Filter to only uncached IDs
    const uncachedIds = getUncachedIds(type, ids);
    if (uncachedIds.length === 0) {
      // All entities are cached, return them
      return ids.map(id => getCached(type, id)).filter((item): item is EntityTypeMap[T] => item !== undefined);
    }

    try {
      let data: unknown[] | null = null;
      let error: unknown = null;

      // Use type-safe table queries
      switch (type) {
        case 'rides':
          const ridesResult = await createTableQuery('rides')
            .select('id, name, slug, park_id')
            .in('id', uncachedIds);
          data = ridesResult.data;
          error = ridesResult.error;
          break;
        
        case 'parks':
          const parksResult = await createTableQuery('parks')
            .select('id, name, slug')
            .in('id', uncachedIds);
          data = parksResult.data;
          error = parksResult.error;
          break;
        
        case 'companies':
          const companiesResult = await createTableQuery('companies')
            .select('id, name, slug, company_type')
            .in('id', uncachedIds);
          data = companiesResult.data;
          error = companiesResult.error;
          break;
        
        default:
          logger.error(`Unknown entity type: ${type}`);
          return [];
      }

      if (error) {
        logger.error(`Error fetching ${type}:`, { error: getErrorMessage(error as Error) });
        return [];
      }

      // Cache the fetched entities
      if (data) {
        (data as Array<Record<string, unknown>>).forEach((entity) => {
          if (entity && typeof entity === 'object' && 'id' in entity && 'name' in entity) {
            setCached(type, entity.id as string, entity as EntityTypeMap[T]);
          }
        });
      }

      return (data as EntityTypeMap[T][]) || [];
    } catch (error: unknown) {
      logger.error(`Failed to bulk fetch ${type}:`, { error: getErrorMessage(error) });
      return [];
    }
  }, [getCached, setCached, getUncachedIds]);

  /**
   * Fetch and cache related entities based on submission content
   * Automatically determines which entities to fetch from submission data
   */
  const fetchRelatedEntities = useCallback(async (submissions: Array<{ content?: Record<string, string | number>; submission_type?: string }>): Promise<void> => {
    const rideIds = new Set<string>();
    const parkIds = new Set<string>();
    const companyIds = new Set<string>();

    // Collect all entity IDs from submissions
    submissions.forEach(submission => {
      const content = submission.content;
      if (content && typeof content === 'object') {
        if (typeof content.ride_id === 'string') rideIds.add(content.ride_id);
        if (typeof content.park_id === 'string') parkIds.add(content.park_id);
        if (typeof content.company_id === 'string') companyIds.add(content.company_id);
        if (typeof content.entity_id === 'string') {
          if (submission.submission_type === 'ride') rideIds.add(content.entity_id);
          if (submission.submission_type === 'park') parkIds.add(content.entity_id);
          if (['manufacturer', 'operator', 'designer', 'property_owner'].includes(submission.submission_type || '')) {
            companyIds.add(content.entity_id);
          }
        }
        if (typeof content.manufacturer_id === 'string') companyIds.add(content.manufacturer_id);
        if (typeof content.designer_id === 'string') companyIds.add(content.designer_id);
        if (typeof content.operator_id === 'string') companyIds.add(content.operator_id);
        if (typeof content.property_owner_id === 'string') companyIds.add(content.property_owner_id);
      }
    });

    // Fetch all entities in parallel
    const fetchPromises: Promise<any[]>[] = [];

    if (rideIds.size > 0) {
      fetchPromises.push(bulkFetch('rides', Array.from(rideIds)));
    }
    if (parkIds.size > 0) {
      fetchPromises.push(bulkFetch('parks', Array.from(parkIds)));
    }
    if (companyIds.size > 0) {
      fetchPromises.push(bulkFetch('companies', Array.from(companyIds)));
    }

    await Promise.all(fetchPromises);
  }, [bulkFetch]);

  /**
   * Clear a specific entity type cache
   */
  const clear = useCallback((type: EntityType): void => {
    cacheRef.current[type].clear();
  }, []);

  /**
   * Clear all entity caches
   */
  const clearAll = useCallback((): void => {
    cacheRef.current.rides.clear();
    cacheRef.current.parks.clear();
    cacheRef.current.companies.clear();
  }, []);

  /**
   * Get cache size for a specific type
   */
  const getSize = useCallback((type: EntityType): number => {
    return cacheRef.current[type].size;
  }, []);

  /**
   * Get total cache size across all entity types
   */
  const getTotalSize = useCallback((): number => {
    return cacheRef.current.rides.size +
           cacheRef.current.parks.size +
           cacheRef.current.companies.size;
  }, []);

  /**
   * Get direct access to cache ref (for advanced use cases)
   * Use with caution - prefer using the provided methods
   */
  const getCacheRef = useCallback(() => cacheRef.current, []);

  // Return without useMemo wrapper (OPTIMIZED)
  return {
    getCached,
    has,
    setCached,
    getUncachedIds,
    bulkFetch,
    fetchRelatedEntities,
    clear,
    clearAll,
    getSize,
    getTotalSize,
    getCacheRef,
  };
}
