import { useState, useEffect, useCallback, useMemo, useRef } from "react";
import { useRsi } from "../../../hooks/useRsi";
import { ErrorType } from "../../../types";
import { RowSelectionState } from "@tanstack/react-table";
import { toast } from "sonner";
import { useQuery } from "@tanstack/react-query";
import config from "@/config";
import { useValidation } from "./useValidation";
import { useRowOperations } from "./useRowOperations";
import { useTemplateManagement } from "./useTemplateManagement";
import { useFilterManagement } from "./useFilterManagement";
import { useUniqueItemNumbersValidation } from "./useUniqueItemNumbersValidation";
import { useUpcValidation } from "./useUpcValidation";
import { Props, RowData } from "./validationTypes";

export const useValidationState = <T extends string>({
  initialData,
  onBack,
  onNext,
}: Props<T>) => {
  const { fields, rowHook, tableHook } = useRsi<T>();

  // Import validateField from useValidation
  const { validateField: validateFieldFromHook } = useValidation<T>(
    fields,
    rowHook
  );

  // Add ref to track template application state
  const isApplyingTemplateRef = useRef(false);

  // Core data state
  const [data, setData] = useState<RowData<T>[]>(() => {
    // Clean price fields in initial data before setting state
    return initialData.map((row) => {
      const updatedRow = { ...row } as Record<string, any>;

      // Clean MSRP
      if (typeof updatedRow.msrp === "string") {
        updatedRow.msrp = updatedRow.msrp.replace(/[$,]/g, "");
        const numValue = parseFloat(updatedRow.msrp);
        if (!isNaN(numValue)) {
          updatedRow.msrp = numValue.toFixed(2);
        }
      }

      // Clean cost_each
      if (typeof updatedRow.cost_each === "string") {
        updatedRow.cost_each = updatedRow.cost_each.replace(/[$,]/g, "");
        const numValue = parseFloat(updatedRow.cost_each);
        if (!isNaN(numValue)) {
          updatedRow.cost_each = numValue.toFixed(2);
        }
      }

      // Set default tax category if not already set
      if (
        updatedRow.tax_cat === undefined ||
        updatedRow.tax_cat === null ||
        updatedRow.tax_cat === ""
      ) {
        updatedRow.tax_cat = "0";
      }

      // Set default shipping restrictions if not already set
      if (
        updatedRow.ship_restrictions === undefined ||
        updatedRow.ship_restrictions === null ||
        updatedRow.ship_restrictions === ""
      ) {
        updatedRow.ship_restrictions = "0";
      }

      return updatedRow as RowData<T>;
    });
  });

  // Row selection state
  const [rowSelection, setRowSelection] = useState<RowSelectionState>({});

  // Validation state
  const [isValidating] = useState(false);
  const [validationErrors, setValidationErrors] = useState<
    Map<number, Record<string, any[]>>
  >(new Map());
  const [rowValidationStatus, setRowValidationStatus] = useState<
    Map<number, "pending" | "validating" | "validated" | "error">
  >(new Map());

  // Add state for tracking cells in loading state
  const [validatingCells, setValidatingCells] = useState<Set<string>>(new Set());

  const initialValidationDoneRef = useRef(false);
  const isValidatingRef = useRef(false);

  // Use row operations hook
  const { validateRow, updateRow, revalidateRows, copyDown } = useRowOperations<T>(
    data,
    fields,
    setData,
    setValidationErrors,
    validateFieldFromHook
  );

  // Use UPC validation hook - MUST be initialized before template management
  const upcValidation = useUpcValidation(data, setData);

  // Use unique item numbers validation hook
  const { validateUniqueItemNumbers } = useUniqueItemNumbersValidation<T>(
    data, 
    fields, 
    setValidationErrors
  );

  // Use template management hook
  const templateManagement = useTemplateManagement<T>(
    data,
    setData,
    rowSelection,
    setValidationErrors,
    setRowValidationStatus,
    validateRow,
    isApplyingTemplateRef,
    upcValidation,
    setValidatingCells
  );

  // Use filter management hook
  const filterManagement = useFilterManagement<T>(data, fields, validationErrors);

  // Run validation when data changes - FIXED to prevent recursive validation
  useEffect(() => {
    // Skip initial load - we have a separate initialization process
    if (!initialValidationDoneRef.current) return;

    // Don't run validation during template application
    if (isApplyingTemplateRef.current) return;

    // CRITICAL FIX: Skip validation if we're already validating to prevent infinite loops
    if (isValidatingRef.current) return;

    console.log("Running validation on data change");
    isValidatingRef.current = true;

    // For faster validation, run synchronously instead of in an async function
    const validateFields = () => {
      try {
        // Run regex validations on all rows
        const regexFields = fields.filter((field) =>
          field.validations?.some((v) => v.rule === "regex")
        );
        if (regexFields.length > 0) {
          // Create a map to collect validation errors
          const regexErrors = new Map<
            number,
            Record<string, any[]>
          >();

          // Check each row for regex errors
          data.forEach((row, rowIndex) => {
            const rowErrors: Record<string, any[]> = {};
            let hasErrors = false;

            // Check each regex field
            regexFields.forEach((field) => {
              const key = String(field.key);
              const value = row[key as keyof typeof row];

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

              // Find regex validation
              const regexValidation = field.validations?.find(
                (v) => v.rule === "regex"
              );
              if (regexValidation) {
                try {
                  // Check if value matches regex
                  const regex = new RegExp(
                    regexValidation.value,
                    regexValidation.flags
                  );
                  if (!regex.test(String(value))) {
                    // Add regex validation error
                    rowErrors[key] = [
                      {
                        message: regexValidation.errorMessage,
                        level: regexValidation.level || "error",
                        source: "row",
                        type: "regex",
                      },
                    ];
                    hasErrors = true;
                  }
                } catch (error) {
                  console.error("Invalid regex in validation:", error);
                }
              }
            });

            // Add errors if any found
            if (hasErrors) {
              regexErrors.set(rowIndex, rowErrors);
            }
          });

          // Update validation errors
          if (regexErrors.size > 0) {
            setValidationErrors((prev) => {
              const newErrors = new Map(prev);
              // Merge in regex errors
              for (const [rowIndex, errors] of regexErrors.entries()) {
                const existingErrors = newErrors.get(rowIndex) || {};
                newErrors.set(rowIndex, { ...existingErrors, ...errors });
              }
              return newErrors;
            });
          }
        }

        // Run uniqueness validations immediately
        validateUniqueItemNumbers();
      } finally {
        // Always ensure the ref is reset, even if an error occurs
        setTimeout(() => {
          isValidatingRef.current = false;
        }, 100);
      }
    };

    // Run validation immediately
    validateFields();
  }, [data, fields, validateUniqueItemNumbers]);

  // Add field options query
  const { data: fieldOptionsData } = useQuery({
    queryKey: ["import-field-options"],
    queryFn: async () => {
      const response = await fetch(`${config.apiUrl}/import/field-options`);
      if (!response.ok) {
        throw new Error("Failed to fetch field options");
      }
      return response.json();
    },
    staleTime: 5 * 60 * 1000, // Consider data fresh for 5 minutes
    gcTime: 30 * 60 * 1000, // Keep in cache for 30 minutes
  });

  // Get display text for a template
  const getTemplateDisplayText = useCallback(
    (templateId: string | null) => {
      if (!templateId) return "Select a template";

      const template = templateManagement.templates.find((t) => t.id.toString() === templateId);
      if (!template) return "Unknown template";

      try {
        const companyId = template.company || "";
        const productType = template.product_type || "Unknown Type";

        // Find company name from field options
        const companyName =
          fieldOptionsData?.companies?.find(
            (c: { value: string; label: string }) => c.value === companyId
          )?.label || companyId;

        return `${companyName} - ${productType}`;
      } catch (error) {
        console.error(
          "Error formatting template display text:",
          error,
          template
        );
        return "Error displaying template";
      }
    },
    [templateManagement.templates, fieldOptionsData]
  );

  // Check if there are any errors
  const hasErrors = useMemo(() => {
    for (const [_, status] of rowValidationStatus.entries()) {
      if (status === "error") return true;
    }
    return false;
  }, [rowValidationStatus]);

  // Create a function to handle button clicks (continue or back)
  const handleButtonClick = useCallback(
    async (direction: "next" | "back") => {
      if (direction === "back" && onBack) {
        // If a specific action is defined for back, use it
        onBack();
        return;
      }

      if (direction === "next") {
        // When proceeding to the next screen, check for unvalidated rows first
        const hasErrors = [...validationErrors.entries()].some(
          ([_, errors]) => {
            return Object.values(errors).some((errorSet) =>
              errorSet.some((error) => error.type !== ErrorType.Required)
            );
          }
        );

        if (hasErrors) {
          // We have validation errors - ask the user to fix them first or continue anyway
          const shouldContinue = window.confirm(
            "There are validation errors in your data. Do you want to continue anyway?"
          );

          if (!shouldContinue) {
            // User chose to fix errors
            return;
          }
        }

        // Prepare the data for the next step
        try {
          // No toast here - unnecessary and distracting

          // Call onNext with the cleaned data
          if (onNext) {
            // Remove metadata fields before passing to onNext
            const cleanedData = data.map((row) => {
              const {
                __index,
                __template,
                __original,
                __corrected,
                __changes,
                ...cleanRow
              } = row;
              return cleanRow as any;
            });

            onNext(cleanedData);
          }
        } catch (error) {
          console.error("Error proceeding to next step:", error);
          toast.error("Error saving data");
        }
      }
    },
    [data, onBack, onNext, validationErrors]
  );

  // Initialize validation on mount
  useEffect(() => {
    if (initialValidationDoneRef.current) return;

    console.log("Running initial validation");

    const runCompleteValidation = async () => {
      if (!data || data.length === 0) return;

      console.log("Running complete validation...");

      // Get required fields
      const requiredFields = fields.filter((field) =>
        field.validations?.some((v) => v.rule === "required")
      );
      console.log(`Found ${requiredFields.length} required fields`);

      // Get fields that have regex validation
      const regexFields = fields.filter((field) =>
        field.validations?.some((v) => v.rule === "regex")
      );
      console.log(`Found ${regexFields.length} fields with regex validation`);

      // Get fields that need uniqueness validation
      const uniqueFields = fields.filter((field) =>
        field.validations?.some((v) => v.rule === "unique")
      );
      console.log(
        `Found ${uniqueFields.length} fields requiring uniqueness validation`
      );

      // Limit batch size to avoid UI freezing
      const BATCH_SIZE = 100;
      const totalRows = data.length;

      // Initialize new data for any modifications
      const newData = [...data];

      // Create a temporary Map to collect all validation errors
      const validationErrorsTemp = new Map<
        number,
        Record<string, any[]>
      >();

      // Variables for batching
      let currentBatch = 0;
      const totalBatches = Math.ceil(totalRows / BATCH_SIZE);

      const processBatch = async () => {
        // Calculate batch range
        const startIdx = currentBatch * BATCH_SIZE;
        const endIdx = Math.min(startIdx + BATCH_SIZE, totalRows);
        console.log(
          `Processing batch ${
            currentBatch + 1
          }/${totalBatches} (rows ${startIdx} to ${endIdx - 1})`
        );

        // Process rows in this batch
        const batchPromises: Promise<void>[] = [];

        for (let rowIndex = startIdx; rowIndex < endIdx; rowIndex++) {
          batchPromises.push(
            new Promise<void>((resolve) => {
              const row = data[rowIndex];

              // Skip if row is empty or undefined
              if (!row) {
                resolve();
                return;
              }

              // Store field errors for this row
              const fieldErrors: Record<string, any[]> = {};
              let hasErrors = false;

              // Check if price fields need formatting
              const rowAsRecord = row as Record<string, any>;
              let mSrpNeedsProcessing = false;
              let costEachNeedsProcessing = false;

              if (
                rowAsRecord.msrp &&
                typeof rowAsRecord.msrp === "string" &&
                (rowAsRecord.msrp.includes("$") ||
                  rowAsRecord.msrp.includes(","))
              ) {
                mSrpNeedsProcessing = true;
              }

              if (
                rowAsRecord.cost_each &&
                typeof rowAsRecord.cost_each === "string" &&
                (rowAsRecord.cost_each.includes("$") ||
                  rowAsRecord.cost_each.includes(","))
              ) {
                costEachNeedsProcessing = true;
              }

              // Process price fields if needed
              if (mSrpNeedsProcessing || costEachNeedsProcessing) {
                // Create a clean copy only if needed
                const cleanedRow = { ...row } as Record<string, any>;

                if (mSrpNeedsProcessing) {
                  const msrpValue = rowAsRecord.msrp.replace(/[$,]/g, "");
                  const numValue = parseFloat(msrpValue);
                  cleanedRow.msrp = !isNaN(numValue)
                    ? numValue.toFixed(2)
                    : msrpValue;
                }

                if (costEachNeedsProcessing) {
                  const costValue = rowAsRecord.cost_each.replace(/[$,]/g, "");
                  const numValue = parseFloat(costValue);
                  cleanedRow.cost_each = !isNaN(numValue)
                    ? numValue.toFixed(2)
                    : costValue;
                }

                newData[rowIndex] = cleanedRow as RowData<T>;
              }

              // Validate required fields
              for (const field of requiredFields) {
                const key = String(field.key);
                const value = row[key as keyof typeof row];

                // Skip non-required empty fields
                if (
                  value === undefined ||
                  value === null ||
                  value === "" ||
                  (Array.isArray(value) && value.length === 0) ||
                  (typeof value === "object" &&
                    value !== null &&
                    Object.keys(value).length === 0)
                ) {
                  // Add error for empty required fields
                  fieldErrors[key] = [
                    {
                      message:
                        field.validations?.find((v) => v.rule === "required")
                          ?.errorMessage || "This field is required",
                      level: "error",
                      source: "row",
                      type: "required",
                    },
                  ];
                  hasErrors = true;
                }
              }

              // Validate regex fields - even if they have data
              for (const field of regexFields) {
                const key = String(field.key);
                const value = row[key as keyof typeof row];

                // Skip empty values as they're handled by required validation
                if (value === undefined || value === null || value === "") {
                  continue;
                }

                // Find regex validation
                const regexValidation = field.validations?.find(
                  (v) => v.rule === "regex"
                );
                if (regexValidation) {
                  try {
                    // Check if value matches regex
                    const regex = new RegExp(
                      regexValidation.value,
                      regexValidation.flags
                    );
                    if (!regex.test(String(value))) {
                      // Add regex validation error
                      fieldErrors[key] = [
                        {
                          message: regexValidation.errorMessage,
                          level: regexValidation.level || "error",
                          source: "row",
                          type: "regex",
                        },
                      ];
                      hasErrors = true;
                    }
                  } catch (error) {
                    console.error("Invalid regex in validation:", error);
                  }
                }
              }

              // Update validation errors for this row
              if (hasErrors) {
                validationErrorsTemp.set(rowIndex, fieldErrors);
              }

              resolve();
            })
          );
        }

        // Wait for all row validations to complete
        await Promise.all(batchPromises);
      };

      const processAllBatches = async () => {
        for (let batch = 0; batch < totalBatches; batch++) {
          currentBatch = batch;
          await processBatch();

          // Yield to UI thread periodically
          if (batch % 2 === 1) {
            await new Promise((resolve) => setTimeout(resolve, 0));
          }
        }

        // All batches complete
        console.log("All initial validation batches complete");

        // Apply collected validation errors all at once
        setValidationErrors(validationErrorsTemp);

        // Apply any data changes (like price formatting)
        if (JSON.stringify(data) !== JSON.stringify(newData)) {
          setData(newData);
        }

        // Run uniqueness validation after the basic validation
        validateUniqueItemNumbers();

        // Mark that initial validation is done
        initialValidationDoneRef.current = true;

        console.log("Initial validation complete");
      };

      // Start the validation process
      processAllBatches();
    };

    // Run the complete validation
    runCompleteValidation();
  }, [data, fields, setData, setValidationErrors, validateUniqueItemNumbers]);

  // Update fields with latest options
  const fieldsWithOptions = useMemo(() => {
    if (!fieldOptionsData) return fields;

    return fields.map((field) => {
      // Skip fields that aren't select or multi-select
      if (
        typeof field.fieldType !== "object" ||
        (field.fieldType.type !== "select" &&
          field.fieldType.type !== "multi-select")
      ) {
        return field;
      }

      // Get the correct options based on field key
      let options = [];
      switch (field.key) {
        case "company":
          options = [...(fieldOptionsData.companies || [])];
          break;
        case "supplier":
          options = [...(fieldOptionsData.suppliers || [])];
          break;
        case "categories":
          options = [...(fieldOptionsData.categories || [])];
          break;
        case "themes":
          options = [...(fieldOptionsData.themes || [])];
          break;
        case "colors":
          options = [...(fieldOptionsData.colors || [])];
          break;
        case "tax_cat":
          options = [...(fieldOptionsData.taxCategories || [])];
          // Ensure tax_cat is always a select, not multi-select
          return {
            ...field,
            fieldType: {
              type: "select",
              options,
            },
          };
        case "ship_restrictions":
          options = [...(fieldOptionsData.shippingRestrictions || [])];
          break;
        case "artist":
          options = [...(fieldOptionsData.artists || [])];
          break;
        case "size_cat":
          options = [...(fieldOptionsData.sizes || [])];
          break;
        default:
          options = [...(field.fieldType.options || [])];
      }

      return {
        ...field,
        fieldType: {
          ...field.fieldType,
          options,
        },
      };
    });
  }, [fields, fieldOptionsData]);

  // Load templates on mount
  useEffect(() => {
    templateManagement.loadTemplates();
  }, [templateManagement.loadTemplates]);

  return {
    // Data
    data,
    setData,
    filteredData: filterManagement.filteredData,

    // Validation
    isValidating,
    validationErrors,
    rowValidationStatus,
    validateRow,
    hasErrors,
    
    // CRITICAL: Export validatingCells to make it available to ValidationContainer
    validatingCells,
    setValidatingCells,

    // Row selection
    rowSelection,
    setRowSelection,

    // Row manipulation
    updateRow,
    copyDown,

    // Templates
    templates: templateManagement.templates,
    isLoadingTemplates: templateManagement.isLoadingTemplates,
    selectedTemplateId: templateManagement.templateState.selectedTemplateId,
    showSaveTemplateDialog: templateManagement.templateState.showSaveTemplateDialog,
    newTemplateName: templateManagement.templateState.newTemplateName,
    newTemplateType: templateManagement.templateState.newTemplateType,
    setTemplateState: templateManagement.setTemplateState,
    templateState: templateManagement.templateState,
    loadTemplates: templateManagement.loadTemplates,
    saveTemplate: templateManagement.saveTemplate,
    applyTemplate: templateManagement.applyTemplate,
    applyTemplateToSelected: templateManagement.applyTemplateToSelected,
    getTemplateDisplayText,
    refreshTemplates: templateManagement.refreshTemplates,

    // Filters
    filters: filterManagement.filters,
    filterFields: filterManagement.filterFields,
    filterValues: filterManagement.filterValues,
    updateFilters: filterManagement.updateFilters,
    resetFilters: filterManagement.resetFilters,

    // Fields reference
    fields: fieldsWithOptions, // Return updated fields with options

    // Hooks
    rowHook,
    tableHook,

    // Button handling
    handleButtonClick,
    revalidateRows,
  };
};