import React, { useMemo, useCallback, useState } from 'react'
import { 
  useReactTable, 
  getCoreRowModel, 
  flexRender,
  RowSelectionState,
  ColumnDef
} from '@tanstack/react-table'
import { Fields, Field } from '../../../types'
import { RowData, Template } from '../hooks/validationTypes'
import ValidationCell, { CopyDownContext } from './ValidationCell'
import { useRsi } from '../../../hooks/useRsi'
import SearchableTemplateSelect from './SearchableTemplateSelect'
import { Table, TableBody, TableRow, TableCell } from '@/components/ui/table'
import { Checkbox } from '@/components/ui/checkbox'
import { cn } from '@/lib/utils'
import { Button } from '@/components/ui/button'
import { Skeleton } from '@/components/ui/skeleton'

// Define a simple Error type locally to avoid import issues
type ErrorType = {
  message: string;
  level: string;
  source?: string;
}

interface ValidationTableProps<T extends string> {
  data: RowData<T>[]
  fields: Fields<T>
  rowSelection: RowSelectionState
  setRowSelection: React.Dispatch<React.SetStateAction<RowSelectionState>>
  updateRow: (rowIndex: number, key: T, value: any) => void
  validationErrors: Map<number, Record<string, ErrorType[]>>
  isValidatingUpc: (rowIndex: number) => boolean
  validatingUpcRows: number[]
  filters?: { showErrorsOnly?: boolean }
  templates: Template[]
  applyTemplate: (templateId: string, rowIndexes: number[]) => void
  getTemplateDisplayText: (templateId: string | null) => string
  rowProductLines?: Record<string, any[]>
  rowSublines?: Record<string, any[]>
  isLoadingLines?: Record<string, boolean>
  isLoadingSublines?: Record<string, boolean>
  upcValidationResults: Map<number, { itemNumber: string }>
  validatingCells: Set<string>
  itemNumbers: Map<number, string>
  isLoadingTemplates?: boolean
  copyDown: (rowIndex: number, key: string, endRowIndex?: number) => void
  [key: string]: any
}

// Create a memoized wrapper for template selects to prevent unnecessary re-renders
const MemoizedTemplateSelect = React.memo(({
  templates,
  value,
  onValueChange,
  getTemplateDisplayText,
  defaultBrand,
  isLoading
}: {
  templates: Template[],
  value: string,
  onValueChange: (value: string) => void,
  getTemplateDisplayText: (value: string | null) => string,
  defaultBrand?: string,
  isLoading?: boolean
}) => {
  if (isLoading) {
    return (
      <div className="flex items-center justify-center gap-2 border border-input rounded-md px-2 py-2">
        <Skeleton className="h-4 w-full" />
      </div>
    );
  }

  return (
    <SearchableTemplateSelect
      templates={templates}
      value={value}
      onValueChange={onValueChange}
      getTemplateDisplayText={getTemplateDisplayText}
      defaultBrand={defaultBrand}
    />
  );
}, (prev, next) => {
  return (
    prev.value === next.value &&
    prev.templates === next.templates &&
    prev.defaultBrand === next.defaultBrand &&
    prev.isLoading === next.isLoading
  );
});

MemoizedTemplateSelect.displayName = 'MemoizedTemplateSelect';

// Create a memoized cell component
const MemoizedCell = React.memo(({
  field,
  value,
  onChange,
  errors,
  isValidating,
  fieldKey,
  options,
  itemNumber,
  width,
  rowIndex,
  copyDown,
  totalRows
}: {
  field: Field<string>,
  value: any,
  onChange: (value: any) => void,
  errors: ErrorType[],
  isValidating?: boolean,
  fieldKey: string,
  options?: readonly any[],
  itemNumber?: string,
  width: number,
  rowIndex: number,
  copyDown?: (endRowIndex?: number) => void,
  totalRows: number
}) => {
  return (
    <ValidationCell
      field={field}
      value={value}
      onChange={onChange}
      errors={errors}
      isValidating={isValidating}
      fieldKey={fieldKey}
      options={options}
      itemNumber={itemNumber}
      width={width}
      rowIndex={rowIndex}
      copyDown={copyDown}
      totalRows={totalRows}
    />
  );
}, (prev, next) => {
  // CRITICAL FIX: Never memoize item_number cells - always re-render them
  if (prev.fieldKey === 'item_number') {
    return false; // Never skip re-renders for item_number cells
  }

  // Optimize the memo comparison function for better performance
  // Only re-render if these essential props change
  const valueEqual = prev.value === next.value;
  const isValidatingEqual = prev.isValidating === next.isValidating;
  
  // Shallow equality check for errors array
  const errorsEqual = prev.errors === next.errors || (
    Array.isArray(prev.errors) && 
    Array.isArray(next.errors) && 
    prev.errors.length === next.errors.length && 
    prev.errors.every((err, idx) => err === next.errors[idx])
  );
  
  // Shallow equality check for options array
  const optionsEqual = prev.options === next.options || (
    Array.isArray(prev.options) && 
    Array.isArray(next.options) && 
    prev.options.length === next.options.length && 
    prev.options.every((opt, idx) => opt === next.options?.[idx])
  );
  
  // Skip checking for props that rarely change
  return valueEqual && isValidatingEqual && errorsEqual && optionsEqual;
});

MemoizedCell.displayName = 'MemoizedCell';

const ValidationTable = <T extends string>({
  data,
  fields,
  rowSelection,
  setRowSelection,
  updateRow,
  validationErrors,
  filters,
  templates,
  applyTemplate,
  getTemplateDisplayText,
  validatingCells,
  itemNumbers,
  isLoadingTemplates = false,
  copyDown,
  rowProductLines = {},
  rowSublines = {},
  isLoadingLines = {},
  isLoadingSublines = {},
  isValidatingUpc,
  validatingUpcRows = [],
  upcValidationResults
}: ValidationTableProps<T>) => {
  const { translations } = useRsi<T>();

  // Add state for copy down selection mode
  const [isInCopyDownMode, setIsInCopyDownMode] = useState(false);
  const [sourceRowIndex, setSourceRowIndex] = useState<number | null>(null);
  const [sourceFieldKey, setSourceFieldKey] = useState<string | null>(null);
  const [targetRowIndex, setTargetRowIndex] = useState<number | null>(null);

  // Handle copy down completion
  const handleCopyDownComplete = useCallback((sourceRowIndex: number, fieldKey: string, targetRowIndex: number) => {
    // Call the copyDown function with the source row index, field key, and target row index
    copyDown(sourceRowIndex, fieldKey, targetRowIndex);
    
    // Reset the copy down selection mode
    setIsInCopyDownMode(false);
    setSourceRowIndex(null);
    setSourceFieldKey(null);
    setTargetRowIndex(null);
  }, [copyDown]);

  // Create copy down context value
  const copyDownContextValue = useMemo(() => ({
    isInCopyDownMode,
    sourceRowIndex,
    sourceFieldKey,
    targetRowIndex,
    setIsInCopyDownMode,
    setSourceRowIndex,
    setSourceFieldKey,
    setTargetRowIndex,
    handleCopyDownComplete
  }), [
    isInCopyDownMode,
    sourceRowIndex,
    sourceFieldKey,
    targetRowIndex,
    handleCopyDownComplete
  ]);

  // Update targetRowIndex when hovering over rows in copy down mode
  const handleRowMouseEnter = useCallback((rowIndex: number) => {
    if (isInCopyDownMode && sourceRowIndex !== null && rowIndex > sourceRowIndex) {
      setTargetRowIndex(rowIndex);
    }
  }, [isInCopyDownMode, sourceRowIndex]);

  // Memoize the selection column with stable callback
  const handleSelectAll = useCallback((value: boolean, table: any) => {
    table.toggleAllPageRowsSelected(!!value);
  }, []);

  const handleRowSelect = useCallback((value: boolean, row: any) => {
    row.toggleSelected(!!value);
  }, []);

  const selectionColumn = useMemo((): ColumnDef<RowData<T>, any> => ({
    id: 'select',
    header: ({ table }) => (
      <div className="flex h-full items-center justify-center py-2">
        <Checkbox
          checked={table.getIsAllPageRowsSelected()}
          onCheckedChange={(value) => handleSelectAll(!!value, table)}
          aria-label="Select all"
        />
      </div>
    ),
    cell: ({ row }) => (
      <div className="flex items-center justify-center py-9">
        <Checkbox
          checked={row.getIsSelected()}
          onCheckedChange={(value) => handleRowSelect(!!value, row)}
          aria-label="Select row"
        />
      </div>
    ),
    enableSorting: false,
    enableHiding: false,
    size: 50,
  }), [handleSelectAll, handleRowSelect]);

  // Memoize template selection handler
  const handleTemplateChange = useCallback((value: string, rowIndex: number) => {
    applyTemplate(value, [rowIndex]);
  }, [applyTemplate]);

  // Memoize the template column with stable callback
  const templateColumn = useMemo((): ColumnDef<RowData<T>, any> => ({
    accessorKey: '__template',
    header: 'Template',
    size: 200,
    cell: ({ row }) => {
      const templateValue = row.original.__template || null;
      const defaultBrand = row.original.company || undefined;
      const rowIndex = data.findIndex(r => r === row.original);
      
      return (
        <TableCell className="p-1" style={{ width: '200px', minWidth: '200px', maxWidth: '200px', overflow: 'hidden' }}>
          <div className="w-full overflow-hidden">
            <MemoizedTemplateSelect
              templates={templates}
              value={templateValue || ''}
              onValueChange={(value) => handleTemplateChange(value, rowIndex)}
              getTemplateDisplayText={getTemplateDisplayText}
              defaultBrand={defaultBrand}
              isLoading={isLoadingTemplates}
            />
          </div>
        </TableCell>
      );
    }
  }), [templates, handleTemplateChange, getTemplateDisplayText, isLoadingTemplates, data]);

  // Cache options by field key to avoid recreating arrays
  const optionsCache = useMemo(() => {
    const cache = new Map<string, readonly any[]>();
    
    fields.forEach((field) => {
      // Get the field key
      const fieldKey = String(field.key);
      
      // Handle all select and multi-select fields the same way
      if (field.fieldType && 
          (typeof field.fieldType === 'object') && 
          (field.fieldType.type === 'select' || field.fieldType.type === 'multi-select')) {
        cache.set(fieldKey, (field.fieldType as any).options || []);
      }
    });
    
    return cache;
  }, [fields]);

  // Memoize the field update handler
  const handleFieldUpdate = useCallback((rowIndex: number, fieldKey: T, value: any) => {
    updateRow(rowIndex, fieldKey, value);
  }, [updateRow]);

  // Memoize the copyDown handler
  const handleCopyDown = useCallback((rowIndex: number, fieldKey: string, endRowIndex?: number) => {
    copyDown(rowIndex, fieldKey, endRowIndex);
  }, [copyDown]);

  // Use validatingUpcRows for calculation
  const isRowValidatingUpc = useCallback((rowIndex: number) => {
    return isValidatingUpc?.(rowIndex) || validatingUpcRows.includes(rowIndex);
  }, [isValidatingUpc, validatingUpcRows]);

  // Use upcValidationResults for display, prioritizing the most recent values
  const getRowUpcResult = useCallback((rowIndex: number) => {
    // ALWAYS get from the data array directly - most authoritative source
    const rowData = data[rowIndex];
    if (rowData && rowData.item_number) {
      return rowData.item_number;
    }
    
    // Maps are only backup sources when data doesn't have a value
    const itemNumberFromMap = itemNumbers.get(rowIndex);
    if (itemNumberFromMap) {
      return itemNumberFromMap;
    }
    
    // Last resort - upcValidationResults
    const upcResult = upcValidationResults?.get(rowIndex)?.itemNumber;
    if (upcResult) {
      return upcResult;
    }
    
    return undefined;
  }, [data, itemNumbers, upcValidationResults]);

  // Memoize field columns with stable handlers
  const fieldColumns = useMemo(() => fields.map((field): ColumnDef<RowData<T>, any> | null => {
    // Don't filter out disabled fields, just pass the disabled state to the cell component
    
    const fieldWidth = field.width || (
      field.fieldType.type === "checkbox" ? 80 :
      field.fieldType.type === "select" ? 150 : 
      field.fieldType.type === "multi-select" ? 200 :
      (field.fieldType.type === "input" || field.fieldType.type === "multi-input") && 
      (field.fieldType as any).multiline ? 300 : 
      150
    );

    const fieldKey = String(field.key);
    // Get cached options for this field
    const fieldOptions = optionsCache.get(fieldKey) || [];

    return {
      accessorKey: fieldKey,
      header: field.label || fieldKey,
      size: fieldWidth,
      cell: ({ row }) => {
        // Get row-specific options for line and subline fields
        let options = fieldOptions;
        const rowId = row.original.__index;
        
        if (fieldKey === 'line' && rowId && rowProductLines[rowId]) {
          options = rowProductLines[rowId];
        } else if (fieldKey === 'subline' && rowId && rowSublines[rowId]) {
          options = rowSublines[rowId];
        }
        
        // Determine if this cell is in loading state - use a clear consistent approach
        let isLoading = false;
        
        // Check the validatingCells Set first (for item_number and other fields)
        const cellLoadingKey = `${row.index}-${fieldKey}`;
        if (validatingCells.has(cellLoadingKey)) {
          isLoading = true;
        }
        // Check if UPC is validating for this row and field is item_number
        else if (fieldKey === 'item_number' && isRowValidatingUpc(row.index)) {
          isLoading = true;
        }
        // Add loading state for line/subline fields
        else if (fieldKey === 'line' && rowId && isLoadingLines[rowId]) {
          isLoading = true;
        } 
        else if (fieldKey === 'subline' && rowId && isLoadingSublines[rowId]) {
          isLoading = true;
        }
        
        // Get validation errors for this cell
        const cellErrors = validationErrors.get(row.index)?.[fieldKey] || [];
        
        // Create a copy of the field with guaranteed field type for line and subline fields
        let fieldWithType = field;
        
        // Ensure line and subline fields always have the correct fieldType
        if (fieldKey === 'line' || fieldKey === 'subline') {
          // Create a deep clone of the field to prevent any reference issues
          fieldWithType = {
            ...JSON.parse(JSON.stringify(field)), // Ensure deep clone
            fieldType: {
              type: 'select',
              options: options
            },
            // Explicitly mark as not disabled to ensure dropdown works
            disabled: false
          };
          
        }
        
        // CRITICAL CHANGE: Get item number directly from row data first for item_number fields
        let itemNumber;
        if (fieldKey === 'item_number') {
          // Check directly in row data first - this is the most accurate source
          const directValue = row.original[fieldKey];
          if (directValue) {
            itemNumber = directValue;
          } else {
            // Fall back to centralized getter that checks all sources
            itemNumber = getRowUpcResult(row.index);
          }
        }
        
        // CRITICAL: For item_number fields, create a unique key that includes the itemNumber value
        // This forces a complete re-render when the itemNumber changes
        const cellKey = fieldKey === 'item_number' 
          ? `cell-${row.index}-${fieldKey}-${itemNumber || 'empty'}-${Date.now()}` // Force re-render on every render cycle for item_number
          : `cell-${row.index}-${fieldKey}`;
        
        return (
          <MemoizedCell
            key={cellKey} // CRITICAL: Add key to force re-render when itemNumber changes
            field={fieldWithType as Field<string>}
            value={fieldKey === 'item_number' && row.original[field.key] 
                   ? row.original[field.key] // Use direct value from row data 
                   : row.original[field.key as keyof typeof row.original]}
            onChange={(value) => handleFieldUpdate(row.index, field.key as T, value)}
            errors={cellErrors}
            isValidating={isLoading}
            fieldKey={fieldKey}
            options={options}
            itemNumber={itemNumber}
            width={fieldWidth}
            rowIndex={row.index}
            copyDown={(endRowIndex?: number) => handleCopyDown(row.index, field.key as string, endRowIndex)}
            totalRows={data.length}
          />
        );
      }
    };
  }).filter((col): col is ColumnDef<RowData<T>, any> => col !== null), 
  [fields, validationErrors, validatingCells, itemNumbers, handleFieldUpdate, handleCopyDown, optionsCache, 
   data.length, rowProductLines, rowSublines, isLoadingLines, isLoadingSublines, 
   isRowValidatingUpc, getRowUpcResult]);

  // Combine columns
  const columns = useMemo(() => [selectionColumn, templateColumn, ...fieldColumns], [selectionColumn, templateColumn, fieldColumns]);

  const table = useReactTable({
    data,
    columns,
    state: {
      rowSelection,
    },
    enableRowSelection: true,
    onRowSelectionChange: setRowSelection,
    getCoreRowModel: getCoreRowModel(),
    getRowId: useCallback((_row: RowData<T>, index: number) => String(index), []),
  });

  // Calculate total table width for stable horizontal scrolling
  const totalWidth = useMemo(() => {
    return columns.reduce((total, col) => total + (col.size || 0), 0);
  }, [columns]);

  // Don't render if no data
  if (data.length === 0) {
    return (
      <div className="flex items-center justify-center h-full">
        <p className="text-muted-foreground">
          {filters?.showErrorsOnly 
            ? translations.validationStep.noRowsMessageWhenFiltered || "No rows with errors"
            : translations.validationStep.noRowsMessage || "No data to display"}
        </p>
      </div>
    );
  }

  return (
    <CopyDownContext.Provider value={copyDownContextValue}>
      <div className="min-w-max relative">
        {/* Add global styles for copy down mode */}
        {isInCopyDownMode && (
          <style>
            {`
              .copy-down-target-row,
              .copy-down-target-row *,
              .copy-down-target-row input,
              .copy-down-target-row textarea,
              .copy-down-target-row div,
              .copy-down-target-row button,
              .target-row-cell,
              .target-row-cell * {
                cursor: pointer !important;
              }
            `}
          </style>
        )}
        {isInCopyDownMode && sourceRowIndex !== null && sourceFieldKey !== null && (
          <div className="sticky top-0 z-30 h-0 overflow-visible">
            <div 
              className="absolute w-[240px] top-16 bg-blue-50 border rounded-2xl shadow-lg border-blue-200 p-3 text-sm text-blue-700 flex items-center justify-between"
              style={{
                left: (() => {
                  // Find the column index
                  const colIndex = columns.findIndex(col => 
                    'accessorKey' in col && col.accessorKey === sourceFieldKey
                  );
                  
                  // If column not found, position at a default location
                  if (colIndex === -1) return '50px';
                  
                  // Calculate position based on column widths
                  let position = 0;
                  for (let i = 0; i < colIndex; i++) {
                    position += columns[i].size || 0;
                  }
                  
                  // Add half of the current column width to center it
                  position += (columns[colIndex].size || 0) / 2;
                  
                  // Adjust to center the notification
                  position -= 120; // Half of the notification width
                  
                  return `${Math.max(50, position)}px`;
                })()
              }}
            >
              <div>
                <span className="font-medium">Click on the last row you want to copy to</span>
              </div>
              <Button 
                variant="outline" 
                size="sm" 
                onClick={() => setIsInCopyDownMode(false)}
                className="text-xs h-7 border-blue-200 text-blue-700 hover:bg-blue-100"
              >
                Cancel
              </Button>
            </div>
          </div>
        )}
        <div className="relative">
          {/* Custom Table Header - Always Visible with GPU acceleration */}
          <div 
            className="sticky top-0 z-20 bg-muted border-b shadow-sm will-change-transform"
            style={{ 
              width: `${totalWidth}px`,
              transform: 'translateZ(0)', // Force GPU acceleration
            }}
          >
            <div className="flex">
              {table.getFlatHeaders().map((header) => {
                const width = header.getSize();
                return (
                  <div
                    key={header.id}
                    className="py-2 px-2 font-bold text-sm text-muted-foreground bg-muted flex items-center justify-center"
                    style={{ 
                      width: `${width}px`,
                      minWidth: `${width}px`,
                      maxWidth: `${width}px`,
                      boxSizing: 'border-box',
                      height: '40px'
                    }}
                  >
                    {flexRender(header.column.columnDef.header, header.getContext())}
                  </div>
                );
              })}
            </div>
          </div>

          {/* Table Body - With optimized rendering */}
          <Table style={{ 
            width: `${totalWidth}px`, 
            tableLayout: 'fixed', 
            borderCollapse: 'separate', 
            borderSpacing: 0, 
            marginTop: '-1px',
            willChange: 'transform', // Help browser optimize
            contain: 'content', // Contain paint operations
            transform: 'translateZ(0)' // Force GPU acceleration
          }}>
            <TableBody>
              {table.getRowModel().rows.map((row) => {
                // Precompute validation error status for this row
                const hasErrors = validationErrors.has(parseInt(row.id)) && 
                                Object.keys(validationErrors.get(parseInt(row.id)) || {}).length > 0;
                                    
                // Precompute copy down target status
                const isCopyDownTarget = isInCopyDownMode && 
                                       sourceRowIndex !== null && 
                                       parseInt(row.id) > sourceRowIndex;
                                    
                // Using CSS variables for better performance on hover/state changes
                const rowStyle = {
                  cursor: isCopyDownTarget ? 'pointer' : undefined,
                  position: 'relative' as const,
                  willChange: isInCopyDownMode ? 'background-color' : 'auto',
                  contain: 'layout',
                  transition: 'background-color 100ms ease-in-out'
                };
                                    
                return (
                  <TableRow
                    key={row.id}
                    className={cn(
                      "hover:bg-muted/50",
                      row.getIsSelected() ? "!bg-blue-50/50" : "",
                      hasErrors ? "bg-red-50/40" : "",
                      isCopyDownTarget ? "cursor-pointer copy-down-target-row" : ""
                    )}
                    style={rowStyle}
                    onMouseEnter={() => handleRowMouseEnter(parseInt(row.id))}
                  >
                    {row.getVisibleCells().map((cell: any) => (
                      <React.Fragment key={cell.id}>
                        {flexRender(cell.column.columnDef.cell, cell.getContext())}
                      </React.Fragment>
                    ))}
                  </TableRow>
                );
              })}
            </TableBody>
          </Table>
        </div>
      </div>
    </CopyDownContext.Provider>
  );
};

// Optimize memo comparison with more efficient checks
const areEqual = (prev: ValidationTableProps<any>, next: ValidationTableProps<any>) => {
  // Check reference equality for simple props first
  if (prev.fields !== next.fields) return false;
  if (prev.templates !== next.templates) return false;
  if (prev.isLoadingTemplates !== next.isLoadingTemplates) return false;
  if (prev.filters?.showErrorsOnly !== next.filters?.showErrorsOnly) return false;
  
  // Fast path: data length change always means re-render
  if (prev.data.length !== next.data.length) return false;
  
  // Efficiently check row selection changes
  const prevSelectionKeys = Object.keys(prev.rowSelection);
  const nextSelectionKeys = Object.keys(next.rowSelection);
  if (prevSelectionKeys.length !== nextSelectionKeys.length) return false;
  if (!prevSelectionKeys.every(key => prev.rowSelection[key] === next.rowSelection[key])) return false;

  // Use size for Map comparisons instead of deeper checks
  if (prev.validationErrors.size !== next.validationErrors.size) return false;
  if (prev.validatingCells.size !== next.validatingCells.size) return false;
  if (prev.itemNumbers.size !== next.itemNumbers.size) return false;
  
  // If values haven't changed, component doesn't need to re-render
  return true;
};

export default React.memo(ValidationTable, areEqual); 