/**
 * Import Session Context
 *
 * Manages import session state across the product import flow,
 * including autosave and named session management.
 */

import {
  createContext,
  useState,
  useCallback,
  useContext,
  useRef,
  useEffect,
  ReactNode,
} from 'react';
import { AuthContext } from './AuthContext';
import {
  autosaveSession,
  createSession,
  updateSession,
  deleteSession,
} from '@/services/importSessionApi';
import type {
  ImportSession,
  ImportSessionData,
  ImportSessionContextValue,
} from '@/types/importSession';
import { useValidationStore } from '@/components/product-import/steps/ValidationStep/store/validationStore';

const AUTOSAVE_DEBOUNCE_MS = 10000; // 10 seconds

const defaultContext: ImportSessionContextValue = {
  sessionId: null,
  sessionName: null,
  isDirty: false,
  lastSaved: null,
  isSaving: false,
  currentStep: 'validation',
  setCurrentStep: () => {},
  markDirty: () => {},
  saveAsNamed: async () => { throw new Error('Not initialized'); },
  save: async () => {},
  forceSave: async () => {},
  loadSession: () => {},
  clearSession: () => {},
  deleteSession: async () => {},
  setDataGetter: () => {},
  getSuggestedSessionName: () => null,
  setGlobalSelections: () => {},
  getGlobalSelections: () => undefined,
};

export const ImportSessionContext = createContext<ImportSessionContextValue>(defaultContext);

interface ImportSessionProviderProps {
  children: ReactNode;
}

export function ImportSessionProvider({ children }: ImportSessionProviderProps) {
  const { user } = useContext(AuthContext);

  // Session state
  const [sessionId, setSessionId] = useState<number | null>(null);
  const [sessionName, setSessionName] = useState<string | null>(null);
  const [isDirty, setIsDirty] = useState(false);
  const [lastSaved, setLastSaved] = useState<Date | null>(null);
  const [isSaving, setIsSaving] = useState(false);
  const [currentStep, setCurrentStep] = useState<'validation' | 'imageUpload'>('validation');

  // Refs to hold current values so closures always read fresh state
  const sessionIdRef = useRef(sessionId);
  const sessionNameRef = useRef(sessionName);
  const isDirtyRef = useRef(isDirty);
  const isSavingRef = useRef(isSaving);
  const userIdRef = useRef(user?.id);

  // Keep refs in sync with state
  useEffect(() => { sessionIdRef.current = sessionId; }, [sessionId]);
  useEffect(() => { sessionNameRef.current = sessionName; }, [sessionName]);
  useEffect(() => { isDirtyRef.current = isDirty; }, [isDirty]);
  useEffect(() => { isSavingRef.current = isSaving; }, [isSaving]);
  useEffect(() => { userIdRef.current = user?.id; }, [user?.id]);

  // Ref to hold the data getter function (set by ValidationStep/ImageUploadStep)
  const dataGetterRef = useRef<(() => ImportSessionData) | null>(null);

  // Ref to hold global selections for inclusion in autosave
  const globalSelectionsRef = useRef<import('@/components/product-import/steps/MatchColumnsStep/types').GlobalSelections | undefined>(undefined);

  // Autosave timer ref
  const autosaveTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);

  // Clear autosave timer on unmount
  useEffect(() => {
    return () => {
      if (autosaveTimerRef.current) {
        clearTimeout(autosaveTimerRef.current);
      }
    };
  }, []);

  /**
   * Set the data getter function (called by child components)
   */
  const setDataGetter = useCallback((getter: () => ImportSessionData) => {
    dataGetterRef.current = getter;
  }, []);

  const setGlobalSelections = useCallback((selections: import('@/components/product-import/steps/MatchColumnsStep/types').GlobalSelections | undefined) => {
    globalSelectionsRef.current = selections;
  }, []);

  const getGlobalSelections = useCallback(() => {
    return globalSelectionsRef.current;
  }, []);

  /**
   * Perform save operation. Reads all values from refs to avoid stale closures.
   */
  const performSave = useCallback(async (data: ImportSessionData, force = false) => {
    const userId = userIdRef.current;
    if (!userId || (!force && isSavingRef.current)) return;

    setIsSaving(true);
    isSavingRef.current = true;
    try {
      const id = sessionIdRef.current;
      if (id) {
        // Update existing session (named or unnamed)
        await updateSession(id, data);
      } else {
        // Create/update unnamed autosave session
        const result = await autosaveSession({
          user_id: userId,
          ...data,
        });
        // Store the session ID for future updates
        setSessionId(result.id);
        sessionIdRef.current = result.id;
      }
      setLastSaved(new Date());
      setIsDirty(false);
      isDirtyRef.current = false;
    } catch (error) {
      console.error('Autosave failed:', error);
      // Don't clear dirty flag - will retry on next change
    } finally {
      setIsSaving(false);
      isSavingRef.current = false;
    }
  }, []);

  /**
   * Mark the session as having unsaved changes.
   * Schedules a debounced autosave.
   */
  const markDirty = useCallback(() => {
    setIsDirty(true);
    isDirtyRef.current = true;

    // Schedule autosave
    if (autosaveTimerRef.current) {
      clearTimeout(autosaveTimerRef.current);
    }

    autosaveTimerRef.current = setTimeout(() => {
      if (dataGetterRef.current && userIdRef.current) {
        const data = dataGetterRef.current();
        performSave(data);
      }
    }, AUTOSAVE_DEBOUNCE_MS);
  }, [performSave]);

  /**
   * Save current state (autosave or update named session)
   */
  const save = useCallback(async () => {
    if (!dataGetterRef.current || !userIdRef.current) return;

    const data = dataGetterRef.current();
    await performSave(data);
  }, [performSave]);

  /**
   * Force immediate save, bypassing debounce (used before closing).
   * Always saves if there's data, regardless of dirty state — ensures
   * at least one save happens even if no edits were made.
   */
  const forceSave = useCallback(async () => {
    // Clear any pending autosave timer
    if (autosaveTimerRef.current) {
      clearTimeout(autosaveTimerRef.current);
      autosaveTimerRef.current = null;
    }

    if (!dataGetterRef.current || !userIdRef.current) return;

    const data = dataGetterRef.current();
    await performSave(data, true);
  }, [performSave]);

  /**
   * Get a suggested session name based on data (Company - Line format)
   */
  const getSuggestedSessionName = useCallback((): string | null => {
    if (!dataGetterRef.current) return null;

    try {
      const data = dataGetterRef.current();
      if (!data.data || data.data.length === 0) return null;

      // Build lookup helpers from the validation store
      const storeState = useValidationStore.getState();
      const companyField = storeState.fields.find((f) => f.key === 'company');
      const companyOptions = companyField?.fieldType && 'options' in companyField.fieldType
        ? (companyField.fieldType.options as { label: string; value: string }[])
        : [];

      const resolveCompany = (id: string) =>
        companyOptions.find((o) => String(o.value) === id)?.label ?? id;

      const resolveLine = (companyId: string, lineId: string) => {
        const cached = storeState.productLinesCache?.get(String(companyId));
        return cached?.find((o) => String(o.value) === lineId)?.label ?? lineId;
      };

      // Find the first row with both company and line
      for (const row of data.data) {
        const company = row.company;
        const line = row.line;

        if (company && line) {
          const companyStr = String(company).trim();
          const lineStr = String(line).trim();

          if (companyStr && lineStr) {
            return `${resolveCompany(companyStr)} - ${resolveLine(companyStr, lineStr)}`;
          }
        }
      }

      // If no row has both, try company alone
      for (const row of data.data) {
        const company = row.company;
        if (company) {
          const companyStr = String(company).trim();
          if (companyStr) {
            return resolveCompany(companyStr);
          }
        }
      }

      return null;
    } catch {
      return null;
    }
  }, []);

  /**
   * Save current state as a named session.
   * If already tracking a named session, updates it. Otherwise creates a new one.
   */
  const saveAsNamed = useCallback(async (name: string): Promise<ImportSession> => {
    if (!dataGetterRef.current || !userIdRef.current) {
      throw new Error('Cannot save: no data or user');
    }

    setIsSaving(true);
    isSavingRef.current = true;
    try {
      const data = dataGetterRef.current();
      const existingId = sessionIdRef.current;
      let result: ImportSession;

      if (existingId) {
        // Existing session (named or unnamed autosave) - update it with the name
        result = await updateSession(existingId, { ...data, name });
      } else {
        // No session exists yet - create a new named session
        result = await createSession({
          user_id: userIdRef.current,
          name,
          ...data,
        });
      }

      // Update context to track this named session
      setSessionId(result.id);
      sessionIdRef.current = result.id;
      setSessionName(name);
      sessionNameRef.current = name;
      setLastSaved(new Date());
      setIsDirty(false);
      isDirtyRef.current = false;

      return result;
    } finally {
      setIsSaving(false);
      isSavingRef.current = false;
    }
  }, []);

  /**
   * Load a session (called when restoring from saved sessions list)
   */
  const loadSession = useCallback((session: ImportSession) => {
    setSessionId(session.id);
    sessionIdRef.current = session.id;
    setSessionName(session.name);
    sessionNameRef.current = session.name;
    setCurrentStep(session.current_step);
    setLastSaved(new Date(session.updated_at));
    setIsDirty(false);
    isDirtyRef.current = false;
  }, []);

  /**
   * Clear current session state (for starting fresh)
   */
  const clearSession = useCallback(() => {
    if (autosaveTimerRef.current) {
      clearTimeout(autosaveTimerRef.current);
    }
    setSessionId(null);
    sessionIdRef.current = null;
    setSessionName(null);
    sessionNameRef.current = null;
    setIsDirty(false);
    isDirtyRef.current = false;
    setLastSaved(null);
    setCurrentStep('validation');
    dataGetterRef.current = null;
  }, []);

  /**
   * Delete the current session (called on successful submit)
   */
  const handleDeleteSession = useCallback(async () => {
    const id = sessionIdRef.current;
    if (!id) return;

    try {
      await deleteSession(id);
      clearSession();
    } catch (error) {
      console.error('Failed to delete session:', error);
      // Clear local state anyway
      clearSession();
    }
  }, [clearSession]);

  return (
    <ImportSessionContext.Provider
      value={{
        sessionId,
        sessionName,
        isDirty,
        lastSaved,
        isSaving,
        currentStep,
        setCurrentStep,
        markDirty,
        saveAsNamed,
        save,
        forceSave,
        loadSession,
        clearSession,
        deleteSession: handleDeleteSession,
        setDataGetter,
        getSuggestedSessionName,
        setGlobalSelections,
        getGlobalSelections,
      }}
    >
      {children}
    </ImportSessionContext.Provider>
  );
}

/**
 * Hook to use the import session context
 */
export function useImportSession() {
  const context = useContext(ImportSessionContext);
  if (!context) {
    throw new Error('useImportSession must be used within an ImportSessionProvider');
  }
  return context;
}
