import { useState, useCallback } from 'react';
import { toast } from 'sonner';
import { getApiUrl, RowData } from './validationTypes';
import { Fields } from '../../../types';
import { Meta } from '../types';
import { addErrorsAndRunHooks } from '../utils/dataMutations';
import * as Diff from 'diff';

// Define interfaces for AI validation
export interface ChangeDetail {
  field: string;
  original: any;
  corrected: any;
}

export interface ProductChangeDetail {
  productIndex: number;
  title: string;
  changes: ChangeDetail[];
}

export interface AiValidationDetails {
  changes: string[];
  warnings: string[];
  changeDetails: ProductChangeDetail[];
  isOpen: boolean;
  originalData?: RowData<string>[];
}

export interface AiValidationProgress {
  isOpen: boolean;
  status: string;
  step: number;
  estimatedSeconds?: number;
  startTime?: Date;
  promptLength?: number;
  elapsedSeconds?: number;
  progressPercent?: number;
}

export interface CurrentPrompt {
  isOpen: boolean;
  prompt: string | null;
  isLoading: boolean;
  debugData?: {
    taxonomyStats: {
      categories: number;
      themes: number;
      colors: number;
      taxCodes: number;
      sizeCategories: number;
      suppliers: number;
      companies: number;
      artists: number;
    } | null;
    basePrompt: string;
    sampleFullPrompt: string;
    promptLength: number;
    apiFormat?: Array<{
      role: string;
      content: string;
    }>;
    promptSources?: {
      systemPrompt?: { id: number; prompt_text: string };
      generalPrompt?: { id: number; prompt_text: string };
      companyPrompts?: Array<{ 
        id: number; 
        company: string; 
        companyName: string; 
        prompt_text: string 
      }>;
    };
    estimatedProcessingTime?: {
      seconds: number | null;
      sampleCount: number;
    };
  };
}

// Declare global interface for the timer
declare global {
  interface Window {
    aiValidationTimer?: NodeJS.Timeout;
  }
}

export const useAiValidation = <T extends string>(
  data: RowData<T>[],
  setData: React.Dispatch<React.SetStateAction<RowData<T>[]>>,
  fields: Fields<T>,
  rowHook?: (row: RowData<T>) => Promise<RowData<T>>,
  tableHook?: (data: RowData<T>[]) => Promise<RowData<T>[]>
) => {
  // State for AI validation
  const [isAiValidating, setIsAiValidating] = useState(false);
  const [aiValidationDetails, setAiValidationDetails] = useState<AiValidationDetails>({
    changes: [],
    warnings: [],
    changeDetails: [],
    isOpen: false,
  });

  const [aiValidationProgress, setAiValidationProgress] = useState<AiValidationProgress>({
    isOpen: false,
    status: "",
    step: 0,
  });

  const [currentPrompt, setCurrentPrompt] = useState<CurrentPrompt>({
    isOpen: false,
    prompt: null,
    isLoading: false,
  });

  // Track reverted changes
  const [revertedChanges, setRevertedChanges] = useState<Set<string>>(new Set());

  // Create an adapter for the rowHook to match the expected RowHook<T> type
  const adaptedRowHook = rowHook ? async (
    row: RowData<T>  ): Promise<Meta> => {
    // Call the original hook
    const result = await rowHook(row);
    // Extract Meta-specific properties
    const { __index } = result;
    // Return a Meta object with only the __index property
    return {
      __index: __index || row.__index || ''
    };
  } : undefined;

  // Create an adapter for the tableHook to match the expected TableHook<T> type
  const adaptedTableHook = tableHook ? async (rows: RowData<T>[]): Promise<Meta[]> => {
    // Call the original hook
    const results = await tableHook(rows);
    // Extract Meta-specific properties from each result
    return results.map((result, index) => ({
      __index: result.__index || rows[index].__index || ''
    }));
  } : undefined;

  // Get field display value
  const getFieldDisplayValue = useCallback((fieldKey: string, value: any): string => {
    const field = fields.find(f => f.key === fieldKey);
    if (!field) return String(value);

    // Handle different field types
    if (field.fieldType.type === 'select' || field.fieldType.type === 'multi-select') {
      const options = field.fieldType.options || [];
      
      // Handle array of values (multi-select)
      if (Array.isArray(value)) {
        return value.map(v => {
          const option = options.find(opt => String(opt.value) === String(v));
          return option ? option.label : v;
        }).join(', ');
      }
      
      // Handle single value
      const option = options.find(opt => String(opt.value) === String(value));
      return option ? option.label : String(value);
    }
    
    return String(value);
  }, [fields]);

  // Function to highlight differences between two text values
  const highlightDifferences = useCallback((original: string | null | undefined, corrected: string | null | undefined): { originalHtml: string, correctedHtml: string } => {
    // Handle null/undefined values
    let originalStr = original === null || original === undefined ? '' : String(original);
    let correctedStr = corrected === null || corrected === undefined ? '' : String(corrected);
    
    // If they're identical, return without highlighting
    if (originalStr === correctedStr) {
      return { 
        originalHtml: originalStr, 
        correctedHtml: correctedStr 
      };
    }
    
    const diff = Diff.diffWords(originalStr, correctedStr);
    
    let originalHtml = '';
    let correctedHtml = '';
    
    diff.forEach((part: Diff.Change) => {
      // Create escaped HTML
      const escapedValue = part.value
        .replace(/&/g, '&amp;')
        .replace(/</g, '&lt;')
        .replace(/>/g, '&gt;')
        .replace(/"/g, '&quot;')
        .replace(/'/g, '&#039;');
      
      if (part.added) {
        // Added parts only show in the corrected version (green)
        correctedHtml += `<span class="text-green-600 font-medium">${escapedValue}</span>`;
      } else if (part.removed) {
        // Removed parts only show in the original version (red)
        originalHtml += `<span class="text-red-600 font-medium">${escapedValue}</span>`;
      } else {
        // Unchanged parts show in both versions
        originalHtml += escapedValue;
        correctedHtml += escapedValue;
      }
    });
    
    return { 
      originalHtml, 
      correctedHtml 
    };
  }, []);

  // Function to get field display value with highlighted differences
  const getFieldDisplayValueWithHighlight = useCallback((fieldKey: string, originalValue: any, correctedValue: any): { originalHtml: string, correctedHtml: string } => {
    const originalDisplay = getFieldDisplayValue(fieldKey, originalValue);
    const correctedDisplay = getFieldDisplayValue(fieldKey, correctedValue);
    
    return highlightDifferences(originalDisplay, correctedDisplay);
  }, [getFieldDisplayValue, highlightDifferences]);

  // Function to check if a change has been reverted
  const isChangeReverted = useCallback((productIndex: number, fieldKey: string): boolean => {
    const revertKey = `${productIndex}:${fieldKey}`;
    return revertedChanges.has(revertKey);
  }, [revertedChanges]);

  // Function to revert a specific AI validation change
  const revertAiChange = useCallback((productIndex: number, fieldKey: string) => {
    // Ensure we have the original data
    if (!aiValidationDetails.originalData) {
      console.error('Cannot revert: original data not available');
      return;
    }

    // Find the original product and current product
    const originalProduct = aiValidationDetails.originalData[productIndex];
    if (!originalProduct) {
      console.error(`Cannot revert: original product at index ${productIndex} not found`);
      return;
    }
    
    const currentDataIndex = data.findIndex(d => d.__index === originalProduct.__index);
    if (currentDataIndex === -1) {
      console.error(`Cannot revert: current product with __index ${originalProduct.__index} not found`);
      return;
    }
    
    const currentProduct = data[currentDataIndex];
    
    // Get the original value in a type-safe way
    const originalValue = fieldKey in originalProduct ? 
      (originalProduct as Record<string, any>)[fieldKey] : undefined;
    
    console.log(`Reverting change to field "${fieldKey}" for product at index ${currentDataIndex}`, {
      originalIndex: productIndex,
      currentIndex: currentDataIndex,
      original: originalValue,
      current: fieldKey in currentProduct ? (currentProduct as Record<string, any>)[fieldKey] : undefined
    });

    // Create a new data array with the reverted field
    const newData = [...data];
    newData[currentDataIndex] = {
      ...newData[currentDataIndex],
      [fieldKey]: originalValue
    };
    
    // Update the data state
    setData(newData);
    
    // Add to the set of reverted changes
    const revertKey = `${productIndex}:${fieldKey}`;
    setRevertedChanges(prev => {
      const newSet = new Set(prev);
      newSet.add(revertKey);
      return newSet;
    });

    // Show success notification
    toast.success("Change reverted");
  }, [aiValidationDetails.originalData, data, setData]);

  // Function to show current prompt
  const showCurrentPrompt = useCallback(async () => {
    try {
      setCurrentPrompt(prev => ({ 
        ...prev, 
        isLoading: true, 
        isOpen: true 
      }));
      
      // Debug log the data being sent
      console.log('Sending products data:', {
        dataLength: data.length,
        firstProduct: data[0],
        lastProduct: data[data.length - 1]
      });

      // Clean the data to ensure we only send what's needed
      const cleanedData = data.map(item => {
        const { __index, ...rest } = item;
        return rest;
      });

      console.log('Cleaned data sample:', {
        length: cleanedData.length,
        firstProduct: cleanedData[0],
        lastProduct: cleanedData[cleanedData.length - 1]
      });
      
      // Use POST to send products in request body
      const response = await fetch(`${await getApiUrl()}/ai-validation/debug`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({ products: cleanedData })
      });

      if (!response.ok) {
        throw new Error(`Failed to get prompt: ${response.status} ${response.statusText}`);
      }

      const result = await response.json();
      console.log('Debug response:', result);
      
      // Check for different possible property names based on the API response
      const promptContent = result.sampleFullPrompt || result.basePrompt || result.prompt;
      
      if (promptContent) {
        setCurrentPrompt(prev => ({ 
          ...prev, 
          prompt: promptContent,
          isLoading: false,
          debugData: {
            taxonomyStats: result.taxonomyStats || null,
            basePrompt: result.basePrompt || '',
            sampleFullPrompt: result.sampleFullPrompt || '',
            promptLength: result.promptLength || (promptContent ? promptContent.length : 0),
            promptSources: result.promptSources,
            estimatedProcessingTime: result.estimatedProcessingTime,
            apiFormat: result.apiFormat
          }
        }));
      } else {
        throw new Error('No prompt returned from server');
      }
    } catch (error) {
      console.error('Error getting prompt:', error);
      toast.error("Failed to get current prompt");
      setCurrentPrompt(prev => ({ 
        ...prev, 
        isLoading: false,
        prompt: "Error loading prompt"
      }));
    }
  }, [data]);

  // Main AI validation function
  const handleAiValidation = useCallback(async () => {
    try {
      if (isAiValidating) return;
      
      // Store the original data before any changes
      const originalDataCopy = [...data];
      
      // Reset reverted changes when starting a new validation
      setRevertedChanges(new Set());
      
      setIsAiValidating(true);
      const startTime = new Date();
      setAiValidationProgress({
        isOpen: true,
        status: "Preparing data for validation...",
        step: 1,
        startTime,
        elapsedSeconds: 0,
        progressPercent: 0,
        ...(aiValidationProgress.estimatedSeconds ? {
          estimatedSeconds: aiValidationProgress.estimatedSeconds,
          promptLength: aiValidationProgress.promptLength
        } : {})
      });
      
      console.log('Sending data for validation:', data);
      
      // Set up progress update interval
      window.aiValidationTimer = setInterval(() => {
        setAiValidationProgress(prev => {
          if (!prev.startTime) return prev;
          
          const now = new Date();
          const elapsedSeconds = Math.floor((now.getTime() - prev.startTime.getTime()) / 1000);
          
          // Calculate progress percentage based on step and elapsed time
          let progressPercent = 0;
          
          if (prev.estimatedSeconds && prev.estimatedSeconds > 0) {
            // If we have an estimated time, use that for progress calculation
            progressPercent = Math.min(95, (elapsedSeconds / prev.estimatedSeconds) * 100);
            console.log('Using time-based progress:', progressPercent);
          } else {
            // Otherwise use step-based progress with some time-based adjustment
            const baseProgress = (prev.step / 5) * 100;
            const timeAdjustment = Math.min(20, elapsedSeconds * 0.5);
            const stepProgress = prev.step === 1 ? timeAdjustment : 0;
            progressPercent = Math.min(95, baseProgress + stepProgress);
            console.log('Using step-based progress:', progressPercent);
          }
          
          // Extract the base status message without any time information
          const baseStatus = prev.status.replace(/\s\(\d+[ms].+\)$/, '').replace(/\s\(\d+m \d+s.+\)$/, '');
          
          return {
            ...prev,
            elapsedSeconds,
            progressPercent,
            // Just use the base status message without time information
            status: baseStatus
          };
        });
      }, 1000) as unknown as NodeJS.Timeout;

      // Clean the data to ensure we only send what's needed
      const cleanedData = data.map(item => {
        const { __index, ...rest } = item;
        return rest;
      });

      console.log('Cleaned data for validation:', cleanedData);

      // If we don't have an estimated time yet, try to get one
      if (!aiValidationProgress.estimatedSeconds) {
        try {
          const debugResponse = await fetch(`${getApiUrl()}/ai-validation/debug`, {
            method: 'POST',
            headers: {
              'Content-Type': 'application/json',
            },
            body: JSON.stringify({ products: cleanedData })
          });
          
          if (debugResponse.ok) {
            const debugData = await debugResponse.json();
            console.log('Debug response details:', {
              hasEstimatedTime: !!debugData.estimatedProcessingTime,
              estimatedTimeSeconds: debugData.estimatedProcessingTime?.seconds,
              calculationMethod: debugData.estimatedProcessingTime?.calculationMethod || 'unknown',
              avgRate: debugData.estimatedProcessingTime?.avgRate,
              promptLength: debugData.promptLength,
              fullResponse: debugData
            });
            if (debugData.estimatedProcessingTime?.seconds) {
              console.log('Setting estimated time:', debugData.estimatedProcessingTime.seconds);
              setAiValidationProgress(prev => {
                const newState = {
                  ...prev,
                  estimatedSeconds: debugData.estimatedProcessingTime.seconds,
                  promptLength: debugData.promptLength
                };
                console.log('New progress state with time:', newState);
                return newState;
              });
            } else {
              console.log('No estimated time in debug response');
            }
          } else {
            console.error('Debug response not OK:', debugResponse.status);
          }
        } catch (estimateError) {
          console.error('Error getting time estimate:', estimateError);
          // Continue without an estimate
        }
      }

      setAiValidationProgress(prev => ({
        ...prev,
        status: "Sending data to AI service...",
        step: 2,
        estimatedSeconds: prev.estimatedSeconds,
        promptLength: prev.promptLength
      }));

      const response = await fetch(`${getApiUrl()}/ai-validation/validate`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({ products: cleanedData }),
      });

      if (!response.ok) {
        const errorText = await response.text();
        console.error('AI validation error response:', {
          status: response.status,
          statusText: response.statusText,
          body: errorText
        });
        throw new Error(`AI validation failed: ${response.status} ${response.statusText}`);
      }

      const result = await response.json();
      console.log('AI validation response:', result);
      
      if (!result.success) {
        throw new Error(result.error || 'AI validation failed');
      }

      // Store the prompt sources if they exist
      if (result.promptSources) {
        setCurrentPrompt(prev => {
          // Create debugData if it doesn't exist
          const prevDebugData = prev.debugData || {
            taxonomyStats: null,
            basePrompt: '',
            sampleFullPrompt: '',
            promptLength: 0
          };
          
          return { 
            ...prev, 
            debugData: {
              ...prevDebugData,
              promptSources: result.promptSources
            }
          };
        });
      }

      // Update progress with actual processing time if available
      if (result.performanceMetrics) {
        console.log('Performance metrics:', result.performanceMetrics);
        setAiValidationProgress(prev => ({
          ...prev,
          status: "Processing AI response...",
          step: 3,
          // Update with actual metrics from the server
          estimatedSeconds: result.performanceMetrics.estimatedSeconds || 
                           result.performanceMetrics.processingTimeSeconds || 
                           prev.estimatedSeconds,
          promptLength: result.performanceMetrics.promptLength || prev.promptLength,
          progressPercent: 75 // 75% complete when we're processing the AI response
        }));
      } else {
        setAiValidationProgress(prev => ({
          ...prev,
          status: "Processing AI response...",
          step: 3,
          progressPercent: 75
        }));
      }

      // Update the data with AI suggestions
      if (result.correctedData && Array.isArray(result.correctedData)) {
        // Process data to properly handle comma-separated values for multi-select fields
        const processedData = result.correctedData.map((corrected: any, index: number) => {
          // Start with original data to preserve __index and other metadata
          const original = data[index] || {};
          const processed = { ...original, ...corrected };
          
          // Process each field
          Object.keys(processed).forEach(key => {
            if (key.startsWith('__')) return; // Skip metadata fields
            
            const fieldConfig = fields.find(f => f.key === key);
            if (!fieldConfig) return; // Skip if no matching field found
            
            // Handle multi-select fields (comma-separated values)
            if (fieldConfig?.fieldType.type === 'multi-select' && typeof processed[key] === 'string') {
              // Split comma-separated values and trim each value
              processed[key] = processed[key].split(',').map((v: string) => v.trim()).filter(Boolean);
            }
            
            // Handle select fields (convert labels to values if needed)
            if ((fieldConfig?.fieldType.type === 'select' || fieldConfig?.fieldType.type === 'multi-select') && 
                'options' in fieldConfig.fieldType) {
              const options = fieldConfig.fieldType.options || [];
              
              // Handle single value (select)
              if (typeof processed[key] === 'string' && fieldConfig.fieldType.type === 'select') {
                // Check if the value is already an ID
                const isAlreadyId = options.some(opt => String(opt.value) === String(processed[key]));
                
                if (!isAlreadyId) {
                  // Try to find the option by label (case insensitive)
                  const matchingOption = options.find(opt => 
                    typeof processed[key] === 'string' && typeof opt.label === 'string' && 
                    opt.label.toLowerCase() === processed[key].toLowerCase()
                  );
                  if (matchingOption) {
                    // Convert label to ID
                    processed[key] = matchingOption.value;
                  }
                }
              }
              
              // Handle array of values (multi-select)
              if (Array.isArray(processed[key])) {
                processed[key] = processed[key].map((val: string | number) => {
                  if (val === null || val === undefined) return val;
                  
                  // Check if the value is already an ID
                  const isAlreadyId = options.some(opt => String(opt.value) === String(val));
                  
                  if (!isAlreadyId) {
                    // Try to find the option by label (case insensitive)
                    const matchingOption = options.find(opt => 
                      typeof val === 'string' && typeof opt.label === 'string' && 
                      opt.label.toLowerCase() === val.toLowerCase()
                    );
                    if (matchingOption) {
                      // Convert label to ID
                      return matchingOption.value;
                    }
                  }
                  return val;
                });
              }
            }
          });
          
          return processed;
        });
        
        console.log('About to update data with AI corrections:', {
          originalDataSample: data.slice(0, 2),
          processedDataSample: processedData.slice(0, 2),
          correctionCount: result.changes?.length || 0
        });
        
        // First validate the new data to ensure all validation rules are applied
        try {
          // Validate the data with the hooks
          const validatedData = await addErrorsAndRunHooks<T>(
            processedData,
            fields,
            adaptedRowHook,
            adaptedTableHook
          );
          
          // Update the component state with the validated data
          setData(validatedData as RowData<T>[]);
          
          // Force a small delay to ensure React updates the state before showing the results
          await new Promise(resolve => setTimeout(resolve, 50));
          
          console.log('Data updated after AI validation:', {
            dataLength: validatedData.length,
            hasErrors: false // We no longer check row.__errors
          });
          
          // Show changes and warnings in dialog after data is updated
          setAiValidationDetails({
            changes: result.changes || [],
            warnings: result.warnings || [],
            changeDetails: result.changeDetails || [],
            isOpen: true,
            originalData: originalDataCopy  // Use the stored original data
          });
        } catch (error) {
          console.error('Error validating AI corrections:', error);
          // Fall back to basic update without validation
          setData(processedData);
          
          // Still show the result dialog even if validation failed
          setAiValidationDetails({
            changes: result.changes || [],
            warnings: result.warnings || [],
            changeDetails: result.changeDetails || [],
            isOpen: true,
            originalData: originalDataCopy,  // Use the stored original data
          });
        }
      }

      setAiValidationProgress(prev => ({
        ...prev,
        status: "Validation complete!",
        step: 5,
        estimatedSeconds: prev.estimatedSeconds,
        promptLength: prev.promptLength
      }));

      setTimeout(() => {
        setAiValidationProgress(prev => ({ ...prev, isOpen: false }));
      }, 1000);
    } catch (error) {
      console.error('AI Validation Error:', error);
      toast.error(error instanceof Error ? error.message : "An error occurred during AI validation");
      setAiValidationProgress(prev => ({
        ...prev,
        status: "Validation failed",
        step: -1,
        estimatedSeconds: prev.estimatedSeconds,
        promptLength: prev.promptLength
      }));
    } finally {
      // Clear the interval when we're done (success or error)
      if (window.aiValidationTimer) {
        clearInterval(window.aiValidationTimer);
        window.aiValidationTimer = undefined;
      }
      setIsAiValidating(false);
      
      // Only set to 100% when actually complete (or in error state)
      setAiValidationProgress(prev => ({
        ...prev,
        progressPercent: prev.step === -1 ? prev.progressPercent : 100, // Only show 100% if successful completion
        estimatedSeconds: prev.estimatedSeconds,
        promptLength: prev.promptLength,
        elapsedSeconds: prev.elapsedSeconds
      }));
    }
  }, [isAiValidating, data, aiValidationProgress.estimatedSeconds, aiValidationProgress.promptLength, fields, adaptedRowHook, adaptedTableHook]);

  return {
    isAiValidating,
    aiValidationDetails,
    aiValidationProgress,
    currentPrompt,
    handleAiValidation,
    showCurrentPrompt,
    setAiValidationDetails,
    setAiValidationProgress,
    setCurrentPrompt,
    getFieldDisplayValue,
    getFieldDisplayValueWithHighlight,
    revertAiChange,
    isChangeReverted
  };
}; 