import React, { useState, useMemo, useCallback } from 'react';
import {
  ChevronUp,
  ChevronDown,
  ChevronsUpDown,
  ChevronLeft,
  ChevronRight,
  ChevronsLeft,
  ChevronsRight,
  Search,
  X,
  Filter,
} from 'lucide-react';
import { cn } from '../utils/cn';
import { SkeletonTable } from './Skeleton';

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

export type SortDirection = 'asc' | 'desc' | null;

export type ColumnAlign = 'left' | 'center' | 'right';

export interface ColumnDef<T> {
  /** Unique column identifier */
  id: string;
  /** Column header label */
  header: string;
  /** Data accessor key or function */
  accessorKey?: keyof T;
  accessorFn?: (row: T) => unknown;
  /** Custom cell renderer */
  cell?: (value: unknown, row: T, rowIndex: number) => React.ReactNode;
  /** Column alignment */
  align?: ColumnAlign;
  /** Whether column is sortable */
  sortable?: boolean;
  /** Whether column is filterable */
  filterable?: boolean;
  /** Column width */
  width?: string | number;
  /** Minimum column width */
  minWidth?: string | number;
  /** Whether to hide on mobile */
  hideOnMobile?: boolean;
  /** Custom sort function */
  sortFn?: (a: T, b: T, direction: SortDirection) => number;
  /** Custom filter function */
  filterFn?: (row: T, filterValue: string) => boolean;
}

export interface PaginationConfig {
  /** Current page (1-indexed) */
  page: number;
  /** Items per page */
  pageSize: number;
  /** Total number of items (for server-side pagination) */
  totalItems?: number;
  /** Available page sizes */
  pageSizeOptions?: number[];
  /** Callback when page changes */
  onPageChange?: (page: number) => void;
  /** Callback when page size changes */
  onPageSizeChange?: (pageSize: number) => void;
}

export interface DataTableProps<T extends Record<string, unknown>> {
  /** Column definitions */
  columns: ColumnDef<T>[];
  /** Table data */
  data: T[];
  /** Row key extractor */
  getRowId?: (row: T, index: number) => string;
  /** Pagination configuration */
  pagination?: PaginationConfig;
  /** Enable global search */
  enableSearch?: boolean;
  /** Search placeholder */
  searchPlaceholder?: string;
  /** Enable column filters */
  enableFilters?: boolean;
  /** Default sort column */
  defaultSortColumn?: string;
  /** Default sort direction */
  defaultSortDirection?: SortDirection;
  /** Loading state */
  isLoading?: boolean;
  /** Empty state message */
  emptyMessage?: string;
  /** Table title */
  title?: string;
  /** Table subtitle */
  subtitle?: string;
  /** Row click handler */
  onRowClick?: (row: T, index: number) => void;
  /** Selected rows (controlled) */
  selectedRows?: Set<string>;
  /** Row selection handler */
  onRowSelect?: (rowId: string, selected: boolean) => void;
  /** Enable row selection */
  enableRowSelection?: boolean;
  /** Striped rows */
  striped?: boolean;
  /** Hover effect on rows */
  hoverable?: boolean;
  /** Compact mode */
  compact?: boolean;
  /** Additional CSS classes */
  className?: string;
}

// ============================================================================
// Utility Functions
// ============================================================================

function getCellValue<T>(row: T, column: ColumnDef<T>): unknown {
  if (column.accessorFn) {
    return column.accessorFn(row);
  }
  if (column.accessorKey) {
    return row[column.accessorKey];
  }
  return null;
}

function defaultSort<T>(
  a: T,
  b: T,
  column: ColumnDef<T>,
  direction: SortDirection
): number {
  if (!direction) return 0;

  const aVal = getCellValue(a, column);
  const bVal = getCellValue(b, column);

  let comparison = 0;

  if (aVal === null || aVal === undefined) comparison = 1;
  else if (bVal === null || bVal === undefined) comparison = -1;
  else if (typeof aVal === 'number' && typeof bVal === 'number') {
    comparison = aVal - bVal;
  } else if (typeof aVal === 'string' && typeof bVal === 'string') {
    comparison = aVal.localeCompare(bVal, 'es-MX');
  } else if (aVal instanceof Date && bVal instanceof Date) {
    comparison = aVal.getTime() - bVal.getTime();
  } else {
    comparison = String(aVal).localeCompare(String(bVal), 'es-MX');
  }

  return direction === 'asc' ? comparison : -comparison;
}

function defaultFilter<T>(row: T, column: ColumnDef<T>, filterValue: string): boolean {
  const value = getCellValue(row, column);
  if (value === null || value === undefined) return false;
  return String(value).toLowerCase().includes(filterValue.toLowerCase());
}

// ============================================================================
// Sub-Components
// ============================================================================

interface SortIconProps {
  direction: SortDirection;
}

function SortIcon({ direction }: SortIconProps): React.ReactElement {
  if (direction === 'asc') {
    return <ChevronUp size={14} className="text-blue-500" />;
  }
  if (direction === 'desc') {
    return <ChevronDown size={14} className="text-blue-500" />;
  }
  return <ChevronsUpDown size={14} className="text-gray-400" />;
}

interface PaginationProps {
  currentPage: number;
  pageSize: number;
  totalItems: number;
  pageSizeOptions: number[];
  onPageChange: (page: number) => void;
  onPageSizeChange: (pageSize: number) => void;
}

function Pagination({
  currentPage,
  pageSize,
  totalItems,
  pageSizeOptions,
  onPageChange,
  onPageSizeChange,
}: PaginationProps): React.ReactElement {
  const totalPages = Math.ceil(totalItems / pageSize);
  const startItem = (currentPage - 1) * pageSize + 1;
  const endItem = Math.min(currentPage * pageSize, totalItems);

  const canGoPrev = currentPage > 1;
  const canGoNext = currentPage < totalPages;

  return (
    <div className="flex flex-col sm:flex-row items-center justify-between gap-4 px-4 py-3 border-t border-gray-200 dark:border-gray-700">
      {/* Page size selector */}
      <div className="flex items-center gap-2 text-sm text-gray-600 dark:text-gray-400">
        <span>Mostrar</span>
        <select
          value={pageSize}
          onChange={(e) => onPageSizeChange(Number(e.target.value))}
          className="rounded border border-gray-300 bg-white px-2 py-1 text-sm dark:border-gray-600 dark:bg-gray-700 dark:text-white"
        >
          {pageSizeOptions.map((size) => (
            <option key={size} value={size}>
              {size}
            </option>
          ))}
        </select>
        <span>por pagina</span>
      </div>

      {/* Info and controls */}
      <div className="flex items-center gap-4">
        <span className="text-sm text-gray-600 dark:text-gray-400">
          {startItem}-{endItem} de {totalItems}
        </span>

        <div className="flex items-center gap-1">
          <button
            onClick={() => onPageChange(1)}
            disabled={!canGoPrev}
            className={cn(
              'p-1 rounded hover:bg-gray-100 dark:hover:bg-gray-700',
              !canGoPrev && 'opacity-50 cursor-not-allowed'
            )}
            aria-label="Primera pagina"
          >
            <ChevronsLeft size={18} />
          </button>
          <button
            onClick={() => onPageChange(currentPage - 1)}
            disabled={!canGoPrev}
            className={cn(
              'p-1 rounded hover:bg-gray-100 dark:hover:bg-gray-700',
              !canGoPrev && 'opacity-50 cursor-not-allowed'
            )}
            aria-label="Pagina anterior"
          >
            <ChevronLeft size={18} />
          </button>

          <span className="px-3 text-sm font-medium text-gray-700 dark:text-gray-300">
            {currentPage} / {totalPages}
          </span>

          <button
            onClick={() => onPageChange(currentPage + 1)}
            disabled={!canGoNext}
            className={cn(
              'p-1 rounded hover:bg-gray-100 dark:hover:bg-gray-700',
              !canGoNext && 'opacity-50 cursor-not-allowed'
            )}
            aria-label="Pagina siguiente"
          >
            <ChevronRight size={18} />
          </button>
          <button
            onClick={() => onPageChange(totalPages)}
            disabled={!canGoNext}
            className={cn(
              'p-1 rounded hover:bg-gray-100 dark:hover:bg-gray-700',
              !canGoNext && 'opacity-50 cursor-not-allowed'
            )}
            aria-label="Ultima pagina"
          >
            <ChevronsRight size={18} />
          </button>
        </div>
      </div>
    </div>
  );
}

// ============================================================================
// Main Component
// ============================================================================

export function DataTable<T extends Record<string, unknown>>({
  columns,
  data,
  getRowId,
  pagination,
  enableSearch = false,
  searchPlaceholder = 'Buscar...',
  enableFilters = false,
  defaultSortColumn,
  defaultSortDirection = null,
  isLoading = false,
  emptyMessage = 'No hay datos disponibles',
  title,
  subtitle,
  onRowClick,
  selectedRows,
  onRowSelect,
  enableRowSelection = false,
  striped = false,
  hoverable = true,
  compact = false,
  className,
}: DataTableProps<T>): React.ReactElement {
  // State
  const [searchQuery, setSearchQuery] = useState('');
  const [sortColumn, setSortColumn] = useState<string | null>(defaultSortColumn ?? null);
  const [sortDirection, setSortDirection] = useState<SortDirection>(defaultSortDirection);
  const [columnFilters, setColumnFilters] = useState<Record<string, string>>({});
  const [showFilters, setShowFilters] = useState(false);

  // Internal pagination state (for client-side pagination)
  const [internalPage, setInternalPage] = useState(pagination?.page ?? 1);
  const [internalPageSize, setInternalPageSize] = useState(pagination?.pageSize ?? 10);

  // Effective pagination values
  const currentPage = pagination?.page ?? internalPage;
  const pageSize = pagination?.pageSize ?? internalPageSize;
  const pageSizeOptions = pagination?.pageSizeOptions ?? [10, 25, 50, 100];

  // Row ID helper
  const getRowIdFn = useCallback(
    (row: T, index: number): string => {
      if (getRowId) return getRowId(row, index);
      if ('id' in row) return String(row.id);
      return String(index);
    },
    [getRowId]
  );

  // Filter and sort data
  const processedData = useMemo(() => {
    let result = [...data];

    // Apply global search
    if (searchQuery) {
      const query = searchQuery.toLowerCase();
      result = result.filter((row) =>
        columns.some((col) => {
          const value = getCellValue(row, col);
          return value !== null && String(value).toLowerCase().includes(query);
        })
      );
    }

    // Apply column filters
    if (Object.keys(columnFilters).length > 0) {
      result = result.filter((row) =>
        Object.entries(columnFilters).every(([colId, filterValue]) => {
          if (!filterValue) return true;
          const column = columns.find((c) => c.id === colId);
          if (!column) return true;
          if (column.filterFn) return column.filterFn(row, filterValue);
          return defaultFilter(row, column, filterValue);
        })
      );
    }

    // Apply sorting
    if (sortColumn && sortDirection) {
      const column = columns.find((c) => c.id === sortColumn);
      if (column) {
        result.sort((a, b) => {
          if (column.sortFn) return column.sortFn(a, b, sortDirection);
          return defaultSort(a, b, column, sortDirection);
        });
      }
    }

    return result;
  }, [data, columns, searchQuery, columnFilters, sortColumn, sortDirection]);

  // Calculate total items
  const totalItems = pagination?.totalItems ?? processedData.length;

  // Apply pagination (client-side only if not server-side)
  const paginatedData = useMemo(() => {
    if (pagination?.totalItems !== undefined) {
      // Server-side pagination - data is already paginated
      return processedData;
    }
    // Client-side pagination
    const start = (currentPage - 1) * pageSize;
    return processedData.slice(start, start + pageSize);
  }, [processedData, pagination?.totalItems, currentPage, pageSize]);

  // Handlers
  const handleSort = useCallback((columnId: string) => {
    setSortColumn((prev) => {
      if (prev !== columnId) {
        setSortDirection('asc');
        return columnId;
      }
      setSortDirection((dir) => {
        if (dir === 'asc') return 'desc';
        if (dir === 'desc') return null;
        return 'asc';
      });
      return columnId;
    });
  }, []);

  const handlePageChange = useCallback(
    (page: number) => {
      if (pagination?.onPageChange) {
        pagination.onPageChange(page);
      } else {
        setInternalPage(page);
      }
    },
    [pagination]
  );

  const handlePageSizeChange = useCallback(
    (size: number) => {
      if (pagination?.onPageSizeChange) {
        pagination.onPageSizeChange(size);
      } else {
        setInternalPageSize(size);
        setInternalPage(1);
      }
    },
    [pagination]
  );

  const handleColumnFilterChange = useCallback((columnId: string, value: string) => {
    setColumnFilters((prev) => ({
      ...prev,
      [columnId]: value,
    }));
    setInternalPage(1);
  }, []);

  const clearFilters = useCallback(() => {
    setColumnFilters({});
    setSearchQuery('');
    setInternalPage(1);
  }, []);

  const hasActiveFilters = searchQuery || Object.values(columnFilters).some(Boolean);

  if (isLoading) {
    return (
      <SkeletonTable
        rows={pageSize}
        columns={columns.length}
        className={className}
      />
    );
  }

  return (
    <div
      className={cn(
        'rounded-lg border border-gray-200 bg-white dark:border-gray-700 dark:bg-gray-800',
        className
      )}
    >
      {/* Header */}
      {(title || subtitle || enableSearch || enableFilters) && (
        <div className="border-b border-gray-200 px-4 py-3 dark:border-gray-700">
          <div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3">
            {/* Title */}
            {(title || subtitle) && (
              <div>
                {title && (
                  <h3 className="text-lg font-semibold text-gray-900 dark:text-white">
                    {title}
                  </h3>
                )}
                {subtitle && (
                  <p className="text-sm text-gray-500 dark:text-gray-400 mt-0.5">
                    {subtitle}
                  </p>
                )}
              </div>
            )}

            {/* Search and filters */}
            <div className="flex items-center gap-2">
              {enableSearch && (
                <div className="relative">
                  <Search
                    size={16}
                    className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400"
                  />
                  <input
                    type="text"
                    value={searchQuery}
                    onChange={(e) => {
                      setSearchQuery(e.target.value);
                      setInternalPage(1);
                    }}
                    placeholder={searchPlaceholder}
                    className="w-full sm:w-64 rounded-md border border-gray-300 bg-white py-2 pl-9 pr-3 text-sm placeholder:text-gray-400 focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder:text-gray-500"
                  />
                </div>
              )}

              {enableFilters && (
                <button
                  onClick={() => setShowFilters(!showFilters)}
                  className={cn(
                    'flex items-center gap-1 rounded-md border px-3 py-2 text-sm font-medium transition-colors',
                    showFilters || hasActiveFilters
                      ? 'border-blue-500 bg-blue-50 text-blue-600 dark:bg-blue-900/20 dark:text-blue-400'
                      : 'border-gray-300 text-gray-700 hover:bg-gray-50 dark:border-gray-600 dark:text-gray-300 dark:hover:bg-gray-700'
                  )}
                >
                  <Filter size={16} />
                  <span>Filtros</span>
                </button>
              )}

              {hasActiveFilters && (
                <button
                  onClick={clearFilters}
                  className="flex items-center gap-1 text-sm text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300"
                >
                  <X size={14} />
                  <span>Limpiar</span>
                </button>
              )}
            </div>
          </div>

          {/* Column filters */}
          {showFilters && enableFilters && (
            <div className="mt-3 flex flex-wrap gap-2">
              {columns
                .filter((col) => col.filterable !== false)
                .map((column) => (
                  <div key={column.id} className="flex-shrink-0">
                    <input
                      type="text"
                      value={columnFilters[column.id] || ''}
                      onChange={(e) =>
                        handleColumnFilterChange(column.id, e.target.value)
                      }
                      placeholder={column.header}
                      className="w-32 rounded border border-gray-300 bg-white px-2 py-1 text-sm placeholder:text-gray-400 focus:border-blue-500 focus:outline-none dark:border-gray-600 dark:bg-gray-700 dark:text-white"
                    />
                  </div>
                ))}
            </div>
          )}
        </div>
      )}

      {/* Table */}
      <div className="overflow-x-auto">
        <table className="w-full">
          <thead>
            <tr className="border-b border-gray-200 bg-gray-50 dark:border-gray-700 dark:bg-gray-800/50">
              {enableRowSelection && (
                <th className="w-10 px-4 py-3">
                  <input
                    type="checkbox"
                    className="rounded border-gray-300 text-blue-600 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700"
                    onChange={(e) => {
                      paginatedData.forEach((row, index) => {
                        const rowId = getRowIdFn(row, index);
                        onRowSelect?.(rowId, e.target.checked);
                      });
                    }}
                  />
                </th>
              )}
              {columns.map((column) => (
                <th
                  key={column.id}
                  className={cn(
                    'px-4 text-xs font-semibold uppercase tracking-wider text-gray-500 dark:text-gray-400',
                    compact ? 'py-2' : 'py-3',
                    column.align === 'center' && 'text-center',
                    column.align === 'right' && 'text-right',
                    column.hideOnMobile && 'hidden md:table-cell',
                    column.sortable !== false && 'cursor-pointer select-none hover:text-gray-700 dark:hover:text-gray-300'
                  )}
                  style={{
                    width: column.width,
                    minWidth: column.minWidth,
                  }}
                  onClick={() => column.sortable !== false && handleSort(column.id)}
                >
                  <div
                    className={cn(
                      'flex items-center gap-1',
                      column.align === 'center' && 'justify-center',
                      column.align === 'right' && 'justify-end'
                    )}
                  >
                    <span>{column.header}</span>
                    {column.sortable !== false && (
                      <SortIcon
                        direction={sortColumn === column.id ? sortDirection : null}
                      />
                    )}
                  </div>
                </th>
              ))}
            </tr>
          </thead>

          <tbody className="divide-y divide-gray-100 dark:divide-gray-700">
            {paginatedData.length === 0 ? (
              <tr>
                <td
                  colSpan={columns.length + (enableRowSelection ? 1 : 0)}
                  className="px-4 py-8 text-center text-gray-500 dark:text-gray-400"
                >
                  {emptyMessage}
                </td>
              </tr>
            ) : (
              paginatedData.map((row, rowIndex) => {
                const rowId = getRowIdFn(row, rowIndex);
                const isSelected = selectedRows?.has(rowId);

                return (
                  <tr
                    key={rowId}
                    className={cn(
                      'transition-colors',
                      striped && rowIndex % 2 === 1 && 'bg-gray-50 dark:bg-gray-800/30',
                      hoverable && 'hover:bg-gray-50 dark:hover:bg-gray-800/50',
                      onRowClick && 'cursor-pointer',
                      isSelected && 'bg-blue-50 dark:bg-blue-900/20'
                    )}
                    onClick={() => onRowClick?.(row, rowIndex)}
                  >
                    {enableRowSelection && (
                      <td className="w-10 px-4 py-3">
                        <input
                          type="checkbox"
                          checked={isSelected}
                          onChange={(e) => {
                            e.stopPropagation();
                            onRowSelect?.(rowId, e.target.checked);
                          }}
                          className="rounded border-gray-300 text-blue-600 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700"
                        />
                      </td>
                    )}
                    {columns.map((column) => {
                      const value = getCellValue(row, column);
                      const displayValue = column.cell
                        ? column.cell(value, row, rowIndex)
                        : value !== null && value !== undefined
                          ? String(value)
                          : '-';

                      return (
                        <td
                          key={column.id}
                          className={cn(
                            'px-4 text-sm text-gray-900 dark:text-gray-100',
                            compact ? 'py-2' : 'py-3',
                            column.align === 'center' && 'text-center',
                            column.align === 'right' && 'text-right',
                            column.hideOnMobile && 'hidden md:table-cell'
                          )}
                          style={{
                            width: column.width,
                            minWidth: column.minWidth,
                          }}
                        >
                          {displayValue}
                        </td>
                      );
                    })}
                  </tr>
                );
              })
            )}
          </tbody>
        </table>
      </div>

      {/* Pagination */}
      {pagination !== undefined && totalItems > 0 && (
        <Pagination
          currentPage={currentPage}
          pageSize={pageSize}
          totalItems={totalItems}
          pageSizeOptions={pageSizeOptions}
          onPageChange={handlePageChange}
          onPageSizeChange={handlePageSizeChange}
        />
      )}
    </div>
  );
}
