/**
 * useValidationActions Hook
 *
 * Provides validation logic and wraps store actions.
 * Handles field validation, unique validation, and row operations.
 *
 * PERFORMANCE NOTE: Uses getState() inside callbacks instead of subscriptions.
 * This is critical because subscribing to rows/fields/errors would cause the
 * parent component to re-render on EVERY cell change, cascading to all children.
 * By using getState(), we read fresh values at call-time without subscriptions.
 */

import { useCallback } from 'react';
import { useValidationStore } from '../store/validationStore';
import { ErrorSource, ErrorType, type ValidationError, type RowData } from '../store/types';
import type { Field, Validation } from '../../../types';

// Debounce cache for validation
const validationCache = new Map<string, NodeJS.Timeout>();

/**
 * Check if a value is empty
 */
const isEmpty = (value: unknown): boolean => {
  if (value === undefined || value === null) return true;
  if (typeof value === 'string' && value.trim() === '') return true;
  if (Array.isArray(value) && value.length === 0) return true;
  return false;
};

/**
 * Validate a single field value against its validation rules
 */
const validateFieldValue = (
  value: unknown,
  field: Field<string>,
  allRows: RowData[],
  currentRowIndex: number
): ValidationError | null => {
  const validations = field.validations || [];

  for (const validation of validations) {
    // Required validation
    if (validation.rule === 'required') {
      if (isEmpty(value)) {
        return {
          message: validation.errorMessage || 'This field is required',
          level: validation.level || 'error',
          source: ErrorSource.Row,
          type: ErrorType.Required,
        };
      }
    }

    // Regex validation
    if (validation.rule === 'regex' && !isEmpty(value)) {
      const regex = new RegExp(validation.value, validation.flags);
      if (!regex.test(String(value))) {
        return {
          message: validation.errorMessage || 'Invalid format',
          level: validation.level || 'error',
          source: ErrorSource.Row,
          type: ErrorType.Regex,
        };
      }
    }

    // Unique validation
    if (validation.rule === 'unique') {
      // Skip if empty and allowEmpty is true
      if (isEmpty(value) && validation.allowEmpty) continue;

      // Check for duplicates in other rows
      const stringValue = String(value ?? '').toLowerCase().trim();
      const isDuplicate = allRows.some((row, index) => {
        if (index === currentRowIndex) return false;
        const otherValue = String(row[field.key] ?? '').toLowerCase().trim();
        return otherValue === stringValue && stringValue !== '';
      });

      if (isDuplicate) {
        return {
          message: validation.errorMessage || 'Must be unique',
          level: validation.level || 'error',
          source: ErrorSource.Table,
          type: ErrorType.Unique,
        };
      }
    }
  }

  return null;
};

/**
 * Hook providing validation actions
 *
 * PERFORMANCE: This hook has NO subscriptions to rows/fields/errors.
 * All state is read via getState() at call-time inside callbacks.
 * This prevents re-renders of components using this hook when cells change.
 */
export const useValidationActions = () => {
  // Store actions (these are stable from Zustand)
  const updateCell = useValidationStore((state) => state.updateCell);
  const updateRow = useValidationStore((state) => state.updateRow);
  const deleteRows = useValidationStore((state) => state.deleteRows);
  const addRow = useValidationStore((state) => state.addRow);
  const copyDown = useValidationStore((state) => state.copyDown);
  const setError = useValidationStore((state) => state.setError);
  const clearFieldError = useValidationStore((state) => state.clearFieldError);
  const setRowValidationStatus = useValidationStore((state) => state.setRowValidationStatus);
  const startValidatingCell = useValidationStore((state) => state.startValidatingCell);
  const stopValidatingCell = useValidationStore((state) => state.stopValidatingCell);
  const startEditingCell = useValidationStore((state) => state.startEditingCell);
  const stopEditingCell = useValidationStore((state) => state.stopEditingCell);

  /**
   * Validate a specific field for a row
   * Uses getState() to access current state at call-time
   */
  const validateField = useCallback(
    async (rowIndex: number, fieldKey: string) => {
      const { rows: currentRows, fields: currentFields } = useValidationStore.getState();

      const field = currentFields.find((f: Field<string>) => f.key === fieldKey);
      if (!field) return;

      const row = currentRows[rowIndex];
      if (!row) return;

      const value = row[fieldKey];
      const error = validateFieldValue(value, field, currentRows, rowIndex);

      if (error) {
        setError(rowIndex, fieldKey, error);
      } else {
        clearFieldError(rowIndex, fieldKey);
      }
    },
    [setError, clearFieldError] // Only stable store actions as deps
  );

  /**
   * Validate all fields for a row
   */
  const validateRow = useCallback(
    async (rowIndex: number, specificFields?: string[]) => {
      const { rows: currentRows, fields: currentFields } = useValidationStore.getState();

      const row = currentRows[rowIndex];
      if (!row) return;

      setRowValidationStatus(rowIndex, 'validating');

      const fieldsToValidate = specificFields
        ? currentFields.filter((f: Field<string>) => specificFields.includes(f.key))
        : currentFields;

      for (const field of fieldsToValidate) {
        const value = row[field.key];
        const error = validateFieldValue(value, field, currentRows, rowIndex);

        if (error) {
          setError(rowIndex, field.key, error);
        } else {
          clearFieldError(rowIndex, field.key);
        }
      }

      // Determine final status based on errors - re-read to get latest
      const latestErrors = useValidationStore.getState().errors;
      const rowErrors = latestErrors.get(rowIndex);
      const hasErrors = rowErrors && Object.keys(rowErrors).length > 0;
      setRowValidationStatus(rowIndex, hasErrors ? 'error' : 'validated');
    },
    [setError, clearFieldError, setRowValidationStatus]
  );

  /**
   * Validate all rows in a SINGLE BATCH operation.
   *
   * PERFORMANCE CRITICAL: This collects ALL errors first, then sets them in ONE
   * store update. This avoids the catastrophic performance of Immer cloning
   * Maps/Sets on every individual setError() call.
   *
   * With 100 rows × 30 fields, the old approach would trigger ~3000 individual
   * set() calls, each cloning the entire errors Map. This approach triggers ONE.
   */
  const validateAllRows = useCallback(async () => {
    const { rows: currentRows, fields: currentFields, errors: existingErrors, setBulkValidationResults } = useValidationStore.getState();

    // Collect ALL errors in plain JS Maps (no Immer overhead)
    const allErrors = new Map<number, Record<string, ValidationError[]>>();
    const allStatuses = new Map<number, 'pending' | 'validating' | 'validated' | 'error'>();

    // Process all rows - collect errors without touching the store
    for (let rowIndex = 0; rowIndex < currentRows.length; rowIndex++) {
      const row = currentRows[rowIndex];
      if (!row) continue;

      // IMPORTANT: Preserve existing UPC errors (from UPC validation phase)
      // These have source: ErrorSource.Upc and would otherwise be overwritten
      const existingRowErrors = existingErrors.get(rowIndex);
      const rowErrors: Record<string, ValidationError[]> = {};

      // Copy over any existing UPC-sourced errors
      if (existingRowErrors) {
        Object.entries(existingRowErrors).forEach(([fieldKey, errors]) => {
          const upcErrors = errors.filter(e => e.source === ErrorSource.Upc);
          if (upcErrors.length > 0) {
            rowErrors[fieldKey] = upcErrors;
          }
        });
      }

      // NOTE: We no longer round price fields on initial load.
      // Full precision is preserved internally (e.g., "3.625") for accurate calculations.
      // - Display: InputCell shows 2 decimals when not focused
      // - Calculations: 2x button uses full precision
      // - API submission: getCleanedData() formats to 2 decimals

      // Validate each field
      for (const field of currentFields) {
        const value = row[field.key];
        const error = validateFieldValue(value, field, currentRows, rowIndex);

        if (error) {
          // Merge with existing errors (e.g., UPC errors) rather than replacing
          const existingFieldErrors = rowErrors[field.key] || [];
          rowErrors[field.key] = [...existingFieldErrors, error];
        }
      }

      // Store results
      if (Object.keys(rowErrors).length > 0) {
        allErrors.set(rowIndex, rowErrors);
        allStatuses.set(rowIndex, 'error');
      } else {
        allStatuses.set(rowIndex, 'validated');
      }
    }

    // Also handle unique field validation in the same batch
    const uniqueFields = currentFields.filter((f: Field<string>) =>
      f.validations?.some((v: Validation) => v.rule === 'unique')
    );

    for (const field of uniqueFields) {
      const valueMap = new Map<string, number[]>();

      // Build map of values to row indices
      currentRows.forEach((row: RowData, index: number) => {
        const value = String(row[field.key] ?? '').toLowerCase().trim();
        if (value === '') return;

        const indices = valueMap.get(value) || [];
        indices.push(index);
        valueMap.set(value, indices);
      });

      // Mark duplicates
      valueMap.forEach((indices) => {
        if (indices.length > 1) {
          const validation = field.validations?.find((v: Validation) => v.rule === 'unique') as
            | { rule: 'unique'; errorMessage?: string; level?: 'error' | 'warning' | 'info' }
            | undefined;

          indices.forEach((rowIndex) => {
            // Get or create row errors
            let rowErrors = allErrors.get(rowIndex);
            if (!rowErrors) {
              rowErrors = {};
              allErrors.set(rowIndex, rowErrors);
            }

            rowErrors[field.key] = [{
              message: validation?.errorMessage || 'Must be unique',
              level: validation?.level || 'error',
              source: ErrorSource.Table,
              type: ErrorType.Unique,
            }];

            allStatuses.set(rowIndex, 'error');
          });
        }
      });
    }

    // SINGLE store update with all validation results
    setBulkValidationResults(allErrors, allStatuses);
  }, []); // No dependencies - reads everything from getState()

  /**
   * Validate unique fields across all rows (for table-level validation)
   * Used for incremental updates after individual cell changes
   */
  const validateUniqueFields = useCallback(() => {
    const { rows: currentRows, fields: currentFields, errors: currentErrors } = useValidationStore.getState();

    const uniqueFields = currentFields.filter((f: Field<string>) =>
      f.validations?.some((v: Validation) => v.rule === 'unique')
    );

    uniqueFields.forEach((field: Field<string>) => {
      const valueMap = new Map<string, number[]>();

      // Build map of values to row indices
      currentRows.forEach((row: RowData, index: number) => {
        const value = String(row[field.key] ?? '').toLowerCase().trim();
        if (value === '') return;

        const indices = valueMap.get(value) || [];
        indices.push(index);
        valueMap.set(value, indices);
      });

      // Mark duplicates
      valueMap.forEach((indices) => {
        if (indices.length > 1) {
          // Multiple rows have the same value - mark all as duplicates
          indices.forEach((rowIndex) => {
            const validation = field.validations?.find((v: Validation) => v.rule === 'unique') as
              | { rule: 'unique'; errorMessage?: string; level?: 'error' | 'warning' | 'info' }
              | undefined;
            setError(rowIndex, field.key, {
              message: validation?.errorMessage || 'Must be unique',
              level: validation?.level || 'error',
              source: ErrorSource.Table,
              type: ErrorType.Unique,
            });
          });
        } else {
          // Only one row has this value - clear any unique error
          const rowIndex = indices[0];
          const rowErrors = currentErrors.get(rowIndex);
          const fieldErrors = rowErrors?.[field.key];
          if (fieldErrors?.some((e: ValidationError) => e.type === ErrorType.Unique)) {
            // Only clear if it was a unique error
            clearFieldError(rowIndex, field.key);
          }
        }
      });
    });
  }, [setError, clearFieldError]);

  /**
   * Handle cell value change with debounced validation
   * IMPORTANT: This callback is stable - no subscriptions, only store actions
   */
  const handleCellChange = useCallback(
    (rowIndex: number, fieldKey: string, value: unknown) => {
      // Update the cell value immediately
      updateCell(rowIndex, fieldKey, value);

      // Cancel any pending validation for this cell
      const cacheKey = `${rowIndex}-${fieldKey}`;
      const existingTimeout = validationCache.get(cacheKey);
      if (existingTimeout) {
        clearTimeout(existingTimeout);
      }

      // Debounce validation
      const timeout = setTimeout(() => {
        validateField(rowIndex, fieldKey);
        validationCache.delete(cacheKey);
      }, 300);

      validationCache.set(cacheKey, timeout);
    },
    [updateCell, validateField]
  );

  /**
   * Handle cell edit start
   */
  const handleCellEditStart = useCallback(
    (rowIndex: number, fieldKey: string) => {
      startEditingCell(rowIndex, fieldKey);
    },
    [startEditingCell]
  );

  /**
   * Handle cell edit end with immediate validation
   * IMPORTANT: This callback is stable - no subscriptions, only store actions
   */
  const handleCellEditEnd = useCallback(
    (rowIndex: number, fieldKey: string, value: unknown) => {
      const { fields: currentFields } = useValidationStore.getState();

      stopEditingCell(rowIndex, fieldKey);

      // Update value and validate immediately
      updateCell(rowIndex, fieldKey, value);
      validateField(rowIndex, fieldKey);

      // Check unique validation for this field
      const field = currentFields.find((f: Field<string>) => f.key === fieldKey);
      if (field?.validations?.some((v: Validation) => v.rule === 'unique')) {
        validateUniqueFields();
      }
    },
    [stopEditingCell, updateCell, validateField, validateUniqueFields]
  );

  /**
   * Handle row deletion with confirmation
   */
  const handleDeleteRows = useCallback(
    (rowIndexes: number[]) => {
      deleteRows(rowIndexes);
      // Re-validate unique fields after deletion
      setTimeout(() => validateUniqueFields(), 0);
    },
    [deleteRows, validateUniqueFields]
  );

  /**
   * Handle adding a new row
   */
  const handleAddRow = useCallback(
    (rowData?: Partial<RowData>) => {
      addRow(rowData);
    },
    [addRow]
  );

  /**
   * Handle copy down operation
   */
  const handleCopyDown = useCallback(
    (fromRowIndex: number, fieldKey: string, toRowIndex?: number) => {
      const { rows: currentRows, fields: currentFields } = useValidationStore.getState();

      copyDown(fromRowIndex, fieldKey, toRowIndex);

      // Validate affected rows
      const endIndex = toRowIndex ?? currentRows.length - 1;
      for (let i = fromRowIndex + 1; i <= endIndex; i++) {
        validateField(i, fieldKey);
      }

      // Check unique validation if applicable
      const field = currentFields.find((f: Field<string>) => f.key === fieldKey);
      if (field?.validations?.some((v: Validation) => v.rule === 'unique')) {
        validateUniqueFields();
      }
    },
    [copyDown, validateField, validateUniqueFields]
  );

  return {
    // Basic operations
    updateCell,
    updateRow,
    handleCellChange,
    handleCellEditStart,
    handleCellEditEnd,

    // Row operations
    handleDeleteRows,
    handleAddRow,
    handleCopyDown,

    // Validation
    validateField,
    validateRow,
    validateAllRows,
    validateUniqueFields,

    // Cell state
    startValidatingCell,
    stopValidatingCell,
    startEditingCell,
    stopEditingCell,

    // Error management
    setError,
    clearFieldError,
  };
};
