/**
 * Refactored FileContext with reducer pattern and normalized state
 * 
 * PERFORMANCE IMPROVEMENTS:
 * - Normalized state: File objects stored in refs, only IDs in state
 * - Pure reducer: No object creation in reducer functions
 * - Split contexts: StateContext vs ActionsContext prevents unnecessary rerenders
 * - Individual selector hooks: Avoid selector object recreation
 * - Stable actions: useCallback + stateRef prevents action recreation
 * - Throttled persistence: Debounced localStorage writes
 * - Proper resource cleanup: Automatic blob URL revocation
 * 
 * USAGE:
 * - State access: useFileState(), useFileRecord(), useFileSelection()
 * - Actions only: useFileActions(), useFileManagement(), useViewerActions()
 * - Combined: useFileContext() (legacy - causes rerenders on any state change)
 * - FileRecord is the new lightweight "processed file" - no heavy processing needed
 * 
 * PERFORMANCE NOTES:
 * - useFileState() still rerenders on ANY state change (selectors object recreation)
 * - For list UIs: consider ids-only context or use-context-selector
 * - Individual hooks (useFileRecord) are the most performant option
 */

import React, { createContext, useContext, useReducer, useCallback, useEffect, useRef, useMemo } from 'react';
import {
  FileContextState,
  FileContextProviderProps,
  FileContextSelectors,
  FileContextStateValue,
  FileContextActionsValue,
  FileContextActions,
  FileContextAction,
  ModeType,
  FileId,
  FileRecord,
  toFileRecord,
  revokeFileResources,
  createFileId,
  computeContentHash
} from '../types/fileContext';

// Import real services
import { EnhancedPDFProcessingService } from '../services/enhancedPDFProcessingService';
import { thumbnailGenerationService } from '../services/thumbnailGenerationService';
import { fileStorage } from '../services/fileStorage';

// Get service instances
const enhancedPDFProcessingService = EnhancedPDFProcessingService.getInstance();

// Initial state
const initialFileContextState: FileContextState = {
  files: {
    ids: [],
    byId: {}
  },
  ui: {
    currentMode: 'pageEditor' as ModeType,
    selectedFileIds: [],
    selectedPageNumbers: [],
    isProcessing: false,
    processingProgress: 0,
    hasUnsavedChanges: false,
    pendingNavigation: null,
    showNavigationWarning: false
  }
};

// Reducer
function fileContextReducer(state: FileContextState, action: FileContextAction): FileContextState {
  switch (action.type) {
    case 'ADD_FILES': {
      const { fileRecords } = action.payload;
      const newIds: FileId[] = [];
      const newById: Record<FileId, FileRecord> = { ...state.files.byId };
      
      fileRecords.forEach(record => {
        // Only add if not already present (dedupe by stable ID)
        if (!newById[record.id]) {
          newIds.push(record.id);
          newById[record.id] = record;
        }
      });
      
      return {
        ...state,
        files: {
          ids: [...state.files.ids, ...newIds],
          byId: newById
        }
      };
    }
    
    case 'REMOVE_FILES': {
      const { fileIds } = action.payload;
      const remainingIds = state.files.ids.filter(id => !fileIds.includes(id));
      const newById = { ...state.files.byId };
      
      // Clean up removed files
      fileIds.forEach(id => {
        const record = newById[id];
        if (record) {
          revokeFileResources(record);
          delete newById[id];
        }
      });
      
      return {
        ...state,
        files: {
          ids: remainingIds,
          byId: newById
        },
        ui: {
          ...state.ui,
          selectedFileIds: state.ui.selectedFileIds.filter(id => !fileIds.includes(id))
        }
      };
    }
    
    case 'UPDATE_FILE_RECORD': {
      const { id, updates } = action.payload;
      const existingRecord = state.files.byId[id];
      if (!existingRecord) return state;
      
      // Immutable merge supports all FileRecord fields including contentHash, hashStatus
      return {
        ...state,
        files: {
          ...state.files,
          byId: {
            ...state.files.byId,
            [id]: { ...existingRecord, ...updates }
          }
        }
      };
    }

    case 'SET_CURRENT_MODE': {
      return {
        ...state,
        ui: {
          ...state.ui,
          currentMode: action.payload
        }
      };
    }
    
    
    case 'SET_SELECTED_FILES': {
      return {
        ...state,
        ui: {
          ...state.ui,
          selectedFileIds: action.payload.fileIds
        }
      };
    }
    
    case 'SET_SELECTED_PAGES': {
      return {
        ...state,
        ui: {
          ...state.ui,
          selectedPageNumbers: action.payload.pageNumbers
        }
      };
    }
    
    case 'CLEAR_SELECTIONS': {
      return {
        ...state,
        ui: {
          ...state.ui,
          selectedFileIds: [],
          selectedPageNumbers: []
        }
      };
    }
    
    case 'SET_PROCESSING': {
      return {
        ...state,
        ui: {
          ...state.ui,
          isProcessing: action.payload.isProcessing,
          processingProgress: action.payload.progress || 0
        }
      };
    }
    
    case 'SET_UNSAVED_CHANGES': {
      return {
        ...state,
        ui: {
          ...state.ui,
          hasUnsavedChanges: action.payload.hasChanges
        }
      };
    }
    
    case 'SET_PENDING_NAVIGATION': {
      return {
        ...state,
        ui: {
          ...state.ui,
          pendingNavigation: action.payload.navigationFn
        }
      };
    }
    
    case 'SHOW_NAVIGATION_WARNING': {
      return {
        ...state,
        ui: {
          ...state.ui,
          showNavigationWarning: action.payload.show
        }
      };
    }
    
    
    case 'RESET_CONTEXT': {
      // Clean up all resources before reset
      Object.values(state.files.byId).forEach(revokeFileResources);
      return { ...initialFileContextState };
    }
    
    default:
      return state;
  }
}

// Split contexts for performance
const FileStateContext = createContext<FileContextStateValue | undefined>(undefined);
const FileActionsContext = createContext<FileContextActionsValue | undefined>(undefined);

// Legacy context for backward compatibility
const FileContext = createContext<any | undefined>(undefined);

// Provider component
export function FileContextProvider({
  children,
  enableUrlSync = true,
  enablePersistence = true 
}: FileContextProviderProps) {
  const [state, dispatch] = useReducer(fileContextReducer, initialFileContextState);

  // File ref map - stores File objects outside React state
  const filesRef = useRef<Map<FileId, File>>(new Map());
  
  // Cleanup timers and refs
  const cleanupTimers = useRef<Map<string, number>>(new Map());
  const blobUrls = useRef<Set<string>>(new Set());
  const pdfDocuments = useRef<Map<string, any>>(new Map());

  // Stable state reference for selectors
  const stateRef = useRef(state);
  stateRef.current = state;

  // Stable selectors (memoized once to avoid re-renders)
  const selectors = useMemo<FileContextSelectors>(() => ({
    getFile: (id: FileId) => filesRef.current.get(id),
    
    getFiles: (ids?: FileId[]) => {
      const currentIds = ids || stateRef.current.files.ids;
      return currentIds.map(id => filesRef.current.get(id)).filter(Boolean) as File[];
    },
    
    getFileRecord: (id: FileId) => stateRef.current.files.byId[id],
    
    getFileRecords: (ids?: FileId[]) => {
      const currentIds = ids || stateRef.current.files.ids;
      return currentIds.map(id => stateRef.current.files.byId[id]).filter(Boolean);
    },
    
    getAllFileIds: () => stateRef.current.files.ids,
    
    getSelectedFiles: () => {
      return stateRef.current.ui.selectedFileIds
        .map(id => filesRef.current.get(id))
        .filter(Boolean) as File[];
    },
    
    getSelectedFileRecords: () => {
      return stateRef.current.ui.selectedFileIds
        .map(id => stateRef.current.files.byId[id])
        .filter(Boolean);
    },
    
    // Stable signature for effects - prevents unnecessary re-renders
    getFilesSignature: () => {
      return stateRef.current.files.ids
        .map(id => {
          const record = stateRef.current.files.byId[id];
          return record ? `${id}:${record.size}:${record.lastModified}` : '';
        })
        .filter(Boolean)
        .join('|');
    }
  }), []); // Empty dependency array - selectors are now stable

  // Centralized memory management
  const trackBlobUrl = useCallback((url: string) => {
    blobUrls.current.add(url);
  }, []);

  const trackPdfDocument = useCallback((fileId: string, pdfDoc: any) => {
    // Clean up existing document for this file if any
    const existing = pdfDocuments.current.get(fileId);
    if (existing && existing.destroy) {
      try {
        existing.destroy();
      } catch (error) {
        console.warn('Error destroying existing PDF document:', error);
      }
    }
    pdfDocuments.current.set(fileId, pdfDoc);
  }, []);

  const cleanupFile = useCallback(async (fileId: string) => {
    console.log('Cleaning up file:', fileId);

    try {
      // Cancel any pending cleanup timer
      const timer = cleanupTimers.current.get(fileId);
      if (timer) {
        clearTimeout(timer);
        cleanupTimers.current.delete(fileId);
      }

      // Cleanup PDF document instances (but preserve processed file cache)
      const pdfDoc = pdfDocuments.current.get(fileId);
      if (pdfDoc && pdfDoc.destroy) {
        pdfDoc.destroy();
        pdfDocuments.current.delete(fileId);
      }

    } catch (error) {
      console.warn('Error during file cleanup:', error);
    }
  }, []);

  const cleanupAllFiles = useCallback(() => {
    console.log('Cleaning up all files');

    try {
      // Clear all timers
      cleanupTimers.current.forEach(timer => clearTimeout(timer));
      cleanupTimers.current.clear();

      // Destroy all PDF documents
      pdfDocuments.current.forEach((pdfDoc, fileId) => {
        if (pdfDoc && pdfDoc.destroy) {
          try {
            pdfDoc.destroy();
          } catch (error) {
            console.warn(`Error destroying PDF document for ${fileId}:`, error);
          }
        }
      });
      pdfDocuments.current.clear();

      // Revoke all blob URLs (only blob: scheme)
      blobUrls.current.forEach(url => {
        if (url.startsWith('blob:')) {
          try {
            URL.revokeObjectURL(url);
          } catch (error) {
            console.warn('Error revoking blob URL:', error);
          }
        }
      });
      blobUrls.current.clear();

      // Clear all processing
      enhancedPDFProcessingService.clearAllProcessing();

      // Destroy thumbnails
      thumbnailGenerationService.destroy();

      // Force garbage collection hint
      if (typeof window !== 'undefined' && (window as any).gc) {
        let gc = (window as any).gc;
        setTimeout(() => gc(), 100);
      }

    } catch (error) {
      console.warn('Error during cleanup all files:', error);
    }
  }, []);

  const scheduleCleanup = useCallback((fileId: string, delay: number = 30000) => {
    // Cancel existing timer
    const existingTimer = cleanupTimers.current.get(fileId);
    if (existingTimer) {
      clearTimeout(existingTimer);
      cleanupTimers.current.delete(fileId);
    }

    // If delay is negative, just cancel (don't reschedule)
    if (delay < 0) {
      return;
    }

    // Schedule new cleanup
    const timer = window.setTimeout(() => {
      cleanupFile(fileId);
    }, delay);

    cleanupTimers.current.set(fileId, timer);
  }, [cleanupFile]);

  // Action implementations
  const addFiles = useCallback(async (files: File[]): Promise<File[]> => {
    // Generate UUID-based IDs and create records
    const fileRecords: FileRecord[] = [];
    const addedFiles: File[] = [];
    
    for (const file of files) {
      const fileId = createFileId(); // UUID-based, zero collisions
      
      // Store File in ref map
      filesRef.current.set(fileId, file);
      
      // Create record with pending hash status
      const record = toFileRecord(file, fileId);
      record.hashStatus = 'pending';
      
      fileRecords.push(record);
      addedFiles.push(file);
      
      // Optional: Persist to IndexedDB if enabled
      if (enablePersistence) {
        try {
          // Generate thumbnail and store in IndexedDB with our UUID
          import('../utils/thumbnailUtils').then(({ generateThumbnailForFile }) => {
            return generateThumbnailForFile(file);
          }).then(thumbnail => {
            return fileStorage.storeFile(file, fileId, thumbnail);
          }).then(() => {
            console.log('File persisted to IndexedDB:', fileId);
          }).catch(error => {
            console.warn('Failed to persist file to IndexedDB:', error);
          });
        } catch (error) {
          console.warn('Failed to initiate file persistence:', error);
        }
      }
      
      // Start async content hashing (don't block add operation)
      computeContentHash(file).then(contentHash => {
        // Only update if file still exists in context
        if (filesRef.current.has(fileId)) {
          updateFileRecord(fileId, {
            contentHash: contentHash || undefined, // Convert null to undefined
            hashStatus: contentHash ? 'completed' : 'failed'
          });
        }
      }).catch(() => {
        // Hash failed, update status if file still exists
        if (filesRef.current.has(fileId)) {
          updateFileRecord(fileId, { hashStatus: 'failed' });
        }
      });
    }
    
    // Only dispatch if we have new files
    if (fileRecords.length > 0) {
      dispatch({ type: 'ADD_FILES', payload: { fileRecords } });
    }

    // Return only the newly added files
    return addedFiles;
  }, [enablePersistence]); // Include enablePersistence for persistence logic

  const removeFiles = useCallback((fileIds: FileId[], deleteFromStorage: boolean = true) => {
    // Clean up Files from ref map first
    fileIds.forEach(fileId => {
      filesRef.current.delete(fileId);
      cleanupFile(fileId);
    });

    // Update state
    dispatch({ type: 'REMOVE_FILES', payload: { fileIds } });

    // Remove from IndexedDB only if requested
    if (enablePersistence && deleteFromStorage) {
      fileIds.forEach(async (fileId) => {
        try {
          await fileStorage.deleteFile(fileId);
        } catch (error) {
          console.error('Failed to remove file from storage:', error);
        }
      });
    }
  }, [enablePersistence, cleanupFile]);

  const updateFileRecord = useCallback((id: FileId, updates: Partial<FileRecord>) => {
    // Ensure immutable merge by dispatching action
    dispatch({ type: 'UPDATE_FILE_RECORD', payload: { id, updates } });
  }, []);

  // Navigation guard system functions
  const setHasUnsavedChanges = useCallback((hasChanges: boolean) => {
    dispatch({ type: 'SET_UNSAVED_CHANGES', payload: { hasChanges } });
  }, []);

  const requestNavigation = useCallback((navigationFn: () => void): boolean => {
    // Use stateRef to avoid stale closure issues with rapid state changes
    if (stateRef.current.ui.hasUnsavedChanges) {
      dispatch({ type: 'SET_PENDING_NAVIGATION', payload: { navigationFn } });
      dispatch({ type: 'SHOW_NAVIGATION_WARNING', payload: { show: true } });
      return false;
    } else {
      navigationFn();
      return true;
    }
  }, []); // No dependencies - uses stateRef for current state

  const confirmNavigation = useCallback(() => {
    // Use stateRef to get current navigation function
    if (stateRef.current.ui.pendingNavigation) {
      stateRef.current.ui.pendingNavigation();
      dispatch({ type: 'SET_PENDING_NAVIGATION', payload: { navigationFn: null } });
    }
    dispatch({ type: 'SHOW_NAVIGATION_WARNING', payload: { show: false } });
  }, []); // No dependencies - uses stateRef

  const cancelNavigation = useCallback(() => {
    dispatch({ type: 'SET_PENDING_NAVIGATION', payload: { navigationFn: null } });
    dispatch({ type: 'SHOW_NAVIGATION_WARNING', payload: { show: false } });
  }, []);

  // Memoized actions to prevent re-renders
  const actions = useMemo<FileContextActions>(() => ({
    addFiles,
    removeFiles,
    updateFileRecord,
    clearAllFiles: () => {
      cleanupAllFiles();
      filesRef.current.clear();
      dispatch({ type: 'RESET_CONTEXT' });
    },
    setCurrentMode: (mode: ModeType) => dispatch({ type: 'SET_CURRENT_MODE', payload: mode }),
    setSelectedFiles: (fileIds: FileId[]) => dispatch({ type: 'SET_SELECTED_FILES', payload: { fileIds } }),
    setSelectedPages: (pageNumbers: number[]) => dispatch({ type: 'SET_SELECTED_PAGES', payload: { pageNumbers } }),
    clearSelections: () => dispatch({ type: 'CLEAR_SELECTIONS' }),
    setProcessing: (isProcessing: boolean, progress = 0) => dispatch({ type: 'SET_PROCESSING', payload: { isProcessing, progress } }),
    setHasUnsavedChanges,
    resetContext: () => {
      cleanupAllFiles();
      filesRef.current.clear();
      dispatch({ type: 'RESET_CONTEXT' });
    },
    // Legacy compatibility
    setMode: (mode: ModeType) => dispatch({ type: 'SET_CURRENT_MODE', payload: mode }),
    confirmNavigation,
    cancelNavigation
  }), [addFiles, removeFiles, cleanupAllFiles, setHasUnsavedChanges, confirmNavigation, cancelNavigation]);

  // Split context values to minimize re-renders
  const stateValue = useMemo<FileContextStateValue>(() => ({
    state,
    selectors
  }), [state]); // selectors are now stable, no need to depend on them

  const actionsValue = useMemo<FileContextActionsValue>(() => ({
    actions,
    dispatch
  }), [actions]);

  // Legacy context value for backward compatibility
  const legacyContextValue = useMemo(() => ({
    ...state,
    ...state.ui,
    // Action compatibility layer
    addFiles,
    removeFiles,
    updateFileRecord,
    clearAllFiles: actions.clearAllFiles,
    setCurrentMode: actions.setCurrentMode,
    setSelectedFiles: actions.setSelectedFiles,
    setSelectedPages: actions.setSelectedPages,
    clearSelections: actions.clearSelections,
    setHasUnsavedChanges,
    requestNavigation,
    confirmNavigation,
    cancelNavigation,
    trackBlobUrl,
    trackPdfDocument,
    cleanupFile,
    scheduleCleanup,
    // Missing operation functions (stubs)
    recordOperation: () => { console.warn('recordOperation is deprecated'); },
    markOperationApplied: () => { console.warn('markOperationApplied is deprecated'); },
    markOperationFailed: () => { console.warn('markOperationFailed is deprecated'); },
    // Computed properties that components expect
    get activeFiles() { return selectors.getFiles(); }, // Getter to avoid creating new arrays on every render
    // Selectors
    ...selectors
  }), [state, actions, addFiles, removeFiles, updateFileRecord, setHasUnsavedChanges, requestNavigation, confirmNavigation, cancelNavigation, trackBlobUrl, trackPdfDocument, cleanupFile, scheduleCleanup]); // Removed selectors dependency

  // Cleanup on unmount
  useEffect(() => {
    return () => {
      console.log('FileContext unmounting - cleaning up all resources');
      cleanupAllFiles();
    };
  }, [cleanupAllFiles]);

  return (
    <FileStateContext.Provider value={stateValue}>
      <FileActionsContext.Provider value={actionsValue}>
        <FileContext.Provider value={legacyContextValue}>
          {children}
        </FileContext.Provider>
      </FileActionsContext.Provider>
    </FileStateContext.Provider>
  );
}

// New hooks for split contexts (prevent unnecessary re-renders)
export function useFileState() {
  const context = useContext(FileStateContext);
  if (!context) {
    throw new Error('useFileState must be used within a FileContextProvider');
  }
  return context;
}

export function useFileActions() {
  const context = useContext(FileActionsContext);
  if (!context) {
    throw new Error('useFileActions must be used within a FileContextProvider');
  }
  return context;
}

// Legacy hook for backward compatibility
export function useFileContext(): any {
  const context = useContext(FileContext);
  if (!context) {
    throw new Error('useFileContext must be used within a FileContextProvider');
  }
  return context;
}

// Helper hooks for specific aspects
export function useCurrentFile() {
  const { state, selectors } = useFileState();
  
  const primaryFileId = state.files.ids[0];
  return useMemo(() => ({
    file: primaryFileId ? selectors.getFile(primaryFileId) : undefined,
    record: primaryFileId ? selectors.getFileRecord(primaryFileId) : undefined
  }), [primaryFileId]); // selectors are stable, don't depend on them
}

export function useFileSelection() {
  const { state } = useFileState();
  const { actions } = useFileActions();

  return {
    selectedFileIds: state.ui.selectedFileIds,
    selectedPageNumbers: state.ui.selectedPageNumbers,
    setSelectedFiles: actions.setSelectedFiles,
    setSelectedPages: actions.setSelectedPages,
    clearSelections: actions.clearSelections
  };
}

// Legacy compatibility hooks - provide stubs for removed functionality
export function useToolFileSelection() {
  const { state, selectors } = useFileState();
  const { actions } = useFileActions();
  
  // Memoize selectedFiles to avoid recreating arrays
  const selectedFiles = useMemo(() => {
    return selectors.getSelectedFiles();
  }, [state.ui.selectedFileIds]); // selectors are stable, don't depend on them
  
  return useMemo(() => ({
    selectedFileIds: state.ui.selectedFileIds,
    selectedPageNumbers: state.ui.selectedPageNumbers,
    selectedFiles, // Now stable - only changes when selectedFileIds actually change
    setSelectedFiles: actions.setSelectedFiles,
    setSelectedPages: actions.setSelectedPages,
    clearSelections: actions.clearSelections,
    // Tool-specific properties that components expect
    maxFiles: 10, // Default value
    isToolMode: true,
    setMaxFiles: (maxFiles: number) => { console.log('setMaxFiles called with:', maxFiles); }, // Stub with proper signature
    setIsToolMode: (isToolMode: boolean) => { console.log('setIsToolMode called with:', isToolMode); } // Stub with proper signature
  }), [selectedFiles, state.ui.selectedFileIds, state.ui.selectedPageNumbers, actions]);
}

export function useProcessedFiles() {
  const { state, selectors } = useFileState();
  
  // Create a Map-like interface for backward compatibility
  const compatibilityMap = {
    size: state.files.ids.length,
    get: (file: File) => {
      console.warn('useProcessedFiles.get is deprecated - File objects no longer have stable IDs');
      return null;
    },
    has: (file: File) => {
      console.warn('useProcessedFiles.has is deprecated - File objects no longer have stable IDs');
      return false;
    },
    set: () => {
      console.warn('processedFiles.set is deprecated - use FileRecord updates instead');
    }
  };
  
  return {
    processedFiles: compatibilityMap, // Map-like interface for backward compatibility
    getProcessedFile: (file: File) => {
      console.warn('getProcessedFile is deprecated - File objects no longer have stable IDs');
      return null;
    },
    updateProcessedFile: () => {
      console.warn('updateProcessedFile is deprecated - processed files are now stored in FileRecord');
    }
  };
}

export function useFileManagement() {
  const { actions } = useFileActions();
  return {
    addFiles: actions.addFiles,
    removeFiles: actions.removeFiles,
    clearAllFiles: actions.clearAllFiles
  };
}