/**
 * Keywords Page - Refactored to use TablePageTemplate
 * This demonstrates how to use the config-driven template system
 * while maintaining all existing functionality.
 */

import { useState, useEffect, useRef, useMemo, useCallback } from 'react';
import TablePageTemplate from '../../templates/TablePageTemplate';
import {
  fetchKeywords,
  createKeyword,
  updateKeyword,
  deleteKeyword,
  bulkDeleteKeywords,
  bulkUpdateKeywordsStatus,
  Keyword,
  KeywordFilters,
  KeywordCreateData,
  fetchClusters,
  Cluster,
  API_BASE_URL,
  autoClusterKeywords,
  fetchSeedKeywords,
  SeedKeyword,
} from '../../services/api';
import { useSiteStore } from '../../store/siteStore';
import { useSectorStore } from '../../store/sectorStore';
import { usePageSizeStore } from '../../store/pageSizeStore';
import PageHeader from '../../components/common/PageHeader';
import { getDifficultyLabelFromNumber, getDifficultyRange } from '../../utils/difficulty';
import FormModal from '../../components/common/FormModal';
import ProgressModal from '../../components/common/ProgressModal';
import { useProgressModal } from '../../hooks/useProgressModal';
import { useResourceDebug } from '../../hooks/useResourceDebug';
import { useToast } from '../../components/ui/toast/ToastContainer';
import { ArrowUpIcon, PlusIcon, ListIcon, DownloadIcon } from '../../icons';
import { useKeywordsImportExport } from '../../config/import-export.config';
import { createKeywordsPageConfig } from '../../config/pages/keywords.config';

export default function Keywords() {
  const toast = useToast();
  const { activeSite } = useSiteStore();
  const { activeSector, loadSectorsForSite } = useSectorStore();
  const { pageSize } = usePageSizeStore();

  // Data state
  const [keywords, setKeywords] = useState<Keyword[]>([]);
  const [clusters, setClusters] = useState<Cluster[]>([]);
  const [availableSeedKeywords, setAvailableSeedKeywords] = useState<SeedKeyword[]>([]);
  const [loading, setLoading] = useState(true);
  const [loadingSeedKeywords, setLoadingSeedKeywords] = useState(false);
  
  // Filter state - match Keywords.tsx
  const [searchTerm, setSearchTerm] = useState('');
  const [statusFilter, setStatusFilter] = useState('');
  const [clusterFilter, setClusterFilter] = useState('');
  const [intentFilter, setIntentFilter] = useState('');
  const [difficultyFilter, setDifficultyFilter] = useState('');
  const [volumeMin, setVolumeMin] = useState<number | ''>('');
  const [volumeMax, setVolumeMax] = useState<number | ''>('');
  const [isVolumeDropdownOpen, setIsVolumeDropdownOpen] = useState(false);
  const [tempVolumeMin, setTempVolumeMin] = useState<number | ''>('');
  const [tempVolumeMax, setTempVolumeMax] = useState<number | ''>('');
  const volumeDropdownRef = useRef<HTMLDivElement>(null);
  const volumeButtonRef = useRef<HTMLButtonElement>(null);
  const [selectedIds, setSelectedIds] = useState<string[]>([]);
  
  // Pagination state
  const [currentPage, setCurrentPage] = useState(1);
  const [totalPages, setTotalPages] = useState(1);
  const [totalCount, setTotalCount] = useState(0);
  
  // Sorting state (global - not page-specific)
  const [sortBy, setSortBy] = useState<string>('created_at');
  const [sortDirection, setSortDirection] = useState<'asc' | 'desc'>('desc');
  const [showContent, setShowContent] = useState(false);
  
  // Modal state
  const [isModalOpen, setIsModalOpen] = useState(false);
  const [isEditMode, setIsEditMode] = useState(false);
  const [editingKeyword, setEditingKeyword] = useState<Keyword | null>(null);
  const [formData, setFormData] = useState<KeywordCreateData>({
    seed_keyword_id: 0,
    volume_override: null,
    difficulty_override: null,
    cluster_id: null,
    status: 'pending',
  });

  // Progress modal for AI functions
  const progressModal = useProgressModal();
  const hasReloadedRef = useRef(false);

  // Resource Debug toggle - controls AI Function Logs
  const resourceDebugEnabled = useResourceDebug();

  // AI Function Logs state
  const [aiLogs, setAiLogs] = useState<Array<{
    timestamp: string;
    type: 'request' | 'success' | 'error' | 'step';
    action: string;
    data: any;
    stepName?: string;
    percentage?: number;
  }>>([]);
  
  // Track last logged step to avoid duplicates
  const lastLoggedStepRef = useRef<string | null>(null);
  const lastLoggedPercentageRef = useRef<number>(-1);

  // Helper function to add log entry (only if Resource Debug is enabled)
  const addAiLog = useCallback((log: {
    timestamp: string;
    type: 'request' | 'success' | 'error' | 'step';
    action: string;
    data: any;
    stepName?: string;
    percentage?: number;
  }) => {
    if (resourceDebugEnabled) {
      setAiLogs(prev => [...prev, log]);
    }
  }, [resourceDebugEnabled]);

  // Load sectors for active site using sector store
  useEffect(() => {
    if (activeSite) {
      loadSectorsForSite(activeSite.id);
    }
  }, [activeSite, loadSectorsForSite]);

  // Load available SeedKeywords when site and sector are selected
  useEffect(() => {
    const loadAvailableSeedKeywords = async () => {
      if (!activeSite || !activeSector || !activeSite.industry) {
        setAvailableSeedKeywords([]);
        return;
      }

      try {
        setLoadingSeedKeywords(true);
        // Fetch SeedKeywords for the site's industry and sector's industry_sector
        const response = await fetchSeedKeywords({
          industry: activeSite.industry,
          sector: activeSector.industry_sector || undefined,
        });
        
        // Filter out SeedKeywords that are already attached to this site/sector
        const attachedSeedKeywordIds = new Set(
          keywords.map(k => k.seed_keyword_id)
        );
        
        const available = (response.results || []).filter(
          sk => !attachedSeedKeywordIds.has(sk.id)
        );
        
        setAvailableSeedKeywords(available);
      } catch (error: any) {
        console.error('Failed to load available seed keywords:', error);
        setAvailableSeedKeywords([]);
      } finally {
        setLoadingSeedKeywords(false);
      }
    };

    loadAvailableSeedKeywords();
  }, [activeSite, activeSector, keywords]);

  // Load clusters for filter dropdown
  useEffect(() => {
    const loadClusters = async () => {
      try {
        const data = await fetchClusters({ ordering: 'name' });
        setClusters(data.results || []);
      } catch (error) {
        console.error('Error fetching clusters:', error);
      }
    };
    loadClusters();
  }, []);

  // Load keywords - wrapped in useCallback to prevent infinite loops
  const loadKeywords = useCallback(async () => {
    setLoading(true);
    setShowContent(false); // Reset showContent to show loading state
    try {
      // Build ordering parameter from sort state
      const ordering = sortBy ? `${sortDirection === 'desc' ? '-' : ''}${sortBy}` : '-created_at';
      
      // Build filters object from individual filter states
      // Only include filters that have actual values (not empty strings)
      // Use activeSector from store for sector filtering
      const filters: KeywordFilters = {
        ...(searchTerm && { search: searchTerm }),
        ...(statusFilter && { status: statusFilter }),
        ...(clusterFilter && { cluster_id: clusterFilter }),
        ...(intentFilter && { intent: intentFilter }),
        ...(activeSector?.id && { sector_id: activeSector.id }),
        page: currentPage,
        page_size: pageSize || 10, // Ensure we always send a page_size
        ordering,
      };
      
      // Add difficulty range filter
      if (difficultyFilter) {
        const difficultyNum = parseInt(difficultyFilter);
        const label = getDifficultyLabelFromNumber(difficultyNum);
        if (label !== null) {
          const range = getDifficultyRange(label);
          if (range) {
            filters.difficulty_min = range.min;
            filters.difficulty_max = range.max;
          }
        }
      }
      
      // Add volume range filters
      if (volumeMin !== '' && volumeMin !== null && volumeMin !== undefined) {
        filters.volume_min = Number(volumeMin);
      }
      if (volumeMax !== '' && volumeMax !== null && volumeMax !== undefined) {
        filters.volume_max = Number(volumeMax);
      }
      
      const data = await fetchKeywords(filters);
      setKeywords(data.results || []);
      setTotalCount(data.count || 0);
      setTotalPages(Math.ceil((data.count || 0) / pageSize));
      
      // Show content after data loads (smooth reveal)
      setTimeout(() => {
        setShowContent(true);
        setLoading(false);
      }, 100);
    } catch (error: any) {
      console.error('Error loading keywords:', error);
      toast.error(`Failed to load keywords: ${error.message}`);
      setShowContent(true);
      setLoading(false);
    }
    }, [currentPage, statusFilter, clusterFilter, intentFilter, difficultyFilter, volumeMin, volumeMax, sortBy, sortDirection, searchTerm, activeSite, activeSector, pageSize]);

  // Listen for site and sector changes and refresh data
  useEffect(() => {
    const handleSiteChange = () => {
      // Reload keywords when site changes (sector store will auto-select first sector)
      loadKeywords();
      // Reload clusters for new site
      const loadClusters = async () => {
        try {
          const data = await fetchClusters({ ordering: 'name' });
          setClusters(data.results || []);
        } catch (error) {
          console.error('Error fetching clusters:', error);
        }
      };
      loadClusters();
    };
    
    const handleSectorChange = () => {
      // Reload keywords when sector changes
      loadKeywords();
    };
    
    window.addEventListener('siteChanged', handleSiteChange);
    window.addEventListener('sectorChanged', handleSectorChange);
    return () => {
      window.removeEventListener('siteChanged', handleSiteChange);
      window.removeEventListener('sectorChanged', handleSectorChange);
    };
  }, [loadKeywords]);

  // Handle click outside volume dropdown
  useEffect(() => {
    const handleClickOutside = (event: MouseEvent) => {
      if (
        volumeDropdownRef.current &&
        !volumeDropdownRef.current.contains(event.target as Node) &&
        volumeButtonRef.current &&
        !volumeButtonRef.current.contains(event.target as Node)
      ) {
        setIsVolumeDropdownOpen(false);
        setTempVolumeMin(volumeMin);
        setTempVolumeMax(volumeMax);
      }
    };

    if (isVolumeDropdownOpen) {
      document.addEventListener("mousedown", handleClickOutside);
      return () => {
        document.removeEventListener("mousedown", handleClickOutside);
      };
    }
  }, [isVolumeDropdownOpen, volumeMin, volumeMax]);

  // Load data on mount and when filters change (excluding search - handled separately)
  useEffect(() => {
    loadKeywords();
  }, [loadKeywords]);

  // Debounced search - reset to page 1 when search term changes
  useEffect(() => {
    const timer = setTimeout(() => {
      // Always reset to page 1 when search changes
      // The main useEffect will handle reloading when currentPage changes
      setCurrentPage(1);
    }, 500);

    return () => clearTimeout(timer);
  }, [searchTerm]); // Only depend on searchTerm

  // Handle pageSize changes - reload data when pageSize changes
  // Note: TablePageTemplate already calls onPageChange(1), but we need to ensure reload happens
  useEffect(() => {
    // When pageSize changes:
    // 1. Reset to page 1 (TablePageTemplate does this, but we ensure it)
    // 2. loadKeywords will be recreated because pageSize is in its dependency array
    // 3. The useEffect that depends on loadKeywords will fire and reload data
    // But we need to ensure reload happens even if currentPage is already 1
    const wasOnPage1 = currentPage === 1;
    setCurrentPage(1);
    
    // If we were already on page 1, explicitly trigger reload
    // Otherwise, the currentPage change will trigger reload via loadKeywords dependency
    if (wasOnPage1) {
      // Use a small timeout to ensure state has updated
      setTimeout(() => {
        loadKeywords();
      }, 0);
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [pageSize]); // Only depend on pageSize - loadKeywords will be recreated when pageSize changes


  // Handle sorting (global)
  const handleSort = (field: string, direction: 'asc' | 'desc') => {
    setSortBy(field || 'created_at');
    setSortDirection(direction);
    setCurrentPage(1); // Reset to first page when sorting changes
  };

  // Import/Export handlers
  const { handleExport, handleImportClick, ImportModal } = useKeywordsImportExport(
    () => {
      toast.success('Import successful', 'Keywords imported successfully.');
      loadKeywords();
    },
    (error) => {
      toast.error('Import failed', error.message);
    },
    // Pass active site_id and active sector_id for import
    activeSite && activeSector
      ? { site_id: activeSite.id, sector_id: activeSector.id }
      : undefined
  );

  // Handle bulk actions (delete, export, update_status are now handled by TablePageTemplate)
  // This is only for actions that don't have modals (like auto_cluster)
  const handleBulkAction = useCallback(async (action: string, ids: string[]) => {
    if (action === 'auto_cluster') {
      if (ids.length === 0) {
        toast.error('Please select at least one keyword to cluster');
        return;
      }
      if (ids.length > 20) {
        toast.error('Maximum 20 keywords allowed for clustering');
        return;
      }
      
      const numIds = ids.map(id => parseInt(id));
      const sectorId = activeSector?.id;
      const selectedKeywords = keywords.filter(k => numIds.includes(k.id));
      const requestData = {
        ids: numIds,
        keyword_count: numIds.length,
        keyword_names: selectedKeywords.map(k => k.keyword),
        sector_id: sectorId,
      };
      
      // Log request (only if Resource Debug is enabled)
      addAiLog({
        timestamp: new Date().toISOString(),
        type: 'request',
        action: 'auto_cluster (Bulk Action)',
        data: requestData,
      });
      
      try {
        const result = await autoClusterKeywords(numIds, sectorId);
        
        // Check if result has success field - if false, it's an error response
        if (result && result.success === false) {
          // Error response from API
          const errorMsg = result.error || 'Failed to cluster keywords';
          // Log error
          addAiLog({
            timestamp: new Date().toISOString(),
            type: 'error',
            action: 'auto_cluster (Bulk Action)',
            data: { error: errorMsg, keyword_count: numIds.length },
          });
          toast.error(errorMsg);
          return;
        }
        
        if (result && result.success) {
          if (result.task_id) {
            // Log success with task_id
            addAiLog({
              timestamp: new Date().toISOString(),
              type: 'success',
              action: 'auto_cluster (Bulk Action)',
              data: { task_id: result.task_id, message: result.message, keyword_count: numIds.length },
            });
            // Async task - open progress modal
            hasReloadedRef.current = false;
            progressModal.openModal(result.task_id, 'Auto-Clustering Keywords', 'ai-auto-cluster-01');
            // Don't show toast - progress modal will show status
          } else {
            // Log success with results
            addAiLog({
              timestamp: new Date().toISOString(),
              type: 'success',
              action: 'auto_cluster (Bulk Action)',
              data: { 
                clusters_created: result.clusters_created || 0, 
                keywords_updated: result.keywords_updated || 0,
                keyword_count: numIds.length,
                message: result.message,
              },
            });
            // Synchronous completion
            toast.success(`Clustering complete: ${result.clusters_created || 0} clusters created, ${result.keywords_updated || 0} keywords updated`);
            if (!hasReloadedRef.current) {
              hasReloadedRef.current = true;
              loadKeywords();
            }
          }
        } else {
          // Unexpected response format - show error
          const errorMsg = result?.error || 'Unexpected response format';
          // Log error
          addAiLog({
            timestamp: new Date().toISOString(),
            type: 'error',
            action: 'auto_cluster (Bulk Action)',
            data: { error: errorMsg, keyword_count: numIds.length },
          });
          toast.error(errorMsg);
        }
      } catch (error: any) {
        // API error (network error, parse error, etc.)
        let errorMsg = 'Failed to cluster keywords';
        if (error.message) {
          // Extract clean error message from API error format
          errorMsg = error.message.replace(/^API Error \(\d+\): [^-]+ - /, '').trim();
          if (!errorMsg || errorMsg === error.message) {
            errorMsg = error.message;
          }
        }
        // Log error
        addAiLog({
          timestamp: new Date().toISOString(),
          type: 'error',
          action: 'auto_cluster (Bulk Action)',
          data: { error: errorMsg, keyword_count: numIds.length },
        });
        toast.error(errorMsg);
      }
    } else {
      toast.info(`Bulk action "${action}" for ${ids.length} items`);
    }
  }, [toast, activeSector, loadKeywords, progressModal, keywords]);

  // Log AI function progress steps
  useEffect(() => {
    if (!progressModal.taskId || !progressModal.isOpen) {
      return;
    }

    const progress = progressModal.progress;
    const currentStep = progress.details?.phase || '';
    const currentPercentage = progress.percentage;
    const currentMessage = progress.message;
    const currentStatus = progress.status;

    // Log step changes
    if (currentStep && currentStep !== lastLoggedStepRef.current) {
      const stepType = currentStatus === 'error' ? 'error' : 
                      currentStatus === 'completed' ? 'success' : 'step';
      
      addAiLog({
        timestamp: new Date().toISOString(),
        type: stepType,
        action: progressModal.title || 'AI Function',
        stepName: currentStep,
        percentage: currentPercentage,
        data: {
          step: currentStep,
          message: currentMessage,
          percentage: currentPercentage,
          status: currentStatus,
          details: progress.details,
        },
      });
      
      lastLoggedStepRef.current = currentStep;
      lastLoggedPercentageRef.current = currentPercentage;
    }
    // Log percentage changes for same step (if significant change)
    else if (currentStep && Math.abs(currentPercentage - lastLoggedPercentageRef.current) >= 10) {
      const stepType = currentStatus === 'error' ? 'error' : 
                      currentStatus === 'completed' ? 'success' : 'step';
      
      addAiLog({
        timestamp: new Date().toISOString(),
        type: stepType,
        action: progressModal.title || 'AI Function',
        stepName: currentStep,
        percentage: currentPercentage,
        data: {
          step: currentStep,
          message: currentMessage,
          percentage: currentPercentage,
          status: currentStatus,
          details: progress.details,
        },
      });
      
      lastLoggedPercentageRef.current = currentPercentage;
    }
    // Log status changes (error, completed)
    else if (currentStatus === 'error' || currentStatus === 'completed') {
      // Only log if we haven't already logged this status for this step
      if (currentStep !== lastLoggedStepRef.current || 
          (currentStatus === 'error' && lastLoggedStepRef.current !== 'error') ||
          (currentStatus === 'completed' && lastLoggedStepRef.current !== 'completed')) {
        const stepType = currentStatus === 'error' ? 'error' : 'success';
        
        addAiLog({
          timestamp: new Date().toISOString(),
          type: stepType,
          action: progressModal.title || 'AI Function',
          stepName: currentStep || 'Final',
          percentage: currentPercentage,
          data: {
            step: currentStep || 'Final',
            message: currentMessage,
            percentage: currentPercentage,
            status: currentStatus,
            details: progress.details,
          },
        });
        
        lastLoggedStepRef.current = currentStep || currentStatus;
      }
    }
  }, [progressModal.progress, progressModal.taskId, progressModal.isOpen, progressModal.title, addAiLog]);

  // Reset step tracking when modal closes or opens
  useEffect(() => {
    if (!progressModal.isOpen) {
      lastLoggedStepRef.current = null;
      lastLoggedPercentageRef.current = -1;
      hasReloadedRef.current = false; // Reset reload flag when modal closes
    } else {
      // Reset reload flag when modal opens for a new task
      hasReloadedRef.current = false;
    }
  }, [progressModal.isOpen, progressModal.taskId]);

  const resetForm = useCallback(() => {
    setFormData({
      seed_keyword_id: 0,
      volume_override: null,
      difficulty_override: null,
      cluster_id: null,
      status: 'pending',
    });
    setIsEditMode(false);
    setEditingKeyword(null);
  }, []);


  // Bulk export handler for selected items (used by TablePageTemplate modal)
  const handleBulkExport = useCallback(async (ids: string[]) => {
    try {
      // Ensure we have valid IDs
      if (!ids || ids.length === 0) {
        throw new Error('No records selected for export');
      }
      
      // For bulk export, ONLY export selected IDs - ignore ALL other filters
      // Build URL directly to ensure only 'ids' parameter is sent
      const idsParam = ids.join(',');
      const exportUrl = `${API_BASE_URL}/v1/planner/keywords/export/?ids=${encodeURIComponent(idsParam)}`;
      
      const response = await fetch(exportUrl, {
        method: 'GET',
        credentials: 'include',
      });

      if (!response.ok) {
        const errorText = await response.text();
        throw new Error(`Export failed: ${response.statusText} - ${errorText}`);
      }

      const blob = await response.blob();
      const downloadUrl = window.URL.createObjectURL(blob);
      const link = document.createElement('a');
      link.href = downloadUrl;
      link.download = 'keywords.csv';
      document.body.appendChild(link);
      link.click();
      document.body.removeChild(link);
      window.URL.revokeObjectURL(downloadUrl);
    } catch (error: any) {
      throw error; // Let TablePageTemplate handle toast
    }
  }, []);

  // Bulk status update handler (used by TablePageTemplate modal)
  const handleBulkUpdateStatus = useCallback(async (ids: string[], status: string) => {
    try {
      const numIds = ids.map(id => parseInt(id));
      await bulkUpdateKeywordsStatus(numIds, status);
      await loadKeywords(); // Reload data after status update
    } catch (error: any) {
      throw error; // Let TablePageTemplate handle toast
    }
  }, [loadKeywords]);

  // Create page config using factory function - all config comes from keywords.config.tsx
  const pageConfig = useMemo(() => {
    return createKeywordsPageConfig({
      clusters,
      activeSector,
      availableSeedKeywords,
      formData,
      setFormData,
      // Filter state handlers
      searchTerm,
      setSearchTerm,
      statusFilter,
      setStatusFilter,
      intentFilter,
      setIntentFilter,
      difficultyFilter,
      setDifficultyFilter,
      clusterFilter,
      setClusterFilter,
      volumeMin,
      volumeMax,
      setVolumeMin,
      setVolumeMax,
      isVolumeDropdownOpen,
      setIsVolumeDropdownOpen,
      tempVolumeMin,
      tempVolumeMax,
      setTempVolumeMin,
      setTempVolumeMax,
      volumeButtonRef,
      volumeDropdownRef,
      setCurrentPage,
      loadKeywords,
    });
  }, [
    clusters,
    activeSector,
    availableSeedKeywords,
    formData,
    searchTerm,
    statusFilter,
    intentFilter,
    difficultyFilter,
    clusterFilter,
    volumeMin,
    volumeMax,
    isVolumeDropdownOpen,
    tempVolumeMin,
    tempVolumeMax,
    loadKeywords,
    activeSite,
  ]);

  // Calculate header metrics from config (matching reference plugin KPIs from kpi-config.php)
  const headerMetrics = useMemo(() => {
    if (!pageConfig?.headerMetrics) return [];
    return pageConfig.headerMetrics.map((metric) => ({
      label: metric.label,
      value: metric.calculate({ keywords, totalCount, clusters }),
      accentColor: metric.accentColor,
    }));
  }, [pageConfig?.headerMetrics, keywords, totalCount, clusters]);

  // Handle create/edit
  const handleSave = async () => {
    try {
      if (!activeSite) {
        toast.error('Please select an active site first');
        return;
      }
      
      if (isEditMode && editingKeyword) {
        await updateKeyword(editingKeyword.id, formData);
        toast.success('Keyword updated successfully');
      } else {
        // For new keywords, add site_id and sector_id
        // Use active sector from store
        if (!activeSector) {
          toast.error('Please select a sector for this site first');
          return;
        }
        
        if (!formData.seed_keyword_id) {
          toast.error('Please select a seed keyword');
          return;
        }
        
        const sectorId = activeSector.id;
        
        const keywordData: any = {
          ...formData,
          site_id: activeSite.id,
          sector_id: sectorId,
        };
        
        await createKeyword(keywordData);
        toast.success('Keyword attached successfully');
      }
      setIsModalOpen(false);
      resetForm();
      loadKeywords();
    } catch (error: any) {
      toast.error(`Failed to save: ${error.message}`);
    }
  };

  // Handle edit - populate form with existing keyword data
  const handleEdit = useCallback((keyword: Keyword) => {
    setEditingKeyword(keyword);
    setIsEditMode(true);
    setFormData({
      seed_keyword_id: keyword.seed_keyword_id,
      volume_override: keyword.volume_override || null,
      difficulty_override: keyword.difficulty_override || null,
      cluster_id: keyword.cluster_id,
      status: keyword.status,
    });
    setIsModalOpen(true);
  }, []);

  return (
    <>
      <PageHeader 
        title="Keywords" 
        badge={{ icon: <ListIcon />, color: 'green' }}
      />
      <TablePageTemplate
        columns={pageConfig.columns}
        data={keywords}
        loading={loading}
        showContent={showContent}
        filters={pageConfig.filters}
        filterValues={{
          search: searchTerm,
          status: statusFilter,
          intent: intentFilter,
          difficulty: difficultyFilter,
          cluster_id: clusterFilter,
          volumeMin: volumeMin,
          volumeMax: volumeMax,
        }}
        onFilterChange={(key, value) => {
          // Normalize value to string, preserving empty strings
          const stringValue = value === null || value === undefined ? '' : String(value);
          
          // Map filter keys to state setters
          if (key === 'search') {
            setSearchTerm(stringValue);
          } else if (key === 'status') {
            setStatusFilter(stringValue);
            setCurrentPage(1);
          } else if (key === 'intent') {
            setIntentFilter(stringValue);
            setCurrentPage(1);
          } else if (key === 'difficulty') {
            setDifficultyFilter(stringValue);
            setCurrentPage(1);
          } else if (key === 'cluster_id') {
            setClusterFilter(stringValue);
            setCurrentPage(1);
          }
          // Note: volume filter is handled by custom render, cluster options updated dynamically
        }}
        onEdit={handleEdit}
        onCreate={() => {
          resetForm();
          setIsModalOpen(true);
        }}
        createLabel="Add Keyword"
        onCreateIcon={<PlusIcon />}
        onDelete={async (id: number) => {
          await deleteKeyword(id);
          loadKeywords();
        }}
        onBulkDelete={async (ids: number[]) => {
          const result = await bulkDeleteKeywords(ids);
          // Clear selection first
          setSelectedIds([]);
          // Reset to page 1 if we deleted all items on current page
          if (currentPage > 1 && keywords.length <= ids.length) {
            setCurrentPage(1);
          }
          // Always reload data to refresh the table
          await loadKeywords();
          return result;
        }}
        onBulkExport={handleBulkExport}
        onBulkUpdateStatus={handleBulkUpdateStatus}
        onBulkAction={handleBulkAction}
        getItemDisplayName={(row: Keyword) => row.keyword}
        onExport={async () => {
          try {
            const filterValues = {
              search: searchTerm,
              status: statusFilter,
              cluster_id: clusterFilter,
              intent: intentFilter,
              difficulty: difficultyFilter,
            };
            await handleExport('csv', filterValues);
            toast.success('Export successful', 'Keywords exported successfully.');
          } catch (error: any) {
            toast.error('Export failed', error.message);
          }
        }}
        onExportIcon={<DownloadIcon />}
        onImport={handleImportClick}
        onImportIcon={<ArrowUpIcon />}
        selectionLabel="keyword"
        pagination={{
          currentPage,
          totalPages,
          totalCount,
          onPageChange: (page: number) => {
            setCurrentPage(page);
          },
        }}
        selection={{
          selectedIds,
          onSelectionChange: setSelectedIds,
        }}
        sorting={{
          sortBy,
          sortDirection,
          onSort: handleSort,
        }}
        headerMetrics={headerMetrics}
        onFilterReset={() => {
          setSearchTerm('');
          setStatusFilter('');
          setClusterFilter('');
          setIntentFilter('');
          setDifficultyFilter('');
          setVolumeMin('');
          setVolumeMax('');
          setTempVolumeMin('');
          setTempVolumeMax('');
          setIsVolumeDropdownOpen(false);
          setCurrentPage(1);
        }}
      />

      {/* Create/Edit Modal */}
      <FormModal
        isOpen={isModalOpen}
        onClose={() => {
          setIsModalOpen(false);
          resetForm();
        }}
        onSubmit={handleSave}
        title={isEditMode ? 'Edit Keyword' : 'Add Keyword'}
        submitLabel={isEditMode ? 'Update' : 'Create'}
        fields={pageConfig.formFields(clusters)}
      />

      {/* Import Modal */}
      <ImportModal />

      {/* Progress Modal for AI Functions */}
      <ProgressModal
        isOpen={progressModal.isOpen}
        title={progressModal.title}
        percentage={progressModal.progress.percentage}
        status={progressModal.progress.status}
        message={progressModal.progress.message}
        details={progressModal.progress.details}
        taskId={progressModal.taskId || undefined}
        functionId={progressModal.functionId}
        onClose={() => {
          progressModal.closeModal();
          // Reload once when modal closes if task was completed
          if (progressModal.progress.status === 'completed' && !hasReloadedRef.current) {
            hasReloadedRef.current = true;
            loadKeywords();
          }
        }}
      />

      {/* AI Function Logs - Display below table (only when Resource Debug is enabled) */}
      {resourceDebugEnabled && aiLogs.length > 0 && (
        <div className="mt-6 bg-gray-50 dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700 p-4">
          <div className="flex items-center justify-between mb-3">
            <h3 className="text-sm font-semibold text-gray-900 dark:text-gray-100">
              AI Function Logs
            </h3>
            <button
              onClick={() => setAiLogs([])}
              className="text-xs text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
            >
              Clear Logs
            </button>
          </div>
          <div className="space-y-2 max-h-96 overflow-y-auto">
            {aiLogs.slice().reverse().map((log, index) => (
              <div
                key={index}
                className={`p-3 rounded border text-xs font-mono ${
                  log.type === 'request'
                    ? 'bg-blue-50 dark:bg-blue-900/20 border-blue-200 dark:border-blue-800'
                    : log.type === 'success'
                    ? 'bg-green-50 dark:bg-green-900/20 border-green-200 dark:border-green-800'
                    : log.type === 'error'
                    ? 'bg-red-50 dark:bg-red-900/20 border-red-200 dark:border-red-800'
                    : 'bg-purple-50 dark:bg-purple-900/20 border-purple-200 dark:border-purple-800'
                }`}
              >
                <div className="flex items-center justify-between mb-1">
                  <div className="flex items-center gap-2 flex-wrap">
                    <span className={`font-semibold ${
                      log.type === 'request'
                        ? 'text-blue-700 dark:text-blue-300'
                        : log.type === 'success'
                        ? 'text-green-700 dark:text-green-300'
                        : log.type === 'error'
                        ? 'text-red-700 dark:text-red-300'
                        : 'text-purple-700 dark:text-purple-300'
                    }`}>
                      [{log.type.toUpperCase()}]
                    </span>
                    <span className="text-gray-700 dark:text-gray-300">
                      {log.action}
                    </span>
                    {log.stepName && (
                      <span className="text-xs px-2 py-0.5 rounded bg-gray-200 dark:bg-gray-700 text-gray-600 dark:text-gray-400">
                        {log.stepName}
                      </span>
                    )}
                    {log.percentage !== undefined && (
                      <span className="text-xs text-gray-500 dark:text-gray-400">
                        {log.percentage}%
                      </span>
                    )}
                  </div>
                  <span className="text-gray-500 dark:text-gray-400">
                    {new Date(log.timestamp).toLocaleTimeString()}
                  </span>
                </div>
                <pre className="text-xs text-gray-700 dark:text-gray-300 whitespace-pre-wrap break-words">
                  {JSON.stringify(log.data, null, 2)}
                </pre>
              </div>
            ))}
          </div>
        </div>
      )}
    </>
  );
}

