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

export const useRowOperations = <T extends string>(
  data: RowData<T>[],
  fields: Fields<T>,
  setData: React.Dispatch<React.SetStateAction<RowData<T>[]>>,
  setValidationErrors: React.Dispatch<React.SetStateAction<Map<number, Record<string, ValidationError[]>>>>,
  validateFieldFromHook: (value: any, field: Field<T>) => ValidationError[]
) => {
  // Helper function to validate a field value
  const fieldValidationHelper = useCallback(
    (rowIndex: number, specificField?: string) => {
      // Skip validation if row doesn't exist
      if (rowIndex < 0 || rowIndex >= data.length) return;

      // Get the row data
      const row = data[rowIndex];

      // If validating a specific field, only check that field
      if (specificField) {
        const field = fields.find((f) => String(f.key) === specificField);
        if (field) {
          const value = row[specificField as keyof typeof row];

          // Use state setter instead of direct mutation
          setValidationErrors((prev) => {
            const newErrors = new Map(prev);
            const existingErrors = { ...(newErrors.get(rowIndex) || {}) };

            // Quick check for required fields - this prevents flashing errors
            const isRequired = field.validations?.some(
              (v) => v.rule === "required"
            );
            const isEmpty =
              value === undefined ||
              value === null ||
              value === "" ||
              (Array.isArray(value) && value.length === 0) ||
              (typeof value === "object" &&
                value !== null &&
                Object.keys(value).length === 0);

            // For non-empty values, remove required errors immediately
            if (isRequired && !isEmpty && existingErrors[specificField]) {
              const nonRequiredErrors = existingErrors[specificField].filter(
                (e) => e.type !== ErrorType.Required
              );
              if (nonRequiredErrors.length === 0) {
                // If no other errors, remove the field entirely from errors
                delete existingErrors[specificField];
              } else {
                existingErrors[specificField] = nonRequiredErrors;
              }
            }

            // Run full validation for the field
            const errors = validateFieldFromHook(value, field as unknown as Field<T>);

            // Update validation errors for this field
            if (errors.length > 0) {
              existingErrors[specificField] = errors;
            } else {
              delete existingErrors[specificField];
            }

            // Update validation errors map
            if (Object.keys(existingErrors).length > 0) {
              newErrors.set(rowIndex, existingErrors);
            } else {
              newErrors.delete(rowIndex);
            }

            return newErrors;
          });
        }
      } else {
        // Validate all fields in the row
        setValidationErrors((prev) => {
          const newErrors = new Map(prev);
          const rowErrors: Record<string, ValidationError[]> = {};

          fields.forEach((field) => {
            const fieldKey = String(field.key);
            const value = row[fieldKey as keyof typeof row];
            const errors = validateFieldFromHook(value, field as unknown as Field<T>);

            if (errors.length > 0) {
              rowErrors[fieldKey] = errors;
            }
          });

          // Update validation errors map
          if (Object.keys(rowErrors).length > 0) {
            newErrors.set(rowIndex, rowErrors);
          } else {
            newErrors.delete(rowIndex);
          }

          return newErrors;
        });
      }
    },
    [data, fields, validateFieldFromHook, setValidationErrors]
  );

  // Use validateRow as an alias for fieldValidationHelper for compatibility
  const validateRow = fieldValidationHelper;

  // Modified updateRow function that properly handles field-specific validation
  const updateRow = useCallback(
    (rowIndex: number, key: T, value: any) => {
      // Process value before updating data
      let processedValue = value;

      // Strip dollar signs from price fields
      if (
        (key === "msrp" || key === "cost_each") &&
        typeof value === "string"
      ) {
        processedValue = value.replace(/[$,]/g, "");

        // Also ensure it's a valid number
        const numValue = parseFloat(processedValue);
        if (!isNaN(numValue)) {
          processedValue = numValue.toFixed(2);
        }
      }

      // Find the row data first
      const rowData = data[rowIndex];
      if (!rowData) {
        console.error(`No row data found for index ${rowIndex}`);
        return;
      }

      // Create a copy of the row to avoid mutation
      const updatedRow = { ...rowData, [key]: processedValue };

      // Update the data immediately - this sets the value
      setData((prevData) => {
        const newData = [...prevData];
        if (rowIndex >= 0 && rowIndex < newData.length) {
          newData[rowIndex] = updatedRow;
        }
        return newData;
      });

      // Find the field definition
      const field = fields.find((f) => String(f.key) === key);
      if (!field) return;

      // CRITICAL FIX: Combine both validation operations into a single state update
      // to prevent intermediate rendering that causes error icon flashing
      setValidationErrors((prev) => {
        const newMap = new Map(prev);
        const existingErrors = newMap.get(rowIndex) || {};
        const newRowErrors = { ...existingErrors };

        // Check for required field first
        const isRequired = field.validations?.some(
          (v) => v.rule === "required"
        );
        const isEmpty =
          processedValue === undefined ||
          processedValue === null ||
          processedValue === "" ||
          (Array.isArray(processedValue) && processedValue.length === 0) ||
          (typeof processedValue === "object" &&
            processedValue !== null &&
            Object.keys(processedValue).length === 0);

        // For required fields with values, remove required errors
        if (isRequired && !isEmpty && newRowErrors[key as string]) {
          const hasRequiredError = newRowErrors[key as string].some(
            (e) => e.type === ErrorType.Required
          );

          if (hasRequiredError) {
            // Remove required errors but keep other types of errors
            const nonRequiredErrors = newRowErrors[key as string].filter(
              (e) => e.type !== ErrorType.Required
            );

            if (nonRequiredErrors.length === 0) {
              // If no other errors, delete the field's errors entirely
              delete newRowErrors[key as string];
            } else {
              // Otherwise keep non-required errors
              newRowErrors[key as string] = nonRequiredErrors;
            }
          }
        }

        // Now run full validation for the field (except for required which we already handled)
        const errors = validateFieldFromHook(
          processedValue,
          field as unknown as Field<T>
        ).filter((e) => e.type !== ErrorType.Required || isEmpty);

        // Update with new validation results
        if (errors.length > 0) {
          newRowErrors[key as string] = errors;
        } else if (!newRowErrors[key as string]) {
          // If no errors found and no existing errors, ensure field is removed from errors
          delete newRowErrors[key as string];
        }

        // Update the map
        if (Object.keys(newRowErrors).length > 0) {
          newMap.set(rowIndex, newRowErrors);
        } else {
          newMap.delete(rowIndex);
        }

        return newMap;
      });

      // Handle simple secondary effects here
      setTimeout(() => {
        // Use __index to find the actual row in the full data array
        const rowId = rowData.__index;

        // Handle company change - clear line/subline
        if (key === "company" && processedValue) {
          // Clear any existing line/subline values
          setData((prevData) => {
            const newData = [...prevData];
            const idx = newData.findIndex((item) => item.__index === rowId);
            if (idx >= 0) {
              newData[idx] = {
                ...newData[idx],
                line: undefined,
                subline: undefined,
              };
            }
            return newData;
          });
        }

        // Handle line change - clear subline
        if (key === "line" && processedValue) {
          // Clear any existing subline value
          setData((prevData) => {
            const newData = [...prevData];
            const idx = newData.findIndex((item) => item.__index === rowId);
            if (idx >= 0) {
              newData[idx] = {
                ...newData[idx],
                subline: undefined,
              };
            }
            return newData;
          });
        }
      }, 50);
    },
    [data, fields, validateFieldFromHook, setData, setValidationErrors]
  );

  // Improved revalidateRows function
  const revalidateRows = useCallback(
    async (
      rowIndexes: number[],
      updatedFields?: { [rowIndex: number]: string[] }
    ) => {
      // Process all specified rows using a single state update to avoid race conditions
      setValidationErrors((prev) => {
        const newErrors = new Map(prev);

        // Process each row
        for (const rowIndex of rowIndexes) {
          if (rowIndex < 0 || rowIndex >= data.length) continue;

          const row = data[rowIndex];
          if (!row) continue;

          // If we have specific fields to update for this row
          const fieldsToValidate = updatedFields?.[rowIndex] || [];

          if (fieldsToValidate.length > 0) {
            // Get existing errors for this row
            const existingRowErrors = { ...(newErrors.get(rowIndex) || {}) };

            // Validate each specified field
            for (const fieldKey of fieldsToValidate) {
              const field = fields.find((f) => String(f.key) === fieldKey);
              if (!field) continue;

              const value = row[fieldKey as keyof typeof row];

              // Run validation for this field
              const errors = validateFieldFromHook(value, field as unknown as Field<T>);

              // Update errors for this field
              if (errors.length > 0) {
                existingRowErrors[fieldKey] = errors;
              } else {
                delete existingRowErrors[fieldKey];
              }
            }

            // Update the row's errors
            if (Object.keys(existingRowErrors).length > 0) {
              newErrors.set(rowIndex, existingRowErrors);
            } else {
              newErrors.delete(rowIndex);
            }
          } else {
            // No specific fields provided - validate the entire row
            const rowErrors: Record<string, ValidationError[]> = {};

            // Validate all fields in the row
            for (const field of fields) {
              const fieldKey = String(field.key);
              const value = row[fieldKey as keyof typeof row];

              // Run validation for this field
              const errors = validateFieldFromHook(value, field as unknown as Field<T>);

              // Update errors for this field
              if (errors.length > 0) {
                rowErrors[fieldKey] = errors;
              }
            }

            // Update the row's errors
            if (Object.keys(rowErrors).length > 0) {
              newErrors.set(rowIndex, rowErrors);
            } else {
              newErrors.delete(rowIndex);
            }
          }
        }

        return newErrors;
      });
    },
    [data, fields, validateFieldFromHook]
  );

  // Copy a cell value to all cells below it in the same column
  const copyDown = useCallback(
    (rowIndex: number, key: T) => {
      // Get the source value to copy
      const sourceValue = data[rowIndex][key];

      // Update all rows below with the same value using the existing updateRow function
      // This ensures all validation logic runs consistently
      for (let i = rowIndex + 1; i < data.length; i++) {
        // Just use updateRow which will handle validation with proper timing
        updateRow(i, key, sourceValue);
      }
    },
    [data, updateRow]
  );

  return {
    validateRow,
    updateRow,
    revalidateRows,
    copyDown
  };
}; 