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

export const useUniqueItemNumbersValidation = <T extends string>(
  data: RowData<T>[],
  fields: Fields<T>,
  setValidationErrors: React.Dispatch<React.SetStateAction<Map<number, Record<string, ValidationError[]>>>>
) => {
  // Update validateUniqueItemNumbers to also check for uniqueness of UPC/barcode
  const validateUniqueItemNumbers = useCallback(async () => {
    // Skip if no data
    if (!data.length) return;

    // Track unique identifiers in maps
    const uniqueFieldsMap = new Map<string, Map<string, number[]>>();

    // Find fields that need uniqueness validation
    const uniqueFields = fields
      .filter((field) => field.validations?.some((v) => v.rule === "unique"))
      .map((field) => String(field.key));

    // Always check item_number uniqueness even if not explicitly defined
    if (!uniqueFields.includes("item_number")) {
      uniqueFields.push("item_number");
    }

    // Initialize maps for each unique field
    uniqueFields.forEach((fieldKey) => {
      uniqueFieldsMap.set(fieldKey, new Map<string, number[]>());
    });

    // Initialize batch updates
    const errors = new Map<number, Record<string, ValidationError[]>>();

    // ASYNC: Single pass through data to identify all unique values in batches
    const BATCH_SIZE = 20;
    for (let batchStart = 0; batchStart < data.length; batchStart += BATCH_SIZE) {
      const batchEnd = Math.min(batchStart + BATCH_SIZE, data.length);
      
      for (let index = batchStart; index < batchEnd; index++) {
        const row = data[index];
        uniqueFields.forEach((fieldKey) => {
          const value = row[fieldKey as keyof typeof row];

          // Skip empty values
          if (value === undefined || value === null || value === "") {
            return;
          }

          const valueStr = String(value);
          const fieldMap = uniqueFieldsMap.get(fieldKey);

          if (fieldMap) {
            // Get or initialize the array of indices for this value
            const indices = fieldMap.get(valueStr) || [];
            indices.push(index);
            fieldMap.set(valueStr, indices);
          }
        });
      }
      
      // Yield control back to UI thread after each batch
      if (batchEnd < data.length) {
        await new Promise(resolve => setTimeout(resolve, 0));
      }
    }

    // ASYNC: Process duplicates in batches to prevent UI blocking
    let processedFields = 0;
    for (const fieldKey of uniqueFields) {
      const fieldMap = uniqueFieldsMap.get(fieldKey);
      if (!fieldMap) continue;

      fieldMap.forEach((indices, value) => {
        // Only process if there are duplicates
        if (indices.length > 1) {
          // Get the validation rule for this field
          const field = fields.find((f) => String(f.key) === fieldKey);
          const validationRule = field?.validations?.find(
            (v) => v.rule === "unique"
          );

          const errorObj = {
            message:
              validationRule?.errorMessage || `Duplicate ${fieldKey}: ${value}`,
            level: validationRule?.level || ("error" as "error"),
            source: ErrorSources.Table,
            type: ErrorType.Unique,
          };

          // Add error to each row with this value
          indices.forEach((rowIndex) => {
            const rowErrors = errors.get(rowIndex) || {};
            rowErrors[fieldKey] = [errorObj];
            errors.set(rowIndex, rowErrors);
          });
        }
      });
      
      processedFields++;
      // Yield control after every few fields to prevent UI blocking
      if (processedFields % 2 === 0) {
        await new Promise(resolve => setTimeout(resolve, 0));
      }
    }

    // Merge uniqueness errors with existing validation errors
    setValidationErrors((prev) => {
      const newMap = new Map(prev);

      // Add uniqueness errors
      errors.forEach((rowErrors, rowIndex) => {
        const existingErrors = newMap.get(rowIndex) || {};
        const updatedErrors = { ...existingErrors };

        // Add uniqueness errors to existing errors
        Object.entries(rowErrors).forEach(([fieldKey, fieldErrors]) => {
          updatedErrors[fieldKey] = fieldErrors;
        });

        newMap.set(rowIndex, updatedErrors);
      });

      // Clean up rows that have no uniqueness errors anymore
      // by removing only uniqueness error types from rows not in the errors map
      newMap.forEach((rowErrors, rowIndex) => {
        if (!errors.has(rowIndex)) {
          // Remove uniqueness errors from this row
          const cleanedErrors: Record<string, ValidationError[]> = {};
          Object.entries(rowErrors).forEach(([fieldKey, fieldErrors]) => {
            // Keep non-uniqueness errors
            const nonUniqueErrors = fieldErrors.filter(error => error.type !== ErrorType.Unique);
            if (nonUniqueErrors.length > 0) {
              cleanedErrors[fieldKey] = nonUniqueErrors;
            }
          });
          
          // Update the row or remove it if no errors remain
          if (Object.keys(cleanedErrors).length > 0) {
            newMap.set(rowIndex, cleanedErrors);
          } else {
            newMap.delete(rowIndex);
          }
        }
      });

      return newMap;
    });

    console.log("Uniqueness validation complete");
  }, [data, fields, setValidationErrors]);

  return {
    validateUniqueItemNumbers
  };
}; 