import { useState, useCallback } from 'react';
import { toast } from 'sonner';
import { Template, RowData, TemplateState, getApiUrl } from './validationTypes';
import { RowSelectionState } from '@tanstack/react-table';
import { ValidationError } from '../../../types';

export const useTemplateManagement = <T extends string>(
  data: RowData<T>[],
  setData: React.Dispatch<React.SetStateAction<RowData<T>[]>>,
  rowSelection: RowSelectionState,
  setValidationErrors: React.Dispatch<React.SetStateAction<Map<number, Record<string, ValidationError[]>>>>,
  setRowValidationStatus: React.Dispatch<React.SetStateAction<Map<number, "pending" | "validating" | "validated" | "error">>>,
  validateRow: (rowIndex: number, specificField?: string) => void,
  isApplyingTemplateRef: React.MutableRefObject<boolean>,
  upcValidation: { 
    validateUpc: (rowIndex: number, supplierId: string, upcValue: string) => Promise<{success: boolean, itemNumber?: string}>,
    applyItemNumbersToData: (onApplied?: (updatedRowIds: number[]) => void) => void
  },
  setValidatingCells?: React.Dispatch<React.SetStateAction<Set<string>>>
) => {
  // Template state
  const [templates, setTemplates] = useState<Template[]>([]);
  const [isLoadingTemplates, setIsLoadingTemplates] = useState(true);
  const [templateState, setTemplateState] = useState<TemplateState>({
    selectedTemplateId: null,
    showSaveTemplateDialog: false,
    newTemplateName: "",
    newTemplateType: "",
  });

  // Load templates
  const loadTemplates = useCallback(async () => {
    try {
      setIsLoadingTemplates(true);
      console.log("Fetching templates from:", `${getApiUrl()}/templates`);
      const response = await fetch(`${getApiUrl()}/templates`);
      if (!response.ok) throw new Error("Failed to fetch templates");
      const templateData = await response.json();
      const validTemplates = templateData.filter(
        (t: any) =>
          t && typeof t === "object" && t.id && t.company && t.product_type
      );
      setTemplates(validTemplates);
    } catch (error) {
      console.error("Error fetching templates:", error);
      toast.error("Failed to load templates");
    } finally {
      setIsLoadingTemplates(false);
    }
  }, []);

  // Refresh templates
  const refreshTemplates = useCallback(() => {
    loadTemplates();
  }, [loadTemplates]);

  // Save a new template
  const saveTemplate = useCallback(
    async (name: string, type: string) => {
      try {
        // Get selected rows
        const selectedRowIndex = Number(Object.keys(rowSelection)[0]);
        const selectedRow = data[selectedRowIndex];

        if (!selectedRow) {
          toast.error("Please select a row to create a template");
          return;
        }

        // Extract data for template, removing metadata fields
        const {
          __index,
          __template,
          __original,
          __corrected,
          __changes,
          ...templateData
        } = selectedRow as any;

        // Clean numeric values (remove $ from price fields)
        const cleanedData: Record<string, any> = {};

        // Process each key-value pair
        Object.entries(templateData).forEach(([key, value]) => {
          // Handle numeric values with dollar signs
          if (typeof value === "string" && value.includes("$")) {
            cleanedData[key] = value.replace(/[$,\s]/g, "").trim();
          }
          // Handle array values (like categories or ship_restrictions)
          else if (Array.isArray(value)) {
            cleanedData[key] = value;
          }
          // Handle other values
          else {
            cleanedData[key] = value;
          }
        });

        // Send the template to the API
        const response = await fetch(`${getApiUrl()}/templates`, {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
          },
          body: JSON.stringify({
            ...cleanedData,
            company: name,
            product_type: type,
          }),
        });

        if (!response.ok) {
          const errorData = await response.json();
          throw new Error(
            errorData.error || errorData.details || "Failed to save template"
          );
        }

        // Get the new template from the response
        const newTemplate = await response.json();

        // Update the templates list with the new template
        setTemplates((prev) => [...prev, newTemplate]);

        // Update the row to show it's using this template
        setData((prev) => {
          const newData = [...prev];
          if (newData[selectedRowIndex]) {
            newData[selectedRowIndex] = {
              ...newData[selectedRowIndex],
              __template: newTemplate.id.toString(),
            };
          }
          return newData;
        });

        toast.success(`Template "${name}" saved successfully`);

        // Reset dialog state
        setTemplateState((prev) => ({
          ...prev,
          showSaveTemplateDialog: false,
          newTemplateName: "",
          newTemplateType: "",
        }));
      } catch (error) {
        console.error("Error saving template:", error);
        toast.error(
          error instanceof Error ? error.message : "Failed to save template"
        );
      }
    },
    [data, rowSelection, setData]
  );

  // Apply template to rows - optimized version
  const applyTemplate = useCallback(
    (templateId: string, rowIndexes: number[]) => {
      const template = templates.find((t) => t.id.toString() === templateId);

      if (!template) {
        toast.error("Template not found");
        return;
      }

      console.log(`Applying template ${templateId} to rows:`, rowIndexes);

      // Validate row indexes
      const validRowIndexes = rowIndexes.filter(
        (index) => index >= 0 && index < data.length && Number.isInteger(index)
      );

      if (validRowIndexes.length === 0) {
        toast.error("No valid rows to update");
        console.error("Invalid row indexes:", rowIndexes);
        return;
      }

      // Set the template application flag
      isApplyingTemplateRef.current = true;

      // Save scroll position
      const scrollPosition = {
        left: window.scrollX,
        top: window.scrollY,
      };

      // Create a copy of data and process all rows at once to minimize state updates
      const newData = [...data];
      const batchErrors = new Map<number, Record<string, ValidationError[]>>();
      const batchStatuses = new Map<
        number,
        "pending" | "validating" | "validated" | "error"
      >();

      // Extract template fields once outside the loop
      const templateFields = Object.entries(template).filter(
        ([key]) =>
          ![
            "id",
            "__meta",
            "__template",
            "__original",
            "__corrected",
            "__changes",
          ].includes(key)
      );

      // Apply template to each valid row
      validRowIndexes.forEach((index) => {
        // Create a new row with template values
        const originalRow = newData[index];
        const updatedRow = { ...originalRow } as Record<string, any>;

        // Apply template fields (excluding metadata fields)
        for (const [key, value] of templateFields) {
          updatedRow[key] = value;
        }

        // Mark the row as using this template
        updatedRow.__template = templateId;

        // Update the row in the data array
        newData[index] = updatedRow as RowData<T>;

        // Clear validation errors and mark as validated
        batchErrors.set(index, {});
        batchStatuses.set(index, "validated");
      });

      // Check which rows need UPC validation
      const upcValidationRows = validRowIndexes.filter((rowIndex) => {
        const row = newData[rowIndex];
        return row && row.upc && row.supplier;
      });

      // Perform a single update for all rows
      setData(newData);

      // Update all validation errors and statuses at once
      setValidationErrors((prev) => {
        const newErrors = new Map(prev);
        for (const [rowIndex, errors] of batchErrors.entries()) {
          newErrors.set(rowIndex, errors);
        }
        return newErrors;
      });

      setRowValidationStatus((prev) => {
        const newStatus = new Map(prev);
        for (const [rowIndex, status] of batchStatuses.entries()) {
          newStatus.set(rowIndex, status);
        }
        return newStatus;
      });

      // Restore scroll position
      requestAnimationFrame(() => {
        window.scrollTo(scrollPosition.left, scrollPosition.top);
      });

      // Show success toast
      if (validRowIndexes.length === 1) {
        toast.success("Template applied");
      } else if (validRowIndexes.length > 1) {
        toast.success(`Template applied to ${validRowIndexes.length} rows`);
      }

      // Reset template application flag to allow validation
      isApplyingTemplateRef.current = false;

      // If there are rows with both UPC and supplier, validate them
      if (upcValidationRows.length > 0) {
        console.log(`Validating UPCs for ${upcValidationRows.length} rows after template application`);
        
        // Process each row sequentially - this mimics the exact manual edit behavior
        const processNextValidation = (index = 0) => {
          if (index >= upcValidationRows.length) {
            return; // All rows processed
          }
          
          const rowIndex = upcValidationRows[index];
          const row = newData[rowIndex];
          
          if (row && row.supplier && row.upc) {
            // The EXACT implementation from handleUpdateRow when supplier is edited manually:
            
            // 1. Mark the item_number cell as being validated - THIS IS CRITICAL FOR LOADING STATE
            const cellKey = `${rowIndex}-item_number`;
            
            // Clear validation errors for this field
            setValidationErrors(prev => {
              const newErrors = new Map(prev);
              if (newErrors.has(rowIndex)) {
                const rowErrors = { ...newErrors.get(rowIndex) };
                if (rowErrors.item_number) {
                  delete rowErrors.item_number;
                }
                newErrors.set(rowIndex, rowErrors);
              }
              return newErrors;
            });
            
            // Set loading state - using setValidatingCells from props
            if (setValidatingCells) {
              setValidatingCells(prev => {
                const newSet = new Set(prev);
                newSet.add(cellKey);
                return newSet;
              });
            }
            
            // Validate UPC for this row
            upcValidation.validateUpc(rowIndex, row.supplier.toString(), row.upc.toString())
              .then(result => {
                if (result.success && result.itemNumber) {
                  // CRITICAL FIX: Directly update data with the item number to ensure immediate UI update
                  setData(prevData => {
                    const newData = [...prevData];
                    
                    // Update this specific row with the item number
                    if (rowIndex >= 0 && rowIndex < newData.length) {
                      newData[rowIndex] = {
                        ...newData[rowIndex],
                        item_number: result.itemNumber
                      };
                    }
                    
                    return newData;
                  });
                  
                  // Also trigger other relevant updates
                  upcValidation.applyItemNumbersToData();
                  
                  // Mark for revalidation after item numbers are updated
                  setTimeout(() => {
                    // Validate the row EXACTLY like in manual edit
                    validateRow(rowIndex, 'item_number');
                    
                    // CRITICAL FIX: Make one final check to ensure data is correct
                    setTimeout(() => {
                      // Get the current item number from the data
                      const currentItemNumber = (() => {
                        try {
                          const dataAtThisPointInTime = data[rowIndex];
                          return dataAtThisPointInTime?.item_number;
                        } catch (e) {
                          return undefined;
                        }
                      })();
                      
                      // If the data is wrong at this point, fix it directly
                      if (currentItemNumber !== result.itemNumber) {
                        // Directly update the data to fix the issue
                        setData(dataRightNow => {
                          const fixedData = [...dataRightNow];
                          if (rowIndex >= 0 && rowIndex < fixedData.length) {
                            fixedData[rowIndex] = {
                              ...fixedData[rowIndex],
                              item_number: result.itemNumber
                            };
                          }
                          return fixedData;
                        });
                        
                        // Then do a force update after a brief delay
                        setTimeout(() => {
                          setData(currentData => {
                            // Critical fix: ensure the item number is correct 
                            if (currentData[rowIndex] && currentData[rowIndex].item_number !== result.itemNumber) {
                              // Create a completely new array with the correct item number
                              const fixedData = [...currentData];
                              fixedData[rowIndex] = {
                                ...fixedData[rowIndex],
                                item_number: result.itemNumber
                              };
                              return fixedData;
                            }
                            
                            // Create a completely new array
                            return [...currentData];
                          });
                        }, 20);
                      } else {
                        // Item number is already correct, just do the force update
                        setData(currentData => {
                          // Create a completely new array
                          return [...currentData];
                        });
                      }
                    }, 50);
                    
                    // Clear loading state
                    if (setValidatingCells) {
                      setValidatingCells(prev => {
                        const newSet = new Set(prev);
                        newSet.delete(cellKey);
                        return newSet;
                      });
                    }
                    
                    // Continue to next row after validation is complete
                    setTimeout(() => processNextValidation(index + 1), 100);
                  }, 100);
                } else {
                  // Clear loading state on failure
                  if (setValidatingCells) {
                    setValidatingCells(prev => {
                      const newSet = new Set(prev);
                      newSet.delete(cellKey);
                      return newSet;
                    });
                  }
                  
                  // Continue to next row if validation fails
                  setTimeout(() => processNextValidation(index + 1), 100);
                }
              })
              .catch(err => {
                console.error(`Error validating UPC for row ${rowIndex}:`, err);
                
                // Clear loading state on error
                if (setValidatingCells) {
                  setValidatingCells(prev => {
                    const newSet = new Set(prev);
                    newSet.delete(cellKey);
                    return newSet;
                  });
                }
                
                // Continue to next row despite error
                setTimeout(() => processNextValidation(index + 1), 100);
              });
          } else {
            // Skip this row and continue to the next
            processNextValidation(index + 1);
          }
        };
        
        // Start processing validations
        processNextValidation();
      }
    },
    [
      data,
      templates,
      setData,
      setValidationErrors,
      setRowValidationStatus,
      validateRow,
      upcValidation,
      setValidatingCells
    ]
  );

  // Apply template to selected rows
  const applyTemplateToSelected = useCallback(
    (templateId: string) => {
      if (!templateId) return;

      // Update the selected template ID
      setTemplateState((prev) => ({
        ...prev,
        selectedTemplateId: templateId,
      }));

      // Get selected row keys (which may be UUIDs)
      const selectedKeys = Object.entries(rowSelection)
        .filter(([_, selected]) => selected === true)
        .map(([key, _]) => key);

      console.log("Selected row keys:", selectedKeys);

      if (selectedKeys.length === 0) {
        toast.error("No rows selected");
        return;
      }

      // Map UUID keys to array indices
      const selectedIndexes = selectedKeys
        .map((key) => {
          // Find the matching row index in the data array
          const index = data.findIndex(
            (row) =>
              (row.__index && row.__index === key) || // Match by __index
              String(data.indexOf(row)) === key // Or by numeric index
          );
          return index;
        })
        .filter((index) => index !== -1); // Filter out any not found

      console.log("Mapped row indices:", selectedIndexes);

      if (selectedIndexes.length === 0) {
        toast.error("Could not find selected rows");
        return;
      }

      // Apply template to selected rows
      applyTemplate(templateId, selectedIndexes);
    },
    [rowSelection, applyTemplate, setTemplateState, data]
  );

  return {
    templates,
    isLoadingTemplates,
    templateState,
    setTemplateState,
    loadTemplates,
    refreshTemplates,
    saveTemplate,
    applyTemplate,
    applyTemplateToSelected
  };
}; 