import { useCallback } from 'react';
import type { Field, Fields, RowHook } from '../../../types';
import type { Meta } from '../types';
import { ErrorType, ValidationError } from '../../../types';
import { RowData, isEmpty } from './validationTypes';

// Create a cache for validation results to avoid repeated validation of the same data
const validationResultCache = new Map();

// Optimize cache clearing - only clear when necessary
export const clearValidationCacheForField = (fieldKey: string, specificValue?: any) => {
  if (specificValue !== undefined) {
    // Only clear specific field-value combinations
    const specificKey = `${fieldKey}-${String(specificValue)}`;
    validationResultCache.forEach((_, key) => {
      if (key.startsWith(specificKey)) {
        validationResultCache.delete(key);
      }
    });
  } else {
    // Clear all entries for the field
    validationResultCache.forEach((_, key) => {
      if (key.startsWith(`${fieldKey}-`)) {
        validationResultCache.delete(key);
      }
    });
  }
};

// Add a special function to clear all uniqueness validation caches
export const clearAllUniquenessCaches = () => {
  // Clear cache for common unique fields
  ['item_number', 'upc', 'barcode', 'supplier_no', 'notions_no'].forEach(fieldKey => {
    clearValidationCacheForField(fieldKey);
  });
  
  // Also clear any cache entries that might involve uniqueness validation
  validationResultCache.forEach((_, key) => {
    if (key.includes('unique')) {
      validationResultCache.delete(key);
    }
  });
};

export const useFieldValidation = <T extends string>(
  fields: Fields<T>,
  rowHook?: RowHook<T>
) => {
  // Validate a single field
  const validateField = useCallback((
    value: any, 
    field: Field<T>
  ): ValidationError[] => {
    const errors: ValidationError[] = [];
    
    if (!field.validations) return errors;
    
    // Create a cache key using field key, value, and validation rules
    const cacheKey = `${field.key}-${String(value)}-${JSON.stringify(field.validations)}`;
    
    // Check cache first to avoid redundant validation
    if (validationResultCache.has(cacheKey)) {
      return validationResultCache.get(cacheKey) || [];
    }
    
    field.validations.forEach(validation => {
      switch (validation.rule) {
        case 'required':
          // Use the shared isEmpty function
          if (isEmpty(value)) {
            errors.push({
              message: validation.errorMessage || 'This field is required',
              level: validation.level || 'error',
              type: ErrorType.Required
            });
          }
          break;
          
        case 'unique':
          // Unique validation happens at table level, not here
          break;
          
        case 'regex':
          if (value !== undefined && value !== null && value !== '') {
            try {
              const regex = new RegExp(validation.value, validation.flags);
              if (!regex.test(String(value))) {
                errors.push({
                  message: validation.errorMessage,
                  level: validation.level || 'error',
                  type: ErrorType.Regex
                });
              }
            } catch (error) {
              console.error('Invalid regex in validation:', error);
            }
          }
          break;
      }
    });
    
    // Store results in cache to speed up future validations
    validationResultCache.set(cacheKey, errors);
    
    return errors;
  }, []);

  // Validate a single row
  const validateRow = useCallback(async (
    row: RowData<T>,
    rowIndex: number,
    allRows: RowData<T>[]
  ): Promise<Meta> => {
    // Run field-level validations
    const fieldErrors: Record<string, ValidationError[]> = {};
    
    fields.forEach(field => {
      const value = row[String(field.key) as keyof typeof row];
      const errors = validateField(value, field as Field<T>);
      
      if (errors.length > 0) {
        fieldErrors[String(field.key)] = errors;
      }
    });
    
    // Special validation for supplier and company fields - only apply if the field exists in fields
    if (fields.some(field => String(field.key) === 'supplier') && isEmpty(row.supplier)) {
      fieldErrors['supplier'] = [{
        message: 'Supplier is required',
        level: 'error',
        type: ErrorType.Required
      }];
    }
    
    if (fields.some(field => String(field.key) === 'company') && isEmpty(row.company)) {
      fieldErrors['company'] = [{
        message: 'Company is required',
        level: 'error',
        type: ErrorType.Required
      }];
    }
    
    // Run row hook if provided
    let rowHookResult: Meta = { 
      __index: row.__index || String(rowIndex)
    };
    if (rowHook) {
      try {
        // Call the row hook and extract only the __index property
        const result = await rowHook(row, rowIndex, allRows);
        rowHookResult.__index = result.__index || rowHookResult.__index;
      } catch (error) {
        console.error('Error in row hook:', error);
      }
    }
    
    // We no longer need to merge errors since we're not storing them in the row data
    // The calling code should handle storing errors in the validationErrors Map
    
    return {
      __index: row.__index || String(rowIndex)
    };
  }, [fields, validateField, rowHook]);

  return {
    validateField,
    validateRow,
    clearValidationCacheForField,
    clearAllUniquenessCaches
  };
}; 