import { useState, useCallback, useRef, useEffect, Dispatch, SetStateAction } from 'react'
import config from '@/config'
import { ErrorSources, ErrorType, ValidationError } from '../../../types'

interface ValidationState {
  validatingCells: Set<string>;  // Using rowIndex-fieldKey as identifier
  itemNumbers: Map<number, string>;  // Using rowIndex as key
  validatingRows: Set<number>;  // Rows currently being validated
  activeValidations: Set<string>;  // Active validations
}

export const useUpcValidation = (
  data: any[],
  setData: (updater: any[] | ((prevData: any[]) => any[])) => void,
  setValidationErrors: Dispatch<SetStateAction<Map<number, Record<string, ValidationError[]>>>>
) => {
  // Use a ref for validation state to avoid triggering re-renders
  const validationStateRef = useRef<ValidationState>({
    validatingCells: new Set(),
    itemNumbers: new Map(),
    validatingRows: new Set(),
    activeValidations: new Set()
  });
  
  // Use state only for forcing re-renders of specific cells
  const [, setValidatingCellKeys] = useState<Set<string>>(new Set());
  const [, setItemNumberUpdates] = useState<Map<number, string>>(new Map());
  const [validatingRows, setValidatingRows] = useState<Set<number>>(new Set());
  const [, setIsValidatingUpc] = useState(false);
  
  // Cache for UPC validation results
  const processedUpcMapRef = useRef(new Map<string, string>());
  const initialUpcValidationStartedRef = useRef(false);
  const initialUpcValidationDoneRef = useRef(false);
  
  // Helper to create cell key
  const getCellKey = (rowIndex: number, fieldKey: string) => `${rowIndex}-${fieldKey}`;
  
  // Start validating a cell
  const startValidatingCell = useCallback((rowIndex: number, fieldKey: string) => {
    const cellKey = getCellKey(rowIndex, fieldKey);
    validationStateRef.current.validatingCells.add(cellKey);
    setValidatingCellKeys(new Set(validationStateRef.current.validatingCells));
  }, []);
  
  // Stop validating a cell
  const stopValidatingCell = useCallback((rowIndex: number, fieldKey: string) => {
    const cellKey = getCellKey(rowIndex, fieldKey);
    validationStateRef.current.validatingCells.delete(cellKey);
    setValidatingCellKeys(new Set(validationStateRef.current.validatingCells));
  }, []);
  
  // Update item number
  const updateItemNumber = useCallback((rowIndex: number, itemNumber: string) => {
    // CRITICAL: Update BOTH the data state and the ref
    // First, update the data directly to ensure UI consistency
    setData(prevData => {
      // Create a new copy of the data
      const newData = [...prevData];
      
      // Only update if the row exists
      if (rowIndex >= 0 && rowIndex < newData.length) {
        // First, we need a new object reference for the row to force a re-render
        newData[rowIndex] = {
          ...newData[rowIndex],
          item_number: itemNumber
        };
      }
      
      return newData;
    });
    
    // Also update the itemNumbers map AFTER the data is updated
    // This ensures the map reflects the current state of the data
    setTimeout(() => {
      // Update the ref with the same value
      validationStateRef.current.itemNumbers.set(rowIndex, itemNumber);
      
      // CRITICAL: Force a React state update to ensure all components re-render
      // Created a brand new Map object to ensure React detects the change
      const newItemNumbersMap = new Map(validationStateRef.current.itemNumbers);
      setItemNumberUpdates(newItemNumbersMap);
      
      // Force an immediate React render cycle by triggering state updates
      setValidatingCellKeys(new Set(validationStateRef.current.validatingCells));
      setValidatingRows(new Set(validationStateRef.current.validatingRows));
    }, 0);
  }, [setData]);

  const applyUpcUniqueError = useCallback((rowIndex: number, message?: string) => {
    const error: ValidationError = {
      message: message || 'Must be unique',
      level: 'error',
      source: ErrorSources.Table,
      type: ErrorType.Unique
    };

    setValidationErrors(prev => {
      const newErrors = new Map(prev);
      const existing = { ...(newErrors.get(rowIndex) || {}) };
      existing.upc = [error];
      newErrors.set(rowIndex, existing);
      return newErrors;
    });
  }, [setValidationErrors]);

  const clearUpcUniqueError = useCallback((rowIndex: number) => {
    setValidationErrors(prev => {
      const existing = prev.get(rowIndex);
      if (!existing || !existing.upc) {
        return prev;
      }

      const filtered = existing.upc.filter(err => err.type !== ErrorType.Unique);
      if (filtered.length === existing.upc.length) {
        return prev;
      }

      const newErrors = new Map(prev);
      const updated = { ...existing } as Record<string, ValidationError[]>;
      if (filtered.length > 0) {
        updated.upc = filtered;
      } else {
        delete updated.upc;
      }

      if (Object.keys(updated).length > 0) {
        newErrors.set(rowIndex, updated);
      } else {
        newErrors.delete(rowIndex);
      }

      return newErrors;
    });
  }, [setValidationErrors]);
  
  // Mark a row as no longer being validated
  const stopValidatingRow = useCallback((rowIndex: number) => {
    validationStateRef.current.validatingRows.delete(rowIndex);
    setValidatingRows(new Set(validationStateRef.current.validatingRows));
    
    // If no more rows are being validated, set global validation state to false
    if (validationStateRef.current.validatingRows.size === 0) {
      setIsValidatingUpc(false);
    }
  }, []);
  
  // Check if a specific cell is being validated
  const isValidatingCell = useCallback((rowIndex: number, fieldKey: string): boolean => {
    return validationStateRef.current.validatingCells.has(getCellKey(rowIndex, fieldKey));
  }, []);
  
  // Check if a specific row is being validated
  const isRowValidatingUpc = useCallback((rowIndex: number): boolean => {
    return validationStateRef.current.validatingRows.has(rowIndex);
  }, []);
  
  // Get item number for a row
  const getItemNumber = useCallback((rowIndex: number): string | undefined => {
    return validationStateRef.current.itemNumbers.get(rowIndex);
  }, []);

  // Fetch product by UPC from API
  const fetchProductByUpc = useCallback(async (supplierId: string, upcValue: string) => {
    try {
      console.log(`Fetching product for UPC ${upcValue} with supplier ${supplierId}`);
      const response = await fetch(`${config.apiUrl}/import/check-upc-and-generate-sku?upc=${encodeURIComponent(upcValue)}&supplierId=${encodeURIComponent(supplierId)}`);

      let payload: any = null;
      try {
        payload = await response.json();
      } catch (parseError) {
        // Non-JSON responses are treated generically below
      }

      if (response.status === 409) {
        console.log(`UPC ${upcValue} already exists`);
        return {
          error: true,
          code: 'conflict',
          message: payload?.error || 'UPC already exists',
          data: payload || undefined
        };
      }

      if (!response.ok) {
        console.error(`API error: ${response.status}`);
        return {
          error: true,
          code: 'http_error',
          message: payload?.error || `API error (${response.status})`,
          data: payload || undefined
        };
      }

      const data = payload;

      if (!data?.success) {
        return {
          error: true,
          code: 'invalid_response',
          message: data?.message || 'Unknown error'
        };
      }

      return {
        error: false,
        data: {
          itemNumber: data.itemNumber || '',
          ...data
        }
      };
    } catch (error) {
      console.error('Network error:', error);
      return { error: true, message: 'Network error' };
    }
  }, []);
  
  // Validate a UPC for a row - returns a promise that resolves when complete
  const validateUpc = useCallback(async (rowIndex: number, supplierId: string, upcValue: string) => {
    // Clear any previous validation keys for this row to avoid cancellations
    const previousKeys = Array.from(validationStateRef.current.activeValidations).filter(key => 
      key.startsWith(`${rowIndex}-`)
    );
    previousKeys.forEach(key => validationStateRef.current.activeValidations.delete(key));
    
    // Log validation start to help debug template issues
    console.log(`[UPC-DEBUG] Starting UPC validation for row ${rowIndex} with supplier ${supplierId}, upc ${upcValue}`);
    
    // IMPORTANT: Set validation state using setState to FORCE UI updates
    validationStateRef.current.validatingRows.add(rowIndex);
    setValidatingRows(new Set(validationStateRef.current.validatingRows));
    setIsValidatingUpc(true);
    
    // Start cell validation and explicitly update UI via setState
    const cellKey = getCellKey(rowIndex, 'item_number');
    validationStateRef.current.validatingCells.add(cellKey);
    setValidatingCellKeys(new Set(validationStateRef.current.validatingCells));
    
    console.log(`[UPC-DEBUG] Set loading state for row ${rowIndex}, cell key ${cellKey}`);
    console.log(`[UPC-DEBUG] Current validating rows: ${Array.from(validationStateRef.current.validatingRows).join(', ')}`);
    console.log(`[UPC-DEBUG] Current validating cells: ${Array.from(validationStateRef.current.validatingCells).join(', ')}`);
    
    try {
      // Create a unique key for this validation to track it
      const validationKey = `${rowIndex}-${supplierId}-${upcValue}`;
      validationStateRef.current.activeValidations.add(validationKey);
      
      // IMPORTANT: First update the data with the new UPC value to prevent UI flicker
      // This ensures the UPC field keeps showing the new value while validation runs
      setData(prevData => {
        const newData = [...prevData];
        if (newData[rowIndex]) {
          newData[rowIndex] = {
            ...newData[rowIndex],
            upc: upcValue
          };
        }
        return newData;
      });
      
      // Fetch the product by UPC
      console.log(`[UPC-DEBUG] Fetching product data for UPC ${upcValue} with supplier ${supplierId}`);
      const product = await fetchProductByUpc(supplierId, upcValue);
      console.log(`[UPC-DEBUG] Fetch complete for row ${rowIndex}, success: ${!product.error}`);
      
      // Check if this validation is still relevant (hasn't been superseded by another)
      if (!validationStateRef.current.activeValidations.has(validationKey)) {
        console.log(`[UPC-DEBUG] Validation ${validationKey} was cancelled`);
        return { success: false };
      }
      
      // Extract the item number from the API response - check for !error since API returns { error: boolean, data: any }
      if (product && !product.error && product.data?.itemNumber) {
        console.log(`[UPC-DEBUG] Got item number for row ${rowIndex}: ${product.data.itemNumber}`);
        updateItemNumber(rowIndex, product.data.itemNumber);

        clearUpcUniqueError(rowIndex);

        return {
          success: true,
          itemNumber: product.data.itemNumber
        };
      } else if (product && product.error) {
        console.log(`[UPC-DEBUG] UPC validation error for row ${rowIndex}: ${product.message}`);

        // Clear any existing item number value in data and internal state
        setData(prevData => {
          const newData = [...prevData];
          if (rowIndex >= 0 && rowIndex < newData.length) {
            newData[rowIndex] = {
              ...newData[rowIndex],
              item_number: ''
            };
          }
          return newData;
        });

        if (validationStateRef.current.itemNumbers.has(rowIndex)) {
          validationStateRef.current.itemNumbers.delete(rowIndex);
          setItemNumberUpdates(new Map(validationStateRef.current.itemNumbers));
          validationStateRef.current.itemNumbers = new Map(validationStateRef.current.itemNumbers);
        }

        if (product.code === 'conflict') {
          applyUpcUniqueError(rowIndex, 'Must be unique');
        }

        return { success: false };
      } else {
        // No item number found but validation was still attempted
        console.log(`[UPC-DEBUG] No item number found for UPC ${upcValue}`);

        // Clear any existing item number to show validation was attempted and failed
        if (validationStateRef.current.itemNumbers.has(rowIndex)) {
          validationStateRef.current.itemNumbers.delete(rowIndex);
          setItemNumberUpdates(new Map(validationStateRef.current.itemNumbers));
          validationStateRef.current.itemNumbers = new Map(validationStateRef.current.itemNumbers);
        }

        return { success: false };
      }
    } catch (error) {
      console.error('[UPC-DEBUG] Error validating UPC:', error);
      return { success: false };
    } finally {
      // End validation - FORCE UI update by using setState directly
      console.log(`[UPC-DEBUG] Ending validation for row ${rowIndex}`);
      
      validationStateRef.current.validatingRows.delete(rowIndex);
      setValidatingRows(new Set(validationStateRef.current.validatingRows));
      
      if (validationStateRef.current.validatingRows.size === 0) {
        setIsValidatingUpc(false);
      }
      
      validationStateRef.current.validatingCells.delete(cellKey);
      setValidatingCellKeys(new Set(validationStateRef.current.validatingCells));
      
      console.log(`[UPC-DEBUG] Cleared loading state for row ${rowIndex}`);
      console.log(`[UPC-DEBUG] Updated validating rows: ${Array.from(validationStateRef.current.validatingRows).join(', ')}`);
      console.log(`[UPC-DEBUG] Updated validating cells: ${Array.from(validationStateRef.current.validatingCells).join(', ')}`);
    }
  }, [fetchProductByUpc, updateItemNumber, setData, applyUpcUniqueError, clearUpcUniqueError]);
  
  // Apply all pending item numbers to the data state
  const applyItemNumbersToData = useCallback((callback?: (updatedRows: number[]) => void) => {
    // Skip if we have nothing to apply
    if (validationStateRef.current.itemNumbers.size === 0) {
      if (callback) callback([]);
      return;
    }
    
    // Gather all row IDs that will be updated
    const rowIds: number[] = [];
    
    // Update the data state with all item numbers
    setData(prevData => {
      const newData = [...prevData];
      
      // Apply each item number to the data
      validationStateRef.current.itemNumbers.forEach((itemNumber, rowIndex) => {
        // Ensure row exists and value has actually changed
        if (rowIndex >= 0 && rowIndex < newData.length && 
            newData[rowIndex]?.item_number !== itemNumber) {
          
          // Create a new row object to force re-rendering
          newData[rowIndex] = {
            ...newData[rowIndex],
            item_number: itemNumber
          };
          
          // Track which row was updated for the callback
          rowIds.push(rowIndex);
        }
      });
      
      return newData;
    });
    
    // Force a re-render by updating React state
    setTimeout(() => {
      setItemNumberUpdates(new Map(validationStateRef.current.itemNumbers));
    }, 0);
    
    // Call the callback with the updated row IDs
    if (callback) {
      callback(rowIds);
    }
  }, [setData]);
  
  // Batch validate all UPCs in the data
  const validateAllUPCs = useCallback(async () => {
    // Skip if we've already done the initial validation
    if (initialUpcValidationStartedRef.current) {
      console.log('Initial UPC validation already in progress or complete, skipping');
      return;
    }

    // Mark that we've started the initial validation
    initialUpcValidationStartedRef.current = true;

    console.log('Starting initial UPC validation...');
    
    // Set validation state
    setIsValidatingUpc(true);
    
    // Find all rows that have both supplier and UPC/barcode
    const rowsToValidate = data
      .map((row, index) => ({ row, index }))
      .filter(({ row }) => {
        const rowAny = row as Record<string, any>;
        const hasSupplier = rowAny.supplier;
        const hasUpc = rowAny.upc || rowAny.barcode;
        return hasSupplier && hasUpc;
      });
    
    const totalRows = rowsToValidate.length;
    console.log(`Found ${totalRows} rows with both supplier and UPC for initial validation`);
    
    if (totalRows === 0) {
      setIsValidatingUpc(false);
      initialUpcValidationDoneRef.current = true;
      return;
    }
    
    // Mark all rows as being validated
    const newValidatingRows = new Set(rowsToValidate.map(({ index }) => index));
    validationStateRef.current.validatingRows = newValidatingRows;
    setValidatingRows(newValidatingRows);
    
    try {
      // Process rows in batches for better UX
      const BATCH_SIZE = 100;
      const batches = [];
      
      // Split rows into batches
      for (let i = 0; i < rowsToValidate.length; i += BATCH_SIZE) {
        batches.push(rowsToValidate.slice(i, i + BATCH_SIZE));
      }
      
      console.log(`Processing ${batches.length} batches for ${totalRows} rows`);
      
      // Process each batch sequentially
      for (let batchIndex = 0; batchIndex < batches.length; batchIndex++) {
        const batch = batches[batchIndex];
        console.log(`Processing batch ${batchIndex + 1}/${batches.length} with ${batch.length} rows`);
        
        // Track updated rows in this batch
        const batchUpdatedRows: number[] = [];
        
        // Process all rows in current batch in parallel
        await Promise.all(
          batch.map(async ({ row, index }) => {
            try {
              const rowAny = row as Record<string, any>;
              const supplierId = rowAny.supplier.toString();
              const upcValue = (rowAny.upc || rowAny.barcode).toString();
              
              console.log(`Validating UPC in initial batch: row=${index}, supplier=${supplierId}, upc=${upcValue}`);
              
              // Mark the item_number cell as validating
              startValidatingCell(index, 'item_number');
              
              // Validate the UPC directly (don't use validateUpc to avoid duplicate UI updates)
              const cacheKey = `${supplierId}-${upcValue}`;
              
              // Check cache first
              if (processedUpcMapRef.current.has(cacheKey)) {
                const cachedItemNumber = processedUpcMapRef.current.get(cacheKey);
                if (cachedItemNumber) {
                  console.log(`Using cached item number for row ${index}: ${cachedItemNumber}`);
                  updateItemNumber(index, cachedItemNumber);
                  batchUpdatedRows.push(index);
                  return;
                }
              }
              
              // Make API call
              const result = await fetchProductByUpc(supplierId, upcValue);
              
              if (!result.error && result.data?.itemNumber) {
                const itemNumber = result.data.itemNumber;
                console.log(`Got item number from API for row ${index}: ${itemNumber}`);
                
                // Cache the result
                processedUpcMapRef.current.set(cacheKey, itemNumber);
                
                // Update item number
                updateItemNumber(index, itemNumber);
                batchUpdatedRows.push(index);
                clearUpcUniqueError(index);
              } else {
                console.warn(`No item number found for row ${index} UPC ${upcValue}`);

                // Clear any previous item numbers for the row
                setData(prevData => {
                  const newData = [...prevData];
                  if (index >= 0 && index < newData.length) {
                    newData[index] = {
                      ...newData[index],
                      item_number: ''
                    };
                  }
                  return newData;
                });

                if (validationStateRef.current.itemNumbers.has(index)) {
                  validationStateRef.current.itemNumbers.delete(index);
                  setItemNumberUpdates(new Map(validationStateRef.current.itemNumbers));
                  validationStateRef.current.itemNumbers = new Map(validationStateRef.current.itemNumbers);
                }

                if (result.error && result.code === 'conflict') {
                  applyUpcUniqueError(index, 'Must be unique');
                }
              }
            } catch (error) {
              console.error(`Error validating row ${index}:`, error);
            } finally {
              // Clear validation state
              stopValidatingCell(index, 'item_number');
              stopValidatingRow(index);
            }
          })
        );
        
        // Apply updates for this batch
        if (validationStateRef.current.itemNumbers.size > 0) {
          console.log(`Applying item numbers after batch ${batchIndex + 1}`);
          applyItemNumbersToData(updatedRowIds => {
            console.log(`Processed initial UPC validation batch ${batchIndex + 1} for rows: ${updatedRowIds.join(', ')}`);
          });
        }
        
        // Small delay between batches to update UI
        if (batchIndex < batches.length - 1) {
          await new Promise(resolve => setTimeout(resolve, 100));
        }
      }
    } catch (error) {
      console.error('Error in batch validation:', error);
    } finally {
      initialUpcValidationDoneRef.current = true;
      // Make sure all validation states are cleared
      validationStateRef.current.validatingRows.clear();
      setValidatingRows(new Set());
      setIsValidatingUpc(false);
      
      console.log('Completed initial UPC validation');
    }
  }, [
    data,
    fetchProductByUpc,
    updateItemNumber,
    startValidatingCell,
    stopValidatingCell,
    stopValidatingRow,
    applyItemNumbersToData,
    setData,
    applyUpcUniqueError,
    clearUpcUniqueError
  ]);
  
  // Run initial UPC validation when data changes
  useEffect(() => {
    if (initialUpcValidationStartedRef.current) return;

    validateAllUPCs();
  }, [data, validateAllUPCs]);

  // Return public API
  return {
    // Validation methods
    validateUpc,
    validateAllUPCs,
    
    // Cell state
    isValidatingCell,
    isRowValidatingUpc,
    
    // Row state
    validatingRows: validatingRows, // Expose as a Set to components
    
    // Item number management
    getItemNumber,
    applyItemNumbersToData,
    
    // CRITICAL: Expose the itemNumbers map directly
    itemNumbers: validationStateRef.current.itemNumbers,
    
    // Initialization state
    initialValidationDone: initialUpcValidationDoneRef.current
  };
}; 
