/**
 * AI Suggestions Context
 *
 * Provides embedding-based suggestions to cells without causing re-renders.
 * Uses refs to store suggestion data and callbacks, so consumers can read
 * values on-demand without subscribing to state changes.
 *
 * PERFORMANCE: This context deliberately uses refs instead of state to avoid
 * cascading re-renders through the virtualized table. Cells read suggestions
 * when they need them (e.g., when dropdown opens).
 */

import React, { createContext, useContext, useRef, useCallback, useEffect, useState } from 'react';
import { apiFetch } from '@/utils/api';
import type { RowData, ProductSuggestions, TaxonomySuggestion } from '../store/types';

// ============================================================================
// Types
// ============================================================================

interface AiSuggestionsContextValue {
  /** Check if service is initialized */
  isInitialized: boolean;
  /** Get suggestions for a product by index */
  getSuggestions: (productIndex: string) => ProductSuggestions | undefined;
  /** Check if suggestions are loading for a product */
  isLoading: (productIndex: string) => boolean;
  /** Trigger suggestion fetch for a product */
  fetchSuggestions: (product: RowData) => void;
  /** Handle field blur - refreshes suggestions if relevant field changed */
  handleFieldBlur: (product: RowData, fieldKey: string) => void;
  /** Get category suggestions for a product */
  getCategorySuggestions: (productIndex: string) => TaxonomySuggestion[];
  /** Get theme suggestions for a product */
  getThemeSuggestions: (productIndex: string) => TaxonomySuggestion[];
  /** Get color suggestions for a product */
  getColorSuggestions: (productIndex: string) => TaxonomySuggestion[];
  /** Subscribe to suggestion changes for a product (returns unsubscribe fn) */
  subscribe: (productIndex: string, callback: () => void) => () => void;
  /** Force refresh suggestions for all products */
  refreshAll: () => void;
}

interface AiSuggestionsProviderProps {
  children: React.ReactNode;
  /** Get company name by ID */
  getCompanyName?: (id: string) => string | undefined;
  /** Get line name by ID */
  getLineName?: (id: string) => string | undefined;
  /** Initial products to fetch suggestions for */
  initialProducts?: RowData[];
  /** Whether to auto-initialize (default: true) */
  autoInitialize?: boolean;
}

// ============================================================================
// Context
// ============================================================================

const AiSuggestionsContext = createContext<AiSuggestionsContextValue | null>(null);

// Fields that affect embeddings
const EMBEDDING_FIELDS = ['company', 'line', 'name', 'description'];

const API_BASE = '/api/ai';

// ============================================================================
// Provider
// ============================================================================

export function AiSuggestionsProvider({
  children,
  getCompanyName,
  getLineName,
  initialProducts,
  autoInitialize = true,
}: AiSuggestionsProviderProps) {
  // State for initialization status (this can cause re-render, but it's rare)
  const [isInitialized, setIsInitialized] = useState(false);

  // Refs for data that shouldn't trigger re-renders
  const suggestionsRef = useRef<Map<string, ProductSuggestions>>(new Map());
  const loadingRef = useRef<Set<string>>(new Set());
  const fieldValuesRef = useRef<Map<string, Record<string, unknown>>>(new Map());
  const subscribersRef = useRef<Map<string, Set<() => void>>>(new Map());

  // Ref for lookup functions (updated on each render)
  const lookupFnsRef = useRef({ getCompanyName, getLineName });
  lookupFnsRef.current = { getCompanyName, getLineName };

  /**
   * Notify subscribers when suggestions change
   */
  const notifySubscribers = useCallback((productIndex: string) => {
    const callbacks = subscribersRef.current.get(productIndex);
    if (callbacks) {
      callbacks.forEach(cb => cb());
    }
  }, []);

  /**
   * Initialize the AI service
   */
  const initialize = useCallback(async (): Promise<boolean> => {
    if (isInitialized) return true;

    try {
      const response = await apiFetch(`${API_BASE}/initialize`, { method: 'POST' });
      const data = await response.json();

      if (!response.ok || !data.success) {
        console.error('[AiSuggestions] Initialization failed:', data.error);
        return false;
      }

      setIsInitialized(true);
      return true;
    } catch (error) {
      console.error('[AiSuggestions] Initialization error:', error);
      return false;
    }
  }, [isInitialized]);

  /**
   * Build product data for API request
   */
  const buildProductRequest = useCallback((product: RowData) => {
    const { getCompanyName: getCompany, getLineName: getLine } = lookupFnsRef.current;
    return {
      name: product.name,
      description: product.description,
      company_name: product.company ? getCompany?.(String(product.company)) : undefined,
      line_name: product.line ? getLine?.(String(product.line)) : undefined,
    };
  }, []);

  /**
   * Fetch suggestions for a single product
   */
  const fetchSuggestions = useCallback(async (product: RowData) => {
    const productIndex = product.__index;
    if (!productIndex) return;

    // Skip if already loading
    if (loadingRef.current.has(productIndex)) return;

    // Ensure initialized
    const ready = await initialize();
    if (!ready) return;

    // Check if product has enough data for meaningful suggestions
    const productData = buildProductRequest(product);
    const hasText = productData.name || productData.description || productData.company_name;
    if (!hasText) return;

    // Mark as loading
    loadingRef.current.add(productIndex);
    notifySubscribers(productIndex);

    try {
      const response = await apiFetch(`${API_BASE}/suggestions`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ product: productData }),
      });

      if (!response.ok) {
        throw new Error(`API error: ${response.status}`);
      }

      const suggestions: ProductSuggestions = await response.json();

      // Store suggestions
      suggestionsRef.current.set(productIndex, suggestions);

      // Store field values for change detection
      fieldValuesRef.current.set(productIndex, {
        company: product.company,
        line: product.line,
        name: product.name,
        description: product.description,
      });
    } catch (error) {
      console.error('[AiSuggestions] Fetch error:', error);
    } finally {
      loadingRef.current.delete(productIndex);
      notifySubscribers(productIndex);
    }
  }, [initialize, buildProductRequest, notifySubscribers]);

  /**
   * Fetch suggestions for multiple products in batch
   */
  const fetchBatchSuggestions = useCallback(async (products: RowData[]) => {
    // Ensure initialized
    const ready = await initialize();
    if (!ready) return;

    // Filter to products that need fetching
    const productsToFetch = products.filter(p => {
      if (!p.__index) return false;
      if (loadingRef.current.has(p.__index)) return false;
      if (suggestionsRef.current.has(p.__index)) return false;

      const productData = buildProductRequest(p);
      return productData.name || productData.description || productData.company_name;
    });

    if (productsToFetch.length === 0) return;

    // Mark all as loading
    productsToFetch.forEach(p => {
      loadingRef.current.add(p.__index);
      notifySubscribers(p.__index);
    });

    try {
      const response = await apiFetch(`${API_BASE}/suggestions/batch`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          products: productsToFetch.map(p => ({
            _index: p.__index,
            ...buildProductRequest(p),
          })),
        }),
      });

      if (!response.ok) {
        throw new Error(`API error: ${response.status}`);
      }

      const data = await response.json();

      // Store results
      for (const result of data.results || []) {
        const product = productsToFetch[result.index];
        if (product?.__index) {
          suggestionsRef.current.set(product.__index, {
            categories: result.categories,
            themes: result.themes,
            colors: result.colors,
          });

          fieldValuesRef.current.set(product.__index, {
            company: product.company,
            line: product.line,
            name: product.name,
            description: product.description,
          });
        }
      }
    } catch (error) {
      console.error('[AiSuggestions] Batch fetch error:', error);
    } finally {
      productsToFetch.forEach(p => {
        loadingRef.current.delete(p.__index);
        notifySubscribers(p.__index);
      });
    }
  }, [initialize, buildProductRequest, notifySubscribers]);

  /**
   * Handle field blur - refresh suggestions if embedding field changed
   */
  const handleFieldBlur = useCallback((product: RowData, fieldKey: string) => {
    if (!EMBEDDING_FIELDS.includes(fieldKey)) return;

    const productIndex = product.__index;
    if (!productIndex) return;

    // Check if value actually changed
    const prevValues = fieldValuesRef.current.get(productIndex);
    if (prevValues) {
      const prevValue = prevValues[fieldKey];
      const currentValue = product[fieldKey];
      if (prevValue === currentValue) return;
    }

    // Clear existing suggestions and refetch
    suggestionsRef.current.delete(productIndex);
    fieldValuesRef.current.delete(productIndex);

    // Debounce the fetch (simple timeout-based debounce)
    setTimeout(() => fetchSuggestions(product), 300);
  }, [fetchSuggestions]);

  /**
   * Get suggestions for a product
   */
  const getSuggestions = useCallback((productIndex: string): ProductSuggestions | undefined => {
    return suggestionsRef.current.get(productIndex);
  }, []);

  /**
   * Check if loading
   */
  const isLoading = useCallback((productIndex: string): boolean => {
    return loadingRef.current.has(productIndex);
  }, []);

  /**
   * Get category suggestions
   */
  const getCategorySuggestions = useCallback((productIndex: string): TaxonomySuggestion[] => {
    return suggestionsRef.current.get(productIndex)?.categories || [];
  }, []);

  /**
   * Get theme suggestions
   */
  const getThemeSuggestions = useCallback((productIndex: string): TaxonomySuggestion[] => {
    return suggestionsRef.current.get(productIndex)?.themes || [];
  }, []);

  /**
   * Get color suggestions
   */
  const getColorSuggestions = useCallback((productIndex: string): TaxonomySuggestion[] => {
    return suggestionsRef.current.get(productIndex)?.colors || [];
  }, []);

  /**
   * Subscribe to suggestion changes
   */
  const subscribe = useCallback((productIndex: string, callback: () => void): (() => void) => {
    if (!subscribersRef.current.has(productIndex)) {
      subscribersRef.current.set(productIndex, new Set());
    }
    subscribersRef.current.get(productIndex)!.add(callback);

    return () => {
      subscribersRef.current.get(productIndex)?.delete(callback);
    };
  }, []);

  /**
   * Refresh all products
   */
  const refreshAll = useCallback(() => {
    // Clear all cached suggestions
    suggestionsRef.current.clear();
    fieldValuesRef.current.clear();

    // If we have initial products, refetch them
    if (initialProducts && initialProducts.length > 0) {
      fetchBatchSuggestions(initialProducts);
    }
  }, [initialProducts, fetchBatchSuggestions]);

  // Auto-initialize and fetch initial products
  useEffect(() => {
    if (!autoInitialize) return;

    const init = async () => {
      const ready = await initialize();
      if (ready && initialProducts && initialProducts.length > 0) {
        // Small delay to avoid blocking initial render
        setTimeout(() => fetchBatchSuggestions(initialProducts), 100);
      }
    };

    init();
  }, [autoInitialize, initialize, initialProducts, fetchBatchSuggestions]);

  const contextValue: AiSuggestionsContextValue = {
    isInitialized,
    getSuggestions,
    isLoading,
    fetchSuggestions,
    handleFieldBlur,
    getCategorySuggestions,
    getThemeSuggestions,
    getColorSuggestions,
    subscribe,
    refreshAll,
  };

  return (
    <AiSuggestionsContext.Provider value={contextValue}>
      {children}
    </AiSuggestionsContext.Provider>
  );
}

// ============================================================================
// Hook
// ============================================================================

export function useAiSuggestionsContext(): AiSuggestionsContextValue | null {
  return useContext(AiSuggestionsContext);
}

/**
 * Hook for cells to get suggestions with re-render on update
 * Only use this in cell components that need to display suggestions
 */
export function useCellSuggestions(productIndex: string) {
  const context = useAiSuggestionsContext();
  const [, forceUpdate] = useState({});

  useEffect(() => {
    if (!context) return;

    // Subscribe to changes for this product
    const unsubscribe = context.subscribe(productIndex, () => {
      forceUpdate({});
    });

    return unsubscribe;
  }, [context, productIndex]);

  if (!context) {
    return {
      categories: [] as TaxonomySuggestion[],
      themes: [] as TaxonomySuggestion[],
      colors: [] as TaxonomySuggestion[],
      isLoading: false,
    };
  }

  return {
    categories: context.getCategorySuggestions(productIndex),
    themes: context.getThemeSuggestions(productIndex),
    colors: context.getColorSuggestions(productIndex),
    isLoading: context.isLoading(productIndex),
  };
}

export default AiSuggestionsContext;
