import React, { useState, useMemo, useRef, useEffect, useCallback } from 'react';
import { useLanguage } from '../../../providers/language/LanguageContext';
import styles from './FormGeneratorTable.module.css';
import { 
  EditActionButton, 
  DeleteActionButton, 
  ViewActionButton,
  CopyActionButton,
  CustomActionButton
} from '../ActionButtons';
import { formatUnixTimestamp } from '../../../utils/time';
import { FormGeneratorControls } from '../FormGeneratorControls';
import { CopyableTruncatedValue } from '../../UiComponents/CopyableTruncatedValue';
import {
  isDateTimeType,
  isCheckboxType
} from '../../../utils/attributeTypeMapper';
import type { AttributeType } from '../../../utils/attributeTypeMapper';
import { FaFilter } from 'react-icons/fa';
import api from '../../../api';

// FK Cache type: maps fkSource -> { id -> displayLabel }
type FkCacheType = Record<string, Record<string, string>>;

// Helper function to detect TextMultilingual objects
// TextMultilingual has structure: { en: string, ge?: string, fr?: string, it?: string }
const isTextMultilingual = (value: any): boolean => {
  if (!value || typeof value !== 'object' || Array.isArray(value) || value instanceof Date) {
    return false;
  }
  // Check if it has 'en' property (required) and optionally other language codes
  return 'en' in value && typeof value.en === 'string';
};

// Helper function to format TextMultilingual for display
const formatTextMultilingual = (value: any, currentLanguage?: string): string => {
  if (!isTextMultilingual(value)) {
    return String(value);
  }
  
  // Map language codes (backend uses 'ge' for German, frontend might use 'de')
  const languageMap: Record<string, string> = {
    'de': 'ge',
    'en': 'en',
    'fr': 'fr',
    'it': 'it'
  };
  
  // Try to get value for current language
  if (currentLanguage) {
    const backendLang = languageMap[currentLanguage] || currentLanguage;
    if (value[backendLang] && typeof value[backendLang] === 'string' && value[backendLang].trim()) {
      return value[backendLang];
    }
  }
  
  // Fallback to English (required field)
  if (value.en && typeof value.en === 'string' && value.en.trim()) {
    return value.en;
  }
  
  // If no English, try other languages
  const languages = ['ge', 'fr', 'it'];
  for (const lang of languages) {
    if (value[lang] && typeof value[lang] === 'string' && value[lang].trim()) {
      return value[lang];
    }
  }
  
  return '-';
};

// Types for the FormGeneratorTable
export interface ColumnConfig {
  key: string;
  label: string;
  type?: AttributeType;
  width?: number;
  minWidth?: number;
  maxWidth?: number;
  sortable?: boolean;
  filterable?: boolean;
  searchable?: boolean;
  formatter?: (value: any, row: any) => React.ReactNode;
  filterOptions?: string[]; // For enum/select filters
  cellClassName?: (value: any, row: any) => string; // For custom cell styling
  fkSource?: string; // API endpoint for FK resolution (e.g., "/api/users/")
  fkDisplayField?: string; // Which field of FK target to display (e.g., "username", "name", "roleLabel")
}

export interface FormGeneratorTableProps<T = any> {
  data: T[];
  columns?: ColumnConfig[];
  title?: string;
  searchable?: boolean;
  filterable?: boolean;
  sortable?: boolean;
  resizable?: boolean;
  pagination?: boolean;
  pageSize?: number;
  pageSizeOptions?: number[];
  showPageSizeSelector?: boolean;
  onRowClick?: (row: T, index: number) => void;
  onRowSelect?: (selectedRows: T[]) => void;
  selectable?: boolean;
  isRowSelectable?: (row: T) => boolean;
  loading?: boolean;
  // Inline editing configuration
  inlineEditable?: boolean; // Enable inline editing for supported field types (checkbox/boolean)
  onInlineUpdate?: (row: T, field: string, newValue: any) => Promise<void> | void; // Called when inline edit occurs
  inlineEditingRows?: Set<string>; // Set of row IDs currently being edited (for loading state)
  idField?: string; // Field name for unique row identifier (default: 'id')
  // Standard action buttons (edit, delete, view, copy, connect, play)
  actionButtons?: {
    type: 'edit' | 'delete' | 'view' | 'copy' | 'connect' | 'play';
    onAction?: (row: T) => Promise<void> | void; // Optional for delete buttons since they handle their own logic
    disabled?: (row: T, hookData?: any) => boolean | { disabled: boolean; message?: string };
    loading?: (row: T, hookData?: any) => boolean;
    title?: string | ((row: T) => string);
    className?: string;
    // For view buttons
    isProcessing?: (row: T, hookData?: any) => boolean;
    // Field mappings for flexible data access
    idField?: string; // Field name for the unique identifier
    nameField?: string; // Field name for display name
    typeField?: string; // Field name for type/mime type
    contentField?: string; // Field name for content (used by copy button)
    statusField?: string; // Field name for status (used by connect action)
    // Operation and loading state names
    operationName?: string; // Name of the operation function in hookData
    loadingStateName?: string; // Name of the loading state in hookData
    fetchItemFunctionName?: string; // Name of the function to fetch a single item (for edit button)
    // Navigation and mode (for play action)
    navigateTo?: string; // Path to navigate to after action
    mode?: string; // Mode to set (e.g., 'prompt', 'workflow')
  }[];
  // Custom action buttons (entity-specific actions like download, connect, play, sendPasswordLink)
  customActions?: {
    id: string; // Unique identifier for the action
    icon: React.ReactNode; // Icon component to display
    onClick: (row: T, hookData?: any) => Promise<void> | void; // Handler function
    visible?: (row: T, hookData?: any) => boolean; // Show/hide based on row data (default: true)
    disabled?: (row: T, hookData?: any) => boolean | { disabled: boolean; message?: string };
    loading?: (row: T, hookData?: any) => boolean; // Loading state based on row data
    title?: string | ((row: T) => string); // Tooltip text
    className?: string; // Optional custom CSS class
    idField?: string; // Field name for unique identifier (default: 'id')
  }[];
  onDelete?: (row: T) => void;
  onDeleteMultiple?: (rows: T[]) => void;
  onRefresh?: () => void;
  className?: string;
  getRowDataAttributes?: (row: T, index: number) => Record<string, string>;
  // For passing hook data to action buttons
  hookData?: any; // Contains all hook data: refetch, operations, loading states, etc.
  // Custom empty message when table is empty
  emptyMessage?: string;
}

export function FormGeneratorTable<T extends Record<string, any>>({
  data,
  columns: providedColumns,
  searchable = true,
  filterable = true,
  sortable = true,
  resizable = true,
  pagination = true,
  pageSize = 10,
  pageSizeOptions = [10, 25, 50, 100, 500],
  showPageSizeSelector = true,
  onRowClick,
  onRowSelect,
  selectable = true, // Default to true for selection functionality
  isRowSelectable,
  loading = false,
  inlineEditable = true, // Enable inline editing by default
  onInlineUpdate,
  inlineEditingRows,
  idField = 'id',
  actionButtons = [],
  customActions = [],
  onDelete,
  onDeleteMultiple,
  onRefresh,
  className = '',
  getRowDataAttributes,
  hookData,
  emptyMessage
}: FormGeneratorTableProps<T>) {
  const { t } = useLanguage();
  // Get current language from localStorage or default to 'en'
  const currentLanguage = useMemo(() => {
    try {
      const stored = localStorage.getItem('language') || localStorage.getItem('app-language');
      if (stored) {
        // Map frontend language codes to backend codes
        const langMap: Record<string, string> = { 'de': 'ge', 'en': 'en', 'fr': 'fr', 'it': 'it' };
        return langMap[stored] || stored;
      }
    } catch {
      // Ignore localStorage errors
    }
    return 'en'; // Default to English
  }, []);
  // Use provided columns from Pydantic attribute definitions
  // NO AUTO-DETECTION - columns must come from backend attribute definitions
  // Use a ref to cache columns so they persist across data changes (e.g., when filtering)
  const columnsRef = useRef<ColumnConfig[]>([]);
  
  const detectedColumns = useMemo((): ColumnConfig[] => {
    // Use providedColumns from Pydantic attribute definitions
    if (providedColumns && providedColumns.length > 0) {
      columnsRef.current = providedColumns;
      return providedColumns;
    }
    
    // Use cached columns if data becomes empty (e.g., after filtering)
    if (columnsRef.current.length > 0) {
      return columnsRef.current;
    }
    
    // NO COLUMNS PROVIDED - this is an error in the calling component
    // The calling component should provide columns from the /attributes/{entityType} endpoint
    console.warn(
      '⚠️ FormGeneratorTable: No columns provided! ' +
      'Columns should come from Pydantic attribute definitions via /attributes/{entityType} endpoint. ' +
      'Please ensure the calling component fetches and passes columns from the backend.'
    );
    
    // Return empty array - table will show no columns
    return [];
  }, [providedColumns, data]);

  // State management
  const [searchTerm, setSearchTerm] = useState('');
  const [searchFocused, setSearchFocused] = useState(false);
  const [filterFocused, setFilterFocused] = useState<Record<string, boolean>>({});
  // Multi-column sorting: array of sort configs in order of priority
  const [sortConfigs, setSortConfigs] = useState<Array<{ key: string; direction: 'asc' | 'desc' }>>([]);
  const [filters, setFilters] = useState<Record<string, any>>({});
  const [columnWidths, setColumnWidths] = useState<Record<string, number>>({});
  // Actions column width - resizable, default based on number of buttons
  const [actionsColumnWidth, setActionsColumnWidth] = useState<number | null>(null);
  const [selectedRows, setSelectedRows] = useState<Set<number>>(new Set());
  const [currentPage, setCurrentPage] = useState(1);
  const [currentPageSize, setCurrentPageSize] = useState(pageSize);
  const [openFilterColumn, setOpenFilterColumn] = useState<string | null>(null);
  const filterDropdownRef = useRef<HTMLDivElement>(null);
  
  // FK Resolution: Cache for resolved FK values (fkSource -> { id -> displayLabel })
  const [fkCache, setFkCache] = useState<FkCacheType>({});
  const [fkLoading, setFkLoading] = useState<Record<string, boolean>>({});
  const fkLoadedSourcesRef = useRef<Set<string>>(new Set());
  
  // Generate a storage key based on column names for localStorage persistence
  const storageKey = useMemo(() => {
    if (detectedColumns.length === 0) return null;
    const columnNames = detectedColumns.map(c => c.key).sort().join(',');
    return `table-column-widths-${columnNames.substring(0, 50)}`; // Limit key length
  }, [detectedColumns]);
  
  // Track if we've loaded from localStorage for this storage key
  const loadedStorageKeyRef = useRef<string | null>(null);
  
  // Check if backend pagination is supported (hookData has refetch that accepts params)
  const supportsBackendPagination = hookData?.refetch && typeof hookData.refetch === 'function';
  
  // Debounce search term for backend calls
  const [debouncedSearchTerm, setDebouncedSearchTerm] = useState(searchTerm);
  
  useEffect(() => {
    const timer = setTimeout(() => {
      setDebouncedSearchTerm(searchTerm);
    }, 300); // 300ms debounce
    
    return () => clearTimeout(timer);
  }, [searchTerm]);
  
  // Call backend when filters/search/sort/pagination change
  useEffect(() => {
    if (!supportsBackendPagination || !hookData?.refetch) return;
    
    // Build pagination parameters
    const paginationParams: any = {
      page: currentPage,
      pageSize: currentPageSize,
    };
    
    // Add search if provided
    if (debouncedSearchTerm && debouncedSearchTerm.trim()) {
      paginationParams.search = debouncedSearchTerm.trim();
    }
    
    // Add filters if provided
    // Note: Date/timestamp filters are disabled in column config, so they won't appear here
    const activeFilters: Record<string, any> = {};
    Object.entries(filters).forEach(([key, value]) => {
      if (value !== undefined && value !== '') {
        activeFilters[key] = value;
      }
    });
    if (Object.keys(activeFilters).length > 0) {
      paginationParams.filters = activeFilters;
    }
    
    // Add sort if provided (multi-column sorting)
    if (sortConfigs.length > 0) {
      paginationParams.sort = sortConfigs.map(sc => ({
        field: sc.key,
        direction: sc.direction
      }));
    }
    
    // Log search parameters being sent to backend
    console.log('🔍 FormGeneratorTable: Calling backend with pagination params:', {
      searchTerm: debouncedSearchTerm,
      searchInParams: paginationParams.search,
      filters: paginationParams.filters,
      sort: paginationParams.sort,
      page: paginationParams.page,
      pageSize: paginationParams.pageSize,
      fullParams: paginationParams
    });
    
    // Call backend refetch with parameters
    hookData.refetch(paginationParams).then(() => {
      console.log('✅ FormGeneratorTable: Backend refetch completed');
    }).catch((error: any) => {
      console.error('❌ FormGeneratorTable: Backend refetch failed:', error);
    });
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [debouncedSearchTerm, filters, sortConfigs, currentPage, currentPageSize, supportsBackendPagination]);
  
  // Refs for action buttons containers to detect clicks outside
  const actionButtonsRefs = useRef<Map<number, HTMLDivElement>>(new Map());
  
  // Refs for resizing
  const tableRef = useRef<HTMLTableElement>(null);
  const tableContainerRef = useRef<HTMLDivElement>(null);
  
  // Track container width for actions column 20% threshold
  const [containerWidth, setContainerWidth] = useState<number>(0);
  
  // Calculate default actions column width and track container width
  const defaultActionsWidth = useMemo(() => {
    return actionButtons.length > 0 ? Math.max(60, actionButtons.length * 32 + 16) : 0;
  }, [actionButtons.length]);
  
  // Current actions column width (user-defined or default)
  const currentActionsWidth = actionsColumnWidth ?? defaultActionsWidth;
  
  // Check if actions column exceeds 20% of container width (enable wrapping)
  const shouldWrapActionButtons = containerWidth > 0 && currentActionsWidth > containerWidth * 0.20;
  
  // Track container width changes
  useEffect(() => {
    const container = tableContainerRef.current;
    if (!container) return;
    
    const updateContainerWidth = () => {
      setContainerWidth(container.clientWidth);
    };
    
    // Initial measurement
    updateContainerWidth();
    
    // Observe resize
    const resizeObserver = new ResizeObserver(updateContainerWidth);
    resizeObserver.observe(container);
    
    return () => resizeObserver.disconnect();
  }, []);
  const resizingColumn = useRef<string | null>(null);
  const startX = useRef<number>(0);
  const startWidth = useRef<number>(0);

  // Initialize column widths - load from localStorage or set defaults
  useEffect(() => {
    if (detectedColumns.length === 0 || !storageKey) return;
    
    // Only load from localStorage once per storage key
    if (loadedStorageKeyRef.current === storageKey) {
      // Already loaded for this key, just add defaults for any new columns
      setColumnWidths(prev => {
        const newWidths = { ...prev };
        let hasChanges = false;
        
        detectedColumns.forEach(col => {
          if (newWidths[col.key] === undefined) {
            newWidths[col.key] = col.width || 150;
            hasChanges = true;
          }
        });
        
        return hasChanges ? newWidths : prev;
      });
      return;
    }
    
    // Load from localStorage for this new storage key
    loadedStorageKeyRef.current = storageKey;
    
    try {
      const saved = localStorage.getItem(storageKey);
      if (saved) {
        const savedWidths = JSON.parse(saved);
        // Merge saved widths with defaults for any missing columns
        const mergedWidths: Record<string, number> = {};
        detectedColumns.forEach(col => {
          mergedWidths[col.key] = savedWidths[col.key] ?? col.width ?? 150;
        });
        setColumnWidths(mergedWidths);
        return;
      }
    } catch {
      // Ignore localStorage errors
    }
    
    // No saved widths, set defaults
    const defaultWidths: Record<string, number> = {};
    detectedColumns.forEach(col => {
      defaultWidths[col.key] = col.width || 150;
    });
    setColumnWidths(defaultWidths);
  }, [detectedColumns, storageKey]);
  
  // Save column widths to localStorage when they change (debounced via resize completion)
  const saveColumnWidthsToStorage = useRef((widths: Record<string, number>, key: string | null) => {
    if (Object.keys(widths).length > 0 && key) {
      try {
        localStorage.setItem(key, JSON.stringify(widths));
      } catch {
        // Ignore localStorage errors
      }
    }
  }).current;

  // Helper function to convert any field value to display string
  // Handles: string, boolean, number, TextMultilingual, objects
  const convertToDisplayString = useCallback((fieldValue: any, language: string): string => {
    if (fieldValue === null || fieldValue === undefined) {
      return '-';
    }
    
    // Boolean → language-neutral symbols (✓/✗)
    if (typeof fieldValue === 'boolean') {
      return fieldValue ? '✓' : '✗';
    }
    
    // Number → String
    if (typeof fieldValue === 'number') {
      return String(fieldValue);
    }
    
    // String → direct
    if (typeof fieldValue === 'string') {
      return fieldValue;
    }
    
    // Object - check for TextMultilingual (has 'en' key)
    if (typeof fieldValue === 'object' && fieldValue !== null) {
      // TextMultilingual: { en: "...", ge: "...", fr: "...", it: "..." }
      if ('en' in fieldValue) {
        // Map frontend language codes to backend codes
        const langMap: Record<string, string> = { 'de': 'ge', 'en': 'en', 'fr': 'fr', 'it': 'it' };
        const backendLang = langMap[language] || language;
        
        // Try current language first, then fallback
        if (fieldValue[backendLang] && typeof fieldValue[backendLang] === 'string' && fieldValue[backendLang].trim()) {
          return fieldValue[backendLang];
        }
        if (fieldValue.en && typeof fieldValue.en === 'string' && fieldValue.en.trim()) {
          return fieldValue.en;
        }
        // Try other languages
        for (const lang of ['ge', 'fr', 'it']) {
          if (fieldValue[lang] && typeof fieldValue[lang] === 'string' && fieldValue[lang].trim()) {
            return fieldValue[lang];
          }
        }
      }
      
      // Other objects → try to stringify
      try {
        return JSON.stringify(fieldValue);
      } catch {
        return String(fieldValue);
      }
    }
    
    // Fallback
    return String(fieldValue);
  }, []);

  // FK Resolution: Load FK data in bulk for columns with fkSource
  useEffect(() => {
    if (data.length === 0 || detectedColumns.length === 0) return;

    // Find columns with fkSource that haven't been loaded yet
    const fkColumns = detectedColumns.filter(col => 
      col.fkSource && !fkLoadedSourcesRef.current.has(col.fkSource)
    );

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

    // For each FK column, collect unique IDs from data and fetch them
    const loadFkData = async () => {
      for (const column of fkColumns) {
        const fkSource = column.fkSource!;
        const displayField = column.fkDisplayField; // Explicit field from Pydantic model
        
        // Skip if already loading
        if (fkLoading[fkSource]) continue;

        // Collect unique IDs from data for this column
        const uniqueIds = new Set<string>();
        data.forEach(row => {
          const value = row[column.key];
          if (value && typeof value === 'string' && value.length > 0) {
            uniqueIds.add(value);
          }
        });

        if (uniqueIds.size === 0) {
          fkLoadedSourcesRef.current.add(fkSource);
          continue;
        }

        // Mark as loading
        setFkLoading(prev => ({ ...prev, [fkSource]: true }));

        try {
          // Fetch all items from the FK source endpoint
          const response = await api.get(fkSource);
          
          // Build cache: id -> display label
          const cacheForSource: Record<string, string> = {};
          const items = Array.isArray(response.data) ? response.data : response.data?.items || [];
          
          items.forEach((item: any) => {
            if (!item || !item.id) return;
            
            let displayLabel = item.id; // Fallback to ID
            
            // Use the EXPLICIT display field from Pydantic model (fkDisplayField)
            if (displayField && item[displayField] !== undefined) {
              displayLabel = convertToDisplayString(item[displayField], currentLanguage);
            } else {
              // Fallback: if no displayField specified, try common fields
              // This should rarely happen if models are properly configured
              const fallbackFields = ['name', 'label', 'username', 'roleLabel', 'title'];
              for (const field of fallbackFields) {
                if (item[field] !== undefined) {
                  displayLabel = convertToDisplayString(item[field], currentLanguage);
                  break;
                }
              }
            }
            
            cacheForSource[item.id] = displayLabel;
          });

          // Update cache
          setFkCache(prev => ({
            ...prev,
            [fkSource]: { ...(prev[fkSource] || {}), ...cacheForSource }
          }));

          // Mark as loaded
          fkLoadedSourcesRef.current.add(fkSource);
        } catch (error) {
          console.error(`Failed to load FK data from ${fkSource}:`, error);
          // Mark as loaded to prevent infinite retries
          fkLoadedSourcesRef.current.add(fkSource);
        } finally {
          setFkLoading(prev => ({ ...prev, [fkSource]: false }));
        }
      }
    };

    loadFkData();
  }, [data, detectedColumns, currentLanguage, fkLoading, convertToDisplayString]);

  // Helper function to resolve FK value to display label
  const resolveFkValue = useCallback((value: string, fkSource: string): string => {
    const sourceCache = fkCache[fkSource];
    if (sourceCache && sourceCache[value]) {
      return sourceCache[value];
    }
    // Return truncated ID while loading or if not found
    return value.length > 8 ? `${value.substring(0, 8)}...` : value;
  }, [fkCache]);

  // Data is already filtered, sorted, and paginated by the backend
  // No client-side processing needed
  const displayData = data;

  // Get pagination info from backend
  const totalPages = useMemo(() => {
    // If pagination object exists, use totalPages from backend
    if (hookData?.pagination) {
      // Debug logging
      if (import.meta.env.DEV) {
        console.log('📊 FormGeneratorTable pagination data:', {
          totalPages: hookData.pagination.totalPages,
          totalItems: hookData.pagination.totalItems,
          currentPageSize,
          calculatedPages: hookData.pagination.totalItems && currentPageSize 
            ? Math.ceil(hookData.pagination.totalItems / currentPageSize) 
            : 'N/A'
        });
      }
      
      if (hookData.pagination.totalPages) {
        return hookData.pagination.totalPages;
      }
      // Fallback: calculate totalPages from totalItems and pageSize
      if (hookData.pagination.totalItems && currentPageSize) {
        const calculated = Math.ceil(hookData.pagination.totalItems / currentPageSize);
        return calculated;
      }
    }
    
    // Default to 1 page (pagination bar will still show)
    return 1;
  }, [hookData?.pagination, currentPageSize]);

  // Handle multi-column sorting
  // Click cycle: not sorted → ascending → descending → not sorted
  // When adding a new column, it becomes the last (lowest priority) sort
  const handleSort = (key: string) => {
    if (!sortable) return;
    
    setSortConfigs(current => {
      const existingIndex = current.findIndex(sc => sc.key === key);
      
      if (existingIndex === -1) {
        // Column not in sort list → add as ascending (lowest priority)
        return [...current, { key, direction: 'asc' }];
      }
      
      const existing = current[existingIndex];
      if (existing.direction === 'asc') {
        // Ascending → change to descending (keep same position)
        const newConfigs = [...current];
        newConfigs[existingIndex] = { key, direction: 'desc' };
        return newConfigs;
      }
      
      // Descending → remove from sort list
      return current.filter(sc => sc.key !== key);
    });
  };

  // Get sort info for a column (returns { direction, position } or null)
  const getSortInfo = useCallback((key: string): { direction: 'asc' | 'desc'; position: number } | null => {
    const index = sortConfigs.findIndex(sc => sc.key === key);
    if (index === -1) return null;
    return { direction: sortConfigs[index].direction, position: index + 1 };
  }, [sortConfigs]);

  // Handle filtering
  const handleFilter = (key: string, value: any) => {
    setFilters(prev => {
      const newFilters = { ...prev };
      if (value === undefined || value === '' || value === null) {
        delete newFilters[key];
      } else {
        newFilters[key] = value;
      }
      return newFilters;
    });
    setCurrentPage(1); // Reset to first page when filtering
    setOpenFilterColumn(null); // Close filter dropdown
  };

  // Handle filter input focus
  const handleFilterFocus = (key: string, focused: boolean) => {
    setFilterFocused(prev => ({
      ...prev,
      [key]: focused
    }));
  };

  // Clear filter for a column
  const clearFilter = useCallback((key: string) => {
    setFilters(prev => {
      const newFilters = { ...prev };
      delete newFilters[key];
      return newFilters;
    });
    setCurrentPage(1);
    setOpenFilterColumn(null);
  }, []);

  // Count active filters
  const activeFiltersCount = useMemo(() => {
    return Object.keys(filters).filter(k => filters[k] !== undefined && filters[k] !== '').length;
  }, [filters]);

  // Get unique values for a column (for filter dropdown)
  const getUniqueValuesForColumn = useCallback((columnKey: string): string[] => {
    const values = new Set<string>();
    data.forEach(row => {
      const value = row[columnKey];
      if (value !== undefined && value !== null && value !== '') {
        // Handle different value types
        if (typeof value === 'object' && !Array.isArray(value)) {
          // For objects (like TextMultilingual), get the string representation
          if (isTextMultilingual(value)) {
            const text = value.en || Object.values(value)[0];
            if (text) values.add(String(text));
          } else {
            values.add(JSON.stringify(value));
          }
        } else if (typeof value === 'boolean') {
          values.add(value ? 'true' : 'false');
        } else {
          values.add(String(value));
        }
      }
    });
    return Array.from(values).sort();
  }, [data]);

  // Close filter dropdown when clicking outside
  useEffect(() => {
    const handleClickOutside = (event: MouseEvent) => {
      if (filterDropdownRef.current && !filterDropdownRef.current.contains(event.target as Node)) {
        setOpenFilterColumn(null);
      }
    };

    if (openFilterColumn) {
      document.addEventListener('mousedown', handleClickOutside);
      return () => document.removeEventListener('mousedown', handleClickOutside);
    }
  }, [openFilterColumn]);

  // Toggle filter dropdown
  const toggleFilterDropdown = useCallback((columnKey: string, event: React.MouseEvent) => {
    event.stopPropagation(); // Prevent sort from triggering
    setOpenFilterColumn(prev => prev === columnKey ? null : columnKey);
  }, []);

  // Handle row selection
  const handleRowSelect = (index: number) => {
    if (!selectable) return;
    
    const row = displayData[index];
    if (isRowSelectable && !isRowSelectable(row)) return;
    
    const newSelected = new Set(selectedRows);
    if (newSelected.has(index)) {
      newSelected.delete(index);
    } else {
      newSelected.add(index);
    }
    setSelectedRows(newSelected);
    
    if (onRowSelect) {
      const selectedData = Array.from(newSelected).map(i => displayData[i]);
      onRowSelect(selectedData);
    }
  };

  // Handle select all
  const handleSelectAll = () => {
    if (!selectable) return;
    
    // Get only selectable rows
    const selectableIndices = displayData
      .map((row, index) => ({ row, index }))
      .filter(({ row }) => !isRowSelectable || isRowSelectable(row))
      .map(({ index }) => index);
    
    if (selectedRows.size === selectableIndices.length) {
      setSelectedRows(new Set());
      onRowSelect?.([]);
    } else {
      const allSelectableIndices = new Set(selectableIndices);
      setSelectedRows(allSelectableIndices);
      const selectableData = selectableIndices.map(i => displayData[i]);
      onRowSelect?.(selectableData);
    }
  };

  // Handle delete single item
  const handleDeleteSingle = (row: T, index: number) => {
    if (onDelete) {
      onDelete(row);
      // Remove from selection if it was selected
      if (selectedRows.has(index)) {
        const newSelected = new Set(selectedRows);
        newSelected.delete(index);
        setSelectedRows(newSelected);
        if (onRowSelect) {
          const selectedData = Array.from(newSelected).map(i => displayData[i]);
          onRowSelect(selectedData);
        }
      }
    }
  };

  // Handle delete multiple items
  const handleDeleteMultiple = () => {
    if (onDeleteMultiple && selectedRows.size > 0) {
      const selectedData = Array.from(selectedRows).map(i => displayData[i]);
      onDeleteMultiple(selectedData);
      // Clear selection
      setSelectedRows(new Set());
      onRowSelect?.([]);
    }
  };

  // Handle page size change
  const handlePageSizeChange = (newPageSize: number) => {
    setCurrentPageSize(newPageSize);
    setCurrentPage(1); // Reset to first page when page size changes
  };

  // Handle column resizing - use refs to store stable handler references
  const handleMouseMoveRef = useRef<((e: MouseEvent) => void) | null>(null);
  const handleMouseUpRef = useRef<(() => void) | null>(null);
  // Track if we're resizing the actions column
  const resizingActionsColumn = useRef<boolean>(false);

  const handleMouseDown = (e: React.MouseEvent, columnKey: string) => {
    if (!resizable) return;
    
    e.preventDefault();
    e.stopPropagation(); // Prevent sorting when resizing
    
    resizingColumn.current = columnKey;
    startX.current = e.clientX;
    startWidth.current = columnWidths[columnKey] || 150;
    
    // Create stable handler functions and store in refs
    const mouseMoveHandler = (moveEvent: MouseEvent) => {
      if (!resizingColumn.current) return;
      
      const diff = moveEvent.clientX - startX.current;
      let newWidth = Math.max(100, startWidth.current + diff);
      
      // Prevent extending beyond table container
      const tableContainer = tableRef.current?.parentElement;
      if (tableContainer) {
        const cWidth = tableContainer.clientWidth;
        // Calculate actions column width dynamically: ~32px per button + padding
        const actionsColWidth = currentActionsWidth;
        const selectColumnWidth = selectable ? 50 : 0;
        const fixedWidth = actionsColWidth + selectColumnWidth;
        
        // Maximum allowed width - simple calculation to prevent overflow
        const maxAllowedWidth = cWidth - fixedWidth - 100; // Leave space for other columns
        newWidth = Math.min(newWidth, Math.max(100, maxAllowedWidth));
      }
      
      setColumnWidths(prev => ({
        ...prev,
        [resizingColumn.current!]: newWidth
      }));
    };
    
    const mouseUpHandler = () => {
      const resizedColumn = resizingColumn.current;
      resizingColumn.current = null;
      if (handleMouseMoveRef.current) {
        document.removeEventListener('mousemove', handleMouseMoveRef.current);
      }
      if (handleMouseUpRef.current) {
        document.removeEventListener('mouseup', handleMouseUpRef.current);
      }
      handleMouseMoveRef.current = null;
      handleMouseUpRef.current = null;
      
      // Save to localStorage after resize completes
      if (resizedColumn) {
        setColumnWidths(currentWidths => {
          saveColumnWidthsToStorage(currentWidths, storageKey);
          return currentWidths;
        });
      }
    };
    
    // Store handlers in refs for cleanup
    handleMouseMoveRef.current = mouseMoveHandler;
    handleMouseUpRef.current = mouseUpHandler;
    
    document.addEventListener('mousemove', mouseMoveHandler);
    document.addEventListener('mouseup', mouseUpHandler);
  };

  // Handle actions column resizing (separate handler)
  const handleActionsColumnMouseDown = (e: React.MouseEvent) => {
    if (!resizable) return;
    
    e.preventDefault();
    e.stopPropagation();
    
    resizingActionsColumn.current = true;
    startX.current = e.clientX;
    startWidth.current = currentActionsWidth;
    
    const mouseMoveHandler = (moveEvent: MouseEvent) => {
      if (!resizingActionsColumn.current) return;
      
      const diff = moveEvent.clientX - startX.current;
      // Minimum width: default width based on buttons, maximum: 40% of container
      const minWidth = defaultActionsWidth;
      const maxWidth = containerWidth > 0 ? containerWidth * 0.4 : 400;
      const newWidth = Math.max(minWidth, Math.min(maxWidth, startWidth.current + diff));
      
      setActionsColumnWidth(newWidth);
    };
    
    const mouseUpHandler = () => {
      resizingActionsColumn.current = false;
      if (handleMouseMoveRef.current) {
        document.removeEventListener('mousemove', handleMouseMoveRef.current);
      }
      if (handleMouseUpRef.current) {
        document.removeEventListener('mouseup', handleMouseUpRef.current);
      }
      handleMouseMoveRef.current = null;
      handleMouseUpRef.current = null;
    };
    
    handleMouseMoveRef.current = mouseMoveHandler;
    handleMouseUpRef.current = mouseUpHandler;
    
    document.addEventListener('mousemove', mouseMoveHandler);
    document.addEventListener('mouseup', mouseUpHandler);
  };
  
  // Cleanup on unmount
  useEffect(() => {
    return () => {
      if (handleMouseMoveRef.current) {
        document.removeEventListener('mousemove', handleMouseMoveRef.current);
      }
      if (handleMouseUpRef.current) {
        document.removeEventListener('mouseup', handleMouseUpRef.current);
      }
    };
  }, []);

  // Track which cells are currently being updated (for loading state)
  const [updatingCells, setUpdatingCells] = useState<Set<string>>(new Set());

  // Check if inline editing is allowed for a column (based on RBAC permissions)
  const canInlineEdit = useMemo(() => {
    // Auto-enable if inlineEditable is explicitly true OR if handleInlineUpdate is available
    const hasInlineSupport = inlineEditable || hookData?.handleInlineUpdate || onInlineUpdate;
    if (!hasInlineSupport) return false;
    // Check RBAC permissions - need update permission
    const permissions = hookData?.permissions;
    if (!permissions) return false;
    // Permission 'n' means no access, anything else (a, m, g) means allowed
    return permissions.update !== 'n' && permissions.view;
  }, [inlineEditable, hookData?.handleInlineUpdate, hookData?.permissions, onInlineUpdate]);

  // Check if a column supports inline editing (only checkbox/boolean types)
  const isInlineEditableColumn = useCallback((column: ColumnConfig): boolean => {
    if (!column.type) return false;
    return isCheckboxType(column.type);
  }, []);

  // Handle inline toggle for boolean fields
  const handleInlineToggle = useCallback(async (row: T, column: ColumnConfig, currentValue: boolean) => {
    if (!canInlineEdit || !isInlineEditableColumn(column)) return;
    
    const rowId = row[idField];
    const cellKey = `${rowId}-${column.key}`;
    
    // Check if update function is available (either from prop or hookData)
    const updateFn = onInlineUpdate || hookData?.handleInlineUpdate;
    if (!updateFn) {
      // Silent return - inline editing is optional, no warning needed
      return;
    }
    
    // Mark cell as updating
    setUpdatingCells(prev => new Set(prev).add(cellKey));
    
    const newValue = !currentValue;
    const hasOptimisticUpdate = !!hookData?.updateOptimistically;
    
    // If updateOptimistically is available, use it for immediate UI feedback
    if (hasOptimisticUpdate) {
      hookData.updateOptimistically(rowId, { [column.key]: newValue });
    }
    
    try {
      // Call the update function (generic - no entity-specific logic)
      if (onInlineUpdate) {
        await onInlineUpdate(row, column.key, newValue);
      } else if (hookData?.handleInlineUpdate) {
        // Pass row as third parameter for hooks that need to merge changes with existing data
        await hookData.handleInlineUpdate(rowId, { [column.key]: newValue }, row);
      }
      
      // Only refetch if we DON'T have optimistic update (to get fresh data)
      // With optimistic update, local state is already correct
      if (!hasOptimisticUpdate && hookData?.refetch) {
        await hookData.refetch();
      }
    } catch (error) {
      console.error('FormGeneratorTable: Inline update failed:', error);
      // Revert optimistic update on error
      if (hasOptimisticUpdate) {
        hookData.updateOptimistically(rowId, { [column.key]: currentValue });
      }
      // Refetch to restore consistent state on error
      if (hookData?.refetch) {
        await hookData.refetch();
      }
    } finally {
      // Remove cell from updating state
      setUpdatingCells(prev => {
        const newSet = new Set(prev);
        newSet.delete(cellKey);
        return newSet;
      });
    }
  }, [canInlineEdit, isInlineEditableColumn, idField, onInlineUpdate, hookData]);

  // Render inline-editable boolean cell
  const renderBooleanCell = useCallback((value: any, column: ColumnConfig, row: T): React.ReactNode => {
    const boolValue = Boolean(value);
    const rowId = row[idField];
    const cellKey = `${rowId}-${column.key}`;
    const isUpdating = updatingCells.has(cellKey) || inlineEditingRows?.has(rowId);
    const isEditable = canInlineEdit && isInlineEditableColumn(column);
    
    // Loading spinner for updating state
    if (isUpdating) {
      return (
        <span className={styles.booleanCell}>
          <span className={styles.booleanLoading}>⋯</span>
        </span>
      );
    }
    
    // Clickable or static based on permissions
    if (isEditable) {
      return (
        <button
          type="button"
          className={`${styles.booleanCell} ${styles.booleanEditable} ${boolValue ? styles.booleanTrue : styles.booleanFalse}`}
          onClick={(e) => {
            e.stopPropagation(); // Prevent row click
            handleInlineToggle(row, column, boolValue);
          }}
          title={t('formgen.table.clickToToggle', 'Click to toggle')}
        >
          {boolValue ? '✓' : '○'}
        </button>
      );
    }
    
    // Non-editable display
    return (
      <span className={`${styles.booleanCell} ${styles.booleanReadonly} ${boolValue ? styles.booleanTrue : styles.booleanFalse}`}>
        {boolValue ? '✓' : '○'}
      </span>
    );
  }, [idField, updatingCells, inlineEditingRows, canInlineEdit, isInlineEditableColumn, handleInlineToggle, t]);

  // Check if a column is an ID field (generic logic only)
  // Matches: "id", "ID", "id" followed by uppercase (e.g., "idUser"), ends with "Id" or "ID" (e.g., "userId", "userID")
  const isIdField = (columnKey: string): boolean => {
    return /^(id|ID|_id)$/.test(columnKey) ||  // Exact match: "id", "ID", "_id"
           /^id[A-Z]/.test(columnKey) ||        // Starts with "id" + uppercase: "idUser", "idMandate"
           /Id$/.test(columnKey) ||             // Ends with "Id": "userId", "mandateId"
           /ID$/.test(columnKey);               // Ends with "ID": "userID", "mandateID"
  };

  // Check if a column is a hash field
  const isHashField = (columnKey: string, value: any): boolean => {
    const hashPatterns = /hash|Hash|HASH/i;
    if (hashPatterns.test(columnKey)) {
      return true;
    }
    // Also check if value looks like a hash (long alphanumeric string)
    if (typeof value === 'string' && value.length > 20) {
      const hashLikePattern = /^[a-f0-9]{20,}$/i;
      return hashLikePattern.test(value);
    }
    return false;
  };

  // Format cell value
  const formatCellValue = (value: any, column: ColumnConfig, row: T) => {
    if (value === null || value === undefined) {
      return '-';
    }

    // Handle boolean/checkbox fields with inline editing support
    if (column.type && isCheckboxType(column.type)) {
      return renderBooleanCell(value, column, row);
    }
    
    // Also detect boolean values even if column type isn't explicitly set
    if (typeof value === 'boolean') {
      return renderBooleanCell(value, column, row);
    }

    // FK Resolution: If column has fkSource and value is a string (UUID), resolve to display label
    if (column.fkSource && typeof value === 'string' && value.length > 0) {
      const resolvedLabel = resolveFkValue(value, column.fkSource);
      const isLoading = fkLoading[column.fkSource];
      
      // Show loading indicator or resolved label
      if (isLoading && !fkCache[column.fkSource]?.[value]) {
        return <span className={styles.fkLoading}>{value.substring(0, 8)}...</span>;
      }
      
      return resolvedLabel;
    }

    // Check if this is an ID or hash field that should be truncated and copyable
    // Do this BEFORE checking for custom formatters to ensure IDs/hashes are always copyable
    const isId = isIdField(column.key);
    const isHash = isHashField(column.key, value);
    
    if ((isId || isHash)) {
      // Convert to string if needed
      const stringValue = String(value);
      if (stringValue.length > 0) {
        return <CopyableTruncatedValue value={stringValue} maxLength={isHash ? 12 : 20} />;
      }
    }

    // Handle objects/arrays (e.g., references to other entities)
    // Check if value is an object (but not Date, Array, or null)
    if (typeof value === 'object' && value !== null && !(value instanceof Date) && !Array.isArray(value)) {
      // Check if this is a TextMultilingual object first
      if (isTextMultilingual(value)) {
        return formatTextMultilingual(value, currentLanguage);
      }
      
      // Try to find a display field in common order: label, name, title, id
      const displayFields = ['label', 'name', 'title', 'id', 'value', 'text'];
      for (const field of displayFields) {
        if (value[field] !== undefined && value[field] !== null) {
          const displayValue = value[field];
          // If the display value is itself an object, check if it's TextMultilingual
          if (typeof displayValue === 'object' && displayValue !== null && !Array.isArray(displayValue) && !(displayValue instanceof Date)) {
            if (isTextMultilingual(displayValue)) {
              return formatTextMultilingual(displayValue, currentLanguage);
            }
            try {
              return JSON.stringify(displayValue);
            } catch {
              return String(displayValue);
            }
          }
          return String(displayValue);
        }
      }
      // If no display field found, try to stringify the object (limited to avoid huge output)
      try {
        const stringified = JSON.stringify(value);
        // Truncate if too long
        if (stringified.length > 100) {
          return stringified.substring(0, 97) + '...';
        }
        return stringified;
      } catch {
        // If stringification fails, show object type
        return `[${value.constructor?.name || 'Object'}]`;
      }
    }

    // Handle arrays (e.g., multiselect values)
    if (Array.isArray(value)) {
      if (value.length === 0) {
        return '-';
      }
      // If array contains objects, try to extract display values
      const displayValues = value.map(item => {
        if (typeof item === 'object' && item !== null) {
          // Try to find a display field
          const displayFields = ['label', 'name', 'title', 'id', 'value', 'text'];
          for (const field of displayFields) {
            if (item[field] !== undefined && item[field] !== null) {
              return String(item[field]);
            }
          }
          // Fallback to stringified object
          try {
            return JSON.stringify(item);
          } catch {
            return String(item);
          }
        }
        return String(item);
      });
      return displayValues.join(', ');
    }

    // Use custom formatter if provided (but only if not an ID/hash field)
    if (column.formatter) {
      return column.formatter(value, row);
    }
    
    // Check if this is a timestamp field based on name OR explicit type
    // Do NOT treat arbitrary numbers as timestamps - only if field name suggests it
    const isTimestampField = /(at|date|time|timestamp|created|updated|expires|checked|valuta)$/i.test(column.key);
    const isExplicitDateType = column.type && isDateTimeType(column.type);
    
    // Only format as date if: field name suggests timestamp OR explicit date type
    // Do NOT format based on value range alone - this causes amounts/percentages to show as dates
    if ((isTimestampField || isExplicitDateType) && typeof value === 'number') {
      try {
        // Handle Unix timestamps in seconds (backend format)
        let timestamp: number;
        if (value < 10000000000) {
          // Likely Unix timestamp in seconds
          timestamp = value;
        } else {
          // Likely Unix timestamp in milliseconds
          timestamp = value / 1000;
        }
        
        const formatted = formatUnixTimestamp(timestamp, undefined, {
          year: 'numeric',
          month: '2-digit',
          day: '2-digit',
          hour: '2-digit',
          minute: '2-digit',
          second: '2-digit',
          hour12: false
        });
        
        return `${formatted.time} ${formatted.timezone}`;
      } catch (error) {
        console.error('Error formatting timestamp:', error, value);
        // Fall through to default number formatting
      }
    }
    
    switch (column.type) {
      case 'date':
      case 'timestamp':
      case 'time':
        try {
          // Handle Unix timestamps in seconds (backend format)
          let timestamp: number;
          
          if (typeof value === 'number') {
            // If it's a number, check if it's in seconds (typical Unix timestamp range)
            // Unix timestamps in seconds are typically much smaller than milliseconds
            if (value < 10000000000) { // Less than year 2286 in seconds
              timestamp = value; // Already in seconds
            } else {
              timestamp = value / 1000; // Convert milliseconds to seconds
            }
          } else if (typeof value === 'string') {
            // Check if it's an ISO date string or date string (contains 'T', '-', or ':')
            if (value.includes('T') || value.includes('-') || value.includes(':')) {
              // Parse as date string and convert to Unix timestamp in seconds
              const date = new Date(value);
              if (isNaN(date.getTime())) return '-';
              timestamp = Math.floor(date.getTime() / 1000);
            } else {
              // Try to parse as number (Unix timestamp as string)
              const numValue = parseFloat(value);
              if (!isNaN(numValue)) {
                if (numValue < 10000000000) { // Less than year 2286 in seconds
                  timestamp = numValue; // Already in seconds
                } else {
                  timestamp = numValue / 1000; // Convert milliseconds to seconds
                }
              } else {
                // Fallback: try parsing as date string
                const date = new Date(value);
                if (isNaN(date.getTime())) return '-';
                timestamp = Math.floor(date.getTime() / 1000);
              }
            }
          } else if (value instanceof Date) {
            // Already a Date object
            if (isNaN(value.getTime())) return '-';
            timestamp = Math.floor(value.getTime() / 1000);
          } else {
            // Try to convert to Date and then to timestamp
            const date = new Date(value);
            if (isNaN(date.getTime())) return '-';
            timestamp = Math.floor(date.getTime() / 1000);
          }
          
          // Use formatUnixTimestamp utility function
          const formatted = formatUnixTimestamp(timestamp, undefined, {
            year: 'numeric',
            month: '2-digit',
            day: '2-digit',
            hour: '2-digit',
            minute: '2-digit',
            second: '2-digit',
            hour12: false
          });
          
          return `${formatted.time} ${formatted.timezone}`;
        } catch (error) {
          console.error('Error formatting date:', error, value);
          return '-';
        }
      case 'boolean':
        return value ? '✓' : '✗';
      case 'number':
        return typeof value === 'number' ? value.toLocaleString() : value;
      default:
        return String(value);
    }
  };

  return (
    <div className={`${styles.formGeneratorTable} ${className}`}>
      
      {(searchable || (selectable && selectedRows.size > 0)) && (
        <FormGeneratorControls
          fields={detectedColumns}
          searchTerm={searchTerm}
          onSearchChange={setSearchTerm}
          searchFocused={searchFocused}
          onSearchFocus={setSearchFocused}
          filters={filters}
          onFilterChange={handleFilter}
          filterFocused={filterFocused}
          onFilterFocus={handleFilterFocus}
          selectedCount={selectedRows.size}
          displayData={displayData}
          onDeleteSingle={selectedRows.size === 1 && onDelete ? () => {
            const selectedIndex = Array.from(selectedRows)[0];
            const selectedRow = displayData[selectedIndex];
            handleDeleteSingle(selectedRow, selectedIndex);
          } : undefined}
          onDeleteMultiple={(() => {
            if (!onDeleteMultiple) return undefined;
            // Show delete multiple button if:
            // 1. More than 1 item is selected, OR
            // 2. All selectable items are selected (even if it's just 1)
            const selectableIndices = displayData
              .map((row, index) => ({ row, index }))
              .filter(({ row }) => !isRowSelectable || isRowSelectable(row))
              .map(({ index }) => index);
            const allSelected = selectedRows.size === selectableIndices.length && selectableIndices.length > 0;
            return (selectedRows.size > 1 || allSelected) ? handleDeleteMultiple : undefined;
          })()}
          onRefresh={onRefresh}
          searchable={searchable}
          selectable={selectable}
          loading={loading}
          activeFiltersCount={activeFiltersCount}
          pagination={pagination}
          currentPage={currentPage}
          totalPages={totalPages}
          currentPageSize={currentPageSize}
          pageSizeOptions={pageSizeOptions}
          showPageSizeSelector={showPageSizeSelector}
          onPageChange={setCurrentPage}
          onPageSizeChange={handlePageSizeChange}
          supportsBackendPagination={supportsBackendPagination}
          hookData={hookData}
        />
      )}

      {/* Table */}
      <div 
        ref={tableContainerRef}
        className={`${styles.tableContainer} ${displayData.length === 0 && !loading ? styles.emptyTable : ''}`}
      >
        {/* Loading overlay - shown while loading */}
        {loading && (
          <div className={styles.loadingOverlay}>
            <div className={styles.loadingSpinner}></div>
            <p>{t('common.loading', 'Loading...')}</p>
          </div>
        )}
        
        {/* Empty state - only shown when not loading AND no data */}
        {!loading && displayData.length === 0 ? (
          <div className={styles.emptyState}>
            <p className={styles.emptyMessage}>{emptyMessage || t('formgen.empty', 'No data available')}</p>
          </div>
        ) : (
          <table ref={tableRef} className={styles.table}>
            <thead>
              <tr>
                {selectable && (
                  <th className={styles.selectColumn} style={{ width: '50px', minWidth: '50px', maxWidth: '50px' }}>
                    <input
                      type="checkbox"
                      checked={(() => {
                        const selectableIndices = displayData
                          .map((row, index) => ({ row, index }))
                          .filter(({ row }) => !isRowSelectable || isRowSelectable(row))
                          .map(({ index }) => index);
                        return selectedRows.size === selectableIndices.length && selectableIndices.length > 0;
                      })()}
                      onChange={handleSelectAll}
                      title={t('formgen.select.all', 'Select all items')}
                    />
                  </th>
                )}
                {actionButtons.length > 0 && (
                  <th 
                    className={styles.actionsColumn}
                    style={{ 
                      width: `${currentActionsWidth}px`, 
                      minWidth: `${defaultActionsWidth}px`,
                      position: 'relative'
                    }}
                  >
                    {resizable && (
                      <div
                        className={styles.resizeHandle}
                        onMouseDown={handleActionsColumnMouseDown}
                      />
                    )}
                  </th>
                )}
                {detectedColumns.map(column => (
                  <th
                    key={column.key}
                    className={`${styles.th} ${sortable && column.sortable ? styles.sortable : ''}`}
                    style={{ 
                      width: columnWidths[column.key] || column.width || 150,
                      minWidth: columnWidths[column.key] || column.width || 150,
                      maxWidth: columnWidths[column.key] || column.width || 150,
                      position: 'relative'
                    }}
                  >
                    <div className={styles.headerContent}>
                      {/* Filter icon */}
                      {filterable && column.filterable !== false && (
                        <button
                          className={`${styles.filterIcon} ${filters[column.key] ? styles.filterActive : ''}`}
                          onClick={(e) => toggleFilterDropdown(column.key, e)}
                          title={filters[column.key] 
                            ? t('formgen.filter.active_value', `Filter: ${filters[column.key]}`).replace('{value}', String(filters[column.key]))
                            : t('formgen.filter.click', 'Click to filter')
                          }
                        >
                          <FaFilter size={10} />
                        </button>
                      )}
                      {/* Sort icon */}
                      {sortable && column.sortable && (() => {
                        const sortInfo = getSortInfo(column.key);
                        return (
                          <span 
                            className={`${styles.sortIcon} ${sortInfo ? styles.sortActive : ''}`}
                            onClick={() => handleSort(column.key)}
                            title={sortInfo 
                              ? t('formgen.sort.active', `Sort ${sortInfo.position}: ${sortInfo.direction === 'asc' ? 'ascending' : 'descending'}`)
                              : t('formgen.sort.click', 'Click to sort')
                            }
                          >
                            {sortInfo ? (
                              <>
                                {sortInfo.direction === 'asc' ? '↑' : '↓'}
                                {sortConfigs.length > 1 && <sub>{sortInfo.position}</sub>}
                              </>
                            ) : '↕'}
                          </span>
                        );
                      })()}
                      {/* Column label */}
                      <span 
                        className={styles.columnLabel}
                        onClick={() => column.sortable && handleSort(column.key)}
                      >
                        {column.label}
                      </span>
                    </div>
                    
                    {/* Filter dropdown */}
                    {openFilterColumn === column.key && (
                      <div 
                        ref={filterDropdownRef}
                        className={styles.filterDropdown}
                        onClick={(e) => e.stopPropagation()}
                      >
                        <div className={styles.filterDropdownHeader}>
                          <span>{t('formgen.filter.title', 'Filter')}: {column.label}</span>
                          {filters[column.key] && (
                            <button
                              className={styles.filterClearBtn}
                              onClick={() => clearFilter(column.key)}
                              title={t('formgen.filter.clear', 'Clear filter')}
                            >
                              ✕
                            </button>
                          )}
                        </div>
                        <div className={styles.filterDropdownOptions}>
                          {/* "All" option to clear filter */}
                          <div
                            className={`${styles.filterOption} ${!filters[column.key] ? styles.filterOptionSelected : ''}`}
                            onClick={() => clearFilter(column.key)}
                          >
                            ({t('formgen.filter.all', 'All')})
                          </div>
                          {/* Unique values from data */}
                          {getUniqueValuesForColumn(column.key).slice(0, 50).map(value => (
                            <div
                              key={value}
                              className={`${styles.filterOption} ${filters[column.key] === value ? styles.filterOptionSelected : ''}`}
                              onClick={() => handleFilter(column.key, value)}
                              title={value}
                            >
                              {value.length > 30 ? value.substring(0, 30) + '...' : value}
                            </div>
                          ))}
                          {getUniqueValuesForColumn(column.key).length > 50 && (
                            <div className={styles.filterOptionMore}>
                              ... {t('formgen.filter.more', 'and {count} more').replace('{count}', String(getUniqueValuesForColumn(column.key).length - 50))}
                            </div>
                          )}
                        </div>
                      </div>
                    )}
                    
                    {resizable && (
                      <div
                        className={styles.resizeHandle}
                        onMouseDown={(e) => handleMouseDown(e, column.key)}
                      />
                    )}
                  </th>
                ))}
              </tr>
            </thead>
            {displayData.length > 0 && (
              <tbody>
                {displayData.map((row, index) => {
                const dataAttributes = getRowDataAttributes ? getRowDataAttributes(row, index) : {};
                return (
                  <tr
                    key={index}
                    className={`${styles.tr} ${selectedRows.has(index) ? styles.selected : ''} ${onRowClick ? styles.clickable : ''}`}
                    onClick={() => onRowClick?.(row, index)}
                    {...Object.fromEntries(
                      Object.entries(dataAttributes).map(([key, value]) => [`data-${key}`, value])
                    )}
                  >
                  {selectable && (
                    <td className={styles.selectColumn} style={{ width: '50px', minWidth: '50px', maxWidth: '50px' }}>
                      <input
                        type="checkbox"
                        checked={selectedRows.has(index)}
                        onChange={() => handleRowSelect(index)}
                        onClick={(e) => e.stopPropagation()}
                        disabled={isRowSelectable && !isRowSelectable(row)}
                        title={
                          isRowSelectable && !isRowSelectable(row) 
                            ? t('formgen.select.disabled', 'This item cannot be selected')
                            : t('formgen.select.item', 'Select this item')
                        }
                        style={{
                          opacity: isRowSelectable && !isRowSelectable(row) ? 0.4 : 1,
                          cursor: isRowSelectable && !isRowSelectable(row) ? 'not-allowed' : 'pointer'
                        }}
                      />
                    </td>
                  )}
                  {actionButtons.length > 0 && (
                    <td 
                      className={styles.actionsColumn}
                      style={{ 
                        width: `${currentActionsWidth}px`, 
                        minWidth: `${defaultActionsWidth}px`
                      }}
                    >
                      <div 
                        ref={(el) => {
                          if (el) {
                            actionButtonsRefs.current.set(index, el);
                          } else {
                            actionButtonsRefs.current.delete(index);
                          }
                        }}
                        className={`${styles.actionButtons} ${shouldWrapActionButtons ? styles.actionButtonsWrap : ''}`}
                      >
                        {/* Standard action buttons (edit, delete, view, copy) */}
                        {actionButtons.map((actionButton, actionIndex) => {
                          const actionTitle = typeof actionButton.title === 'function' 
                            ? actionButton.title(row) 
                            : actionButton.title;
                          
                          // Row-level permission check - uses _permissions from backend API
                          // Backend delivers per-record permissions: { _permissions: { canEdit, canDelete } }
                          let disabledResult: boolean | { disabled: boolean; message?: string } = false;
                          if (actionButton.disabled) {
                            // Explicit disabled function takes precedence
                            disabledResult = actionButton.disabled(row, hookData);
                          } else if (row._permissions) {
                            // Use per-record permissions from backend
                            if (actionButton.type === 'edit' && row._permissions.canUpdate === false) {
                              disabledResult = true;
                            } else if (actionButton.type === 'delete' && row._permissions.canDelete === false) {
                              disabledResult = true;
                            }
                          }
                          
                          const isLoading = actionButton.loading ? actionButton.loading(row) : false;
                          const isProcessing = actionButton.isProcessing ? actionButton.isProcessing(row) : false;

                          const baseProps = {
                            row,
                            disabled: disabledResult,
                            loading: isLoading,
                            className: actionButton.className,
                            title: actionTitle,
                            idField: actionButton.idField ?? 'id',
                            nameField: actionButton.nameField ?? 'name',
                            typeField: actionButton.typeField ?? 'type',
                            contentField: actionButton.contentField ?? 'content',
                            operationName: actionButton.operationName,
                            loadingStateName: actionButton.loadingStateName
                          };

                          switch (actionButton.type) {
                            case 'edit':
                              return <EditActionButton 
                                key={`action-${actionIndex}`} 
                                {...baseProps} 
                                onEdit={actionButton.onAction}
                                hookData={hookData}
                              />;
                            case 'delete':
                              return <DeleteActionButton 
                                key={`action-${actionIndex}`} 
                                {...baseProps} 
                                containerRef={{ current: actionButtonsRefs.current.get(index) || null }} 
                                hookData={hookData} 
                              />;
                            case 'view':
                              return <ViewActionButton 
                                key={`action-${actionIndex}`} 
                                {...baseProps} 
                                onView={actionButton.onAction || (() => {})} 
                                isViewing={isProcessing} 
                                hookData={hookData} 
                              />;
                            case 'copy':
                              return <CopyActionButton 
                                key={`action-${actionIndex}`} 
                                {...baseProps} 
                                onCopy={actionButton.onAction} 
                                isCopying={isProcessing} 
                                contentField={actionButton.contentField} 
                              />;
                            default:
                              return null;
                          }
                        })}
                        {/* Custom action buttons (entity-specific actions) */}
                        {customActions.map((customAction) => (
                          <CustomActionButton
                            key={`custom-${customAction.id}`}
                            row={row}
                            id={customAction.id}
                            icon={customAction.icon}
                            onClick={customAction.onClick}
                            visible={customAction.visible}
                            disabled={customAction.disabled}
                            loading={customAction.loading}
                            title={customAction.title}
                            className={customAction.className}
                            hookData={hookData}
                            idField={customAction.idField ?? 'id'}
                          />
                        ))}
                      </div>
                    </td>
                  )}
                  {detectedColumns.map(column => {
                    const cellValue = row[column.key];
                    const customClassName = column.cellClassName ? column.cellClassName(cellValue, row) : '';
                    const combinedClassName = `${styles.td} ${customClassName}`.trim();
                    
                    return (
                      <td 
                        key={column.key} 
                        className={combinedClassName}
                        style={{ 
                          width: columnWidths[column.key] || column.width || 150,
                          minWidth: columnWidths[column.key] || column.width || 150,
                          maxWidth: columnWidths[column.key] || column.width || 150
                        }}
                      >
                        {formatCellValue(cellValue, column, row)}
                      </td>
                    );
                  })}
                  
                </tr>
                );
                })}
              </tbody>
            )}
          </table>
        )}
      </div>
    </div>
  );
}

export default FormGeneratorTable;

