/**
 * ErrorContext - Global Error State Management
 *
 * Provides centralized error handling with:
 * - Error queue for multiple errors
 * - Toast notifications for transient errors
 * - Persistent errors that require user action
 * - Error dismissal and retry callbacks
 */

import React, { createContext, useContext, useCallback, useReducer, useMemo, useRef, ReactNode } from 'react';
import { AppError, ErrorSeverity, isAuthError, isCriticalError } from '@/types/errors';
import { logError } from '@/services/errorHandler';

// Maximum number of errors to keep in history
const MAX_ERROR_HISTORY = 50;

// Auto-dismiss timeout for non-critical errors (ms)
const AUTO_DISMISS_TIMEOUT = 5000;

// Error with display metadata
interface DisplayableError extends AppError {
  isVisible: boolean;
  autoDismiss: boolean;
  onRetry?: () => void;
  onDismiss?: () => void;
}

// State shape
interface ErrorContextState {
  // Current visible error (top of queue)
  currentError: DisplayableError | null;

  // All pending errors
  errorQueue: DisplayableError[];

  // Error history (for debugging)
  errorHistory: AppError[];

  // Global loading state for error recovery
  isRecovering: boolean;
}

// Actions
type ErrorAction =
  | { type: 'ADD_ERROR'; payload: DisplayableError }
  | { type: 'DISMISS_CURRENT' }
  | { type: 'DISMISS_ALL' }
  | { type: 'DISMISS_BY_ID'; payload: string }
  | { type: 'SET_RECOVERING'; payload: boolean }
  | { type: 'CLEAR_HISTORY' };

// Initial state
const initialState: ErrorContextState = {
  currentError: null,
  errorQueue: [],
  errorHistory: [],
  isRecovering: false,
};

// Reducer
function errorReducer(state: ErrorContextState, action: ErrorAction): ErrorContextState {
  switch (action.type) {
    case 'ADD_ERROR': {
      const newError = action.payload;

      // Add to history
      const newHistory = [newError, ...state.errorHistory].slice(0, MAX_ERROR_HISTORY);

      // If no current error, set this as current
      if (!state.currentError) {
        return {
          ...state,
          currentError: { ...newError, isVisible: true },
          errorHistory: newHistory,
        };
      }

      // Add to queue
      return {
        ...state,
        errorQueue: [...state.errorQueue, newError],
        errorHistory: newHistory,
      };
    }

    case 'DISMISS_CURRENT': {
      // Get next error from queue
      const [nextError, ...remainingQueue] = state.errorQueue;

      return {
        ...state,
        currentError: nextError ? { ...nextError, isVisible: true } : null,
        errorQueue: remainingQueue,
      };
    }

    case 'DISMISS_ALL': {
      return {
        ...state,
        currentError: null,
        errorQueue: [],
      };
    }

    case 'DISMISS_BY_ID': {
      const errorId = action.payload;

      // If current error matches, dismiss it
      if (state.currentError?.errorId === errorId) {
        const [nextError, ...remainingQueue] = state.errorQueue;
        return {
          ...state,
          currentError: nextError ? { ...nextError, isVisible: true } : null,
          errorQueue: remainingQueue,
        };
      }

      // Remove from queue
      return {
        ...state,
        errorQueue: state.errorQueue.filter((e) => e.errorId !== errorId),
      };
    }

    case 'SET_RECOVERING': {
      return {
        ...state,
        isRecovering: action.payload,
      };
    }

    case 'CLEAR_HISTORY': {
      return {
        ...state,
        errorHistory: [],
      };
    }

    default:
      return state;
  }
}

// Context value shape
interface ErrorContextValue {
  // State
  currentError: DisplayableError | null;
  errorQueue: DisplayableError[];
  errorHistory: AppError[];
  isRecovering: boolean;
  hasErrors: boolean;
  errorCount: number;

  // Actions
  showError: (
    error: AppError,
    options?: {
      autoDismiss?: boolean;
      onRetry?: () => void;
      onDismiss?: () => void;
    }
  ) => void;
  dismissCurrent: () => void;
  dismissAll: () => void;
  dismissById: (errorId: string) => void;
  clearHistory: () => void;
  setRecovering: (recovering: boolean) => void;

  // Utilities
  getErrorsByCategory: (category: string) => AppError[];
  getErrorsBySeverity: (severity: ErrorSeverity) => AppError[];
}

// Create context
const ErrorContext = createContext<ErrorContextValue | undefined>(undefined);

// Provider component
interface ErrorProviderProps {
  children: ReactNode;
  onAuthError?: (error: AppError) => void;
  onCriticalError?: (error: AppError) => void;
}

export function ErrorProvider({
  children,
  onAuthError,
  onCriticalError,
}: ErrorProviderProps) {
  const [state, dispatch] = useReducer(errorReducer, initialState);

  // Timer refs for auto-dismiss
  const dismissTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);

  // Clear existing timer
  const clearDismissTimer = useCallback(() => {
    if (dismissTimerRef.current) {
      clearTimeout(dismissTimerRef.current);
      dismissTimerRef.current = null;
    }
  }, []);

  // Schedule auto-dismiss
  const scheduleAutoDismiss = useCallback((timeout: number = AUTO_DISMISS_TIMEOUT) => {
    clearDismissTimer();
    dismissTimerRef.current = setTimeout(() => {
      dispatch({ type: 'DISMISS_CURRENT' });
    }, timeout);
  }, [clearDismissTimer]);

  // Show error
  const showError = useCallback(
    (
      error: AppError,
      options?: {
        autoDismiss?: boolean;
        onRetry?: () => void;
        onDismiss?: () => void;
      }
    ) => {
      // Log error
      logError(error);

      // Handle auth errors specially
      if (isAuthError(error) && onAuthError) {
        onAuthError(error);
        return;
      }

      // Handle critical errors specially
      if (isCriticalError(error) && onCriticalError) {
        onCriticalError(error);
      }

      // Determine auto-dismiss behavior
      const shouldAutoDismiss =
        options?.autoDismiss ??
        (error.severity !== 'critical' && error.severity !== 'error');

      const displayableError: DisplayableError = {
        ...error,
        isVisible: true,
        autoDismiss: shouldAutoDismiss,
        onRetry: options?.onRetry,
        onDismiss: options?.onDismiss,
      };

      dispatch({ type: 'ADD_ERROR', payload: displayableError });

      // Schedule auto-dismiss if applicable
      if (shouldAutoDismiss && !state.currentError) {
        scheduleAutoDismiss();
      }
    },
    [state.currentError, onAuthError, onCriticalError, scheduleAutoDismiss]
  );

  // Dismiss current error
  const dismissCurrent = useCallback(() => {
    clearDismissTimer();

    // Call onDismiss callback if provided
    state.currentError?.onDismiss?.();

    dispatch({ type: 'DISMISS_CURRENT' });

    // Schedule auto-dismiss for next error if applicable
    const nextError = state.errorQueue[0];
    if (nextError?.autoDismiss) {
      scheduleAutoDismiss();
    }
  }, [state.currentError, state.errorQueue, clearDismissTimer, scheduleAutoDismiss]);

  // Dismiss all errors
  const dismissAll = useCallback(() => {
    clearDismissTimer();
    dispatch({ type: 'DISMISS_ALL' });
  }, [clearDismissTimer]);

  // Dismiss by ID
  const dismissById = useCallback(
    (errorId: string) => {
      if (state.currentError?.errorId === errorId) {
        dismissCurrent();
      } else {
        dispatch({ type: 'DISMISS_BY_ID', payload: errorId });
      }
    },
    [state.currentError, dismissCurrent]
  );

  // Clear history
  const clearHistory = useCallback(() => {
    dispatch({ type: 'CLEAR_HISTORY' });
  }, []);

  // Set recovering state
  const setRecovering = useCallback((recovering: boolean) => {
    dispatch({ type: 'SET_RECOVERING', payload: recovering });
  }, []);

  // Get errors by category
  const getErrorsByCategory = useCallback(
    (category: string) => {
      return state.errorHistory.filter((e) => e.category === category);
    },
    [state.errorHistory]
  );

  // Get errors by severity
  const getErrorsBySeverity = useCallback(
    (severity: ErrorSeverity) => {
      return state.errorHistory.filter((e) => e.severity === severity);
    },
    [state.errorHistory]
  );

  // Context value
  const value = useMemo<ErrorContextValue>(
    () => ({
      currentError: state.currentError,
      errorQueue: state.errorQueue,
      errorHistory: state.errorHistory,
      isRecovering: state.isRecovering,
      hasErrors: state.currentError !== null || state.errorQueue.length > 0,
      errorCount: (state.currentError ? 1 : 0) + state.errorQueue.length,
      showError,
      dismissCurrent,
      dismissAll,
      dismissById,
      clearHistory,
      setRecovering,
      getErrorsByCategory,
      getErrorsBySeverity,
    }),
    [
      state.currentError,
      state.errorQueue,
      state.errorHistory,
      state.isRecovering,
      showError,
      dismissCurrent,
      dismissAll,
      dismissById,
      clearHistory,
      setRecovering,
      getErrorsByCategory,
      getErrorsBySeverity,
    ]
  );

  return (
    <ErrorContext.Provider value={value}>{children}</ErrorContext.Provider>
  );
}

// Hook to use error context
export function useErrorContext(): ErrorContextValue {
  const context = useContext(ErrorContext);
  if (!context) {
    throw new Error('useErrorContext must be used within an ErrorProvider');
  }
  return context;
}

// Convenience hook for showing errors
export function useShowError() {
  const { showError } = useErrorContext();
  return showError;
}

// Safe hook that returns undefined if not in ErrorProvider (for optional use)
export function useShowErrorSafe() {
  const context = useContext(ErrorContext);
  return context?.showError;
}

// Convenience hook for current error
export function useCurrentError() {
  const { currentError, dismissCurrent } = useErrorContext();
  return { error: currentError, dismiss: dismissCurrent };
}

export default ErrorContext;
