// @design-system: domain/admin/AdminTable

'use client';

import { useCallback, useMemo, useRef, useState, type DragEvent, type ReactNode } from 'react';
import { useT, useLocale } from '@/lib/i18n/react';
import { cn } from '@/lib/cn';
import { Spinner } from '@/components/ui/feedback/Spinner';
import { EmptyState } from '@/components/ui/feedback/EmptyState';
import { Table } from '@/components/ui/primitives/Table';
import { Checkbox } from '@/components/ui/primitives/Checkbox';
import { Icon } from '@/components/ui/icons/Icon/Icon';
import {
  TooltipProvider,
  Tooltip,
  TooltipTrigger,
  TooltipContent,
} from '@/components/ui/overlays/Tooltip';
import { BulkActionBar } from './BulkActionBar';
import {
  ariaSortValue,
  nextSortState,
  sortRows,
  type SortDir,
  type SortableColumnDef,
} from '@/lib/admin/table-sort';
import type { TableDensity } from '@/lib/admin/table-prefs';
import { useAdminTableLayout, type AdminTableLayoutApi } from './useAdminTableLayout';
import { VirtualList } from '@platform-modules/ui-primitives/virtual';
import { useListScrollRestoration } from '@/lib/hooks/useListScrollRestoration';
import { useBrowserPathname } from '@/lib/hooks/useHydrated';

// ─── Types ────────────────────────────────────────────────────────────────────

export interface ColumnDef<T> {
  key: string;
  label: string;
  align?: 'start' | 'center' | 'end';
  width?: string;
  render?: (row: T) => ReactNode;
  sortable?: boolean;
  sortKey?: string;
  sortType?: 'string' | 'number' | 'date';
  exportable?: boolean;
  exportValue?: (row: T) => string | number;
  /** @default true */
  hideable?: boolean;
  /** When true, this cell gets relative z-[1] so its interactive content (buttons, links) escapes a rowHref overlay. */
  interactive?: boolean;
  /** Optional tooltip shown via info icon in the column header. */
  headerTooltip?: string;
}

export interface ActionDef<T> {
  key: string;
  label: string;
  onAction: (row: T) => void;
  variant?: 'ghost' | 'danger';
  hidden?: (row: T) => boolean;
}

export interface BulkActionDef {
  key: string;
  label: string;
  onClick: () => void;
  variant?: 'ghost' | 'danger';
}

export type { AdminTableLayoutApi };

export interface AdminTableProps<T> {
  columns: ColumnDef<T>[];
  rows: (T & { id: string })[];
  rowHref?: (row: T & { id: string }) => string;
  actions?: ActionDef<T>[];
  loading?: boolean;
  className?: string;
  mode?: 'client' | 'server';
  onSortChange?: (sortBy: string | null, sortDir: SortDir | null) => void;
  sortBy?: string | null;
  sortDir?: SortDir | null;
  stickyHeader?: boolean;
  tableId?: string;
  columnControls?: boolean;
  layout?: AdminTableLayoutApi<T>;
  density?: TableDensity;
  selectable?: boolean;
  selectedIds?: string[];
  onSelectionChange?: (ids: string[]) => void;
  bulkActions?: BulkActionDef[];
  bulkSelectedLabel?: string;
  bulkClearLabel?: string;
  /** aria-label for the select-all checkbox. @default 'Select all rows' */
  selectAllLabel?: string;
  /** aria-label for each row checkbox. @default derived from display field or 1-based index */
  rowSelectLabel?: (row: T & { id: string }) => string;
  /** Override default admin_list empty state copy. */
  emptyTitle?: string;
  emptyDesc?: string;
  virtualize?: {
    estimateRowHeight: number;
    overscan?: number;
    maxHeightClassName?: string;
    scrollRestorationKey?: string;
    scrollRestorationReady?: boolean;
  };
}

const alignClass: Record<NonNullable<ColumnDef<unknown>['align']>, string> = {
  start: 'text-start',
  center: 'text-center',
  end: 'text-end',
};

const densityHeadClass: Record<TableDensity, string> = {
  comfortable: 'py-2.5',
  compact: 'py-1',
};

const densityCellClass: Record<TableDensity, string> = {
  comfortable: 'py-3 text-base',
  compact: 'py-1.5 text-sm',
};

function defaultRowSelectLabel<T>(
  row: T & { id: string },
  index: number,
  columns: ColumnDef<T>[],
): string {
  const record = row as Record<string, unknown>;
  const displayName = record.displayName;
  if (typeof displayName === 'string' && displayName.trim()) {
    return `Select row ${displayName}`;
  }
  const name = record.name;
  if (typeof name === 'string' && name.trim()) {
    return `Select row ${name}`;
  }
  const firstCol = columns[0];
  if (firstCol) {
    const raw = firstCol.render ? firstCol.render(row) : record[firstCol.key];
    if (raw != null && typeof raw !== 'object') {
      const text = String(raw).trim();
      if (text) return `Select row ${text}`;
    }
  }
  return `Select row ${index + 1}`;
}

export function AdminTable<T>({
  columns,
  rows,
  rowHref,
  actions = [],
  loading = false,
  className,
  mode = 'client',
  onSortChange,
  sortBy = null,
  sortDir = null,
  stickyHeader = true,
  tableId,
  columnControls = true,
  layout: layoutProp,
  density: densityProp,
  selectable = false,
  selectedIds = [],
  onSelectionChange,
  bulkActions = [],
  bulkSelectedLabel = '{n} selected',
  bulkClearLabel = 'Clear selection',
  selectAllLabel = 'Select all rows',
  rowSelectLabel,
  emptyTitle,
  emptyDesc,
  virtualize,
}: AdminTableProps<T>) {
  const t = useT('admin_list');
  const tCommon = useT('common');
  const { locale } = useLocale();
  const pathname = useBrowserPathname();
  const internalLayout = useAdminTableLayout(tableId ?? pathname, columns);
  const layout = columnControls && tableId ? (layoutProp ?? internalLayout) : null;
  const density = densityProp ?? layout?.density ?? 'comfortable';

  const displayColumns = layout?.visibleColumns ?? columns;
  const [dragKey, setDragKey] = useState<string | null>(null);
  const virtualRootRef = useRef<HTMLDivElement>(null);

  useListScrollRestoration({
    routeKey: virtualize?.scrollRestorationKey ?? null,
    ready: Boolean(virtualize?.scrollRestorationReady),
    getElement: () => virtualRootRef.current?.querySelector('[role="grid"]') ?? null,
  });

  const handleSortClick = useCallback(
    (columnKey: string) => {
      const next = nextSortState(sortBy, sortDir, columnKey);
      if (mode === 'server' && onSortChange) {
        onSortChange(next.sortBy, next.sortDir);
        return;
      }
      onSortChange?.(next.sortBy, next.sortDir);
    },
    [mode, onSortChange, sortBy, sortDir],
  );

  const sortedRows = useMemo(() => {
    if (mode === 'server') return rows;
    return sortRows(rows, displayColumns as SortableColumnDef[], sortBy, sortDir, locale);
  }, [mode, rows, displayColumns, sortBy, sortDir, locale]);

  const pageIds = useMemo(() => sortedRows.map((row) => row.id), [sortedRows]);
  const allPageSelected =
    selectable && pageIds.length > 0 && pageIds.every((id) => selectedIds.includes(id));
  const somePageSelected =
    selectable && pageIds.some((id) => selectedIds.includes(id)) && !allPageSelected;

  const handleSelectAll = useCallback(() => {
    if (!onSelectionChange) return;
    if (allPageSelected) {
      onSelectionChange(selectedIds.filter((id) => !pageIds.includes(id)));
      return;
    }
    onSelectionChange([...new Set([...selectedIds, ...pageIds])]);
  }, [allPageSelected, onSelectionChange, pageIds, selectedIds]);

  const handleToggleRow = useCallback(
    (rowId: string) => {
      if (!onSelectionChange) return;
      if (selectedIds.includes(rowId)) {
        onSelectionChange(selectedIds.filter((id) => id !== rowId));
        return;
      }
      onSelectionChange([...selectedIds, rowId]);
    },
    [onSelectionChange, selectedIds],
  );

  const handleClearSelection = useCallback(() => {
    onSelectionChange?.([]);
  }, [onSelectionChange]);

  const onDragStart = useCallback((key: string) => {
    setDragKey(key);
  }, []);

  const onDragOver = useCallback((e: DragEvent) => {
    e.preventDefault();
  }, []);

  const onDrop = useCallback(
    (targetKey: string) => {
      if (!dragKey || !layout) return;
      layout.reorderColumns(dragKey, targetKey);
      setDragKey(null);
    },
    [dragKey, layout],
  );

  if (loading) {
    return (
      <div className="flex items-center justify-center py-16" aria-live="polite" aria-busy="true">
        <Spinner size="lg" />
        <span className="sr-only">{t('loading')}</span>
      </div>
    );
  }

  if (rows.length === 0) {
    return (
      <EmptyState
        title={emptyTitle ?? t('empty_title')}
        description={emptyDesc ?? t('empty_desc')}
        className="py-16"
      />
    );
  }

  const hasActions = actions.length > 0;
  const hasRowHref = typeof rowHref === 'function';
  const enableDrag = Boolean(layout && columnControls);
  const showBulkBar = selectable && selectedIds.length > 0 && bulkActions.length > 0;
  const shouldVirtualize = Boolean(virtualize);

  const renderColGroup = () => (
    <colgroup>
      {selectable && <col className="w-10" />}
      {displayColumns.map((col) => (
        <col key={col.key} className={col.width} />
      ))}
      {hasActions && <col className="w-32" />}
    </colgroup>
  );

  const renderHeaderRow = () => (
    <Table.Row
      className={cn(
        'border-border-default bg-surface-app border-b',
        !shouldVirtualize && stickyHeader && 'sticky top-0 z-10',
      )}
    >
      {selectable && (
        <Table.HeadCell scope="col" className={cn('w-10 px-4', densityHeadClass[density])}>
          <Checkbox
            checked={allPageSelected ? true : somePageSelected ? 'indeterminate' : false}
            onCheckedChange={handleSelectAll}
            aria-label={selectAllLabel}
          />
        </Table.HeadCell>
      )}
      {displayColumns.map((col) => {
        const sortable = col.sortable === true;
        const ariaSort = sortable ? ariaSortValue(sortBy, sortDir, col.key) : undefined;

        return (
          <Table.HeadCell
            key={col.key}
            scope="col"
            aria-sort={ariaSort}
            onDragOver={enableDrag ? onDragOver : undefined}
            onDrop={enableDrag ? () => onDrop(col.key) : undefined}
            className={cn(
              'px-4',
              densityHeadClass[density],
              'text-text-secondary tracking-wider whitespace-nowrap uppercase',
              alignClass[col.align ?? 'start'],
              col.width,
            )}
          >
            <div
              className={cn(
                'flex items-center gap-1',
                col.align === 'end' && 'justify-end',
                col.align === 'center' && 'justify-center',
              )}
            >
              {enableDrag && (
                <span
                  draggable
                  onDragStart={() => onDragStart(col.key)}
                  className="text-text-muted shrink-0 cursor-grab active:cursor-grabbing"
                  aria-hidden
                >
                  <Icon name="GripVertical" size="xs" />
                </span>
              )}
              {sortable ? (
                <button
                  type="button"
                  className={cn(
                    'inline-flex items-center gap-0.5',
                    'hover:text-text-primary focus-visible:ring-brand-primary-500 rounded',
                    'focus-visible:ring-2 focus-visible:outline-none',
                  )}
                  onClick={() => handleSortClick(col.key)}
                  onPointerDown={(e) => e.stopPropagation()}
                  aria-label={col.label}
                >
                  <span>{col.label}</span>
                  {sortBy === col.key && sortDir === 'asc' && (
                    <Icon name="ChevronUp" size="xs" aria-hidden />
                  )}
                  {sortBy === col.key && sortDir === 'desc' && (
                    <Icon name="ChevronDown" size="xs" aria-hidden />
                  )}
                </button>
              ) : (
                <span>{col.label}</span>
              )}
              {col.headerTooltip && (
                <Tooltip>
                  <TooltipTrigger asChild>
                    <button
                      type="button"
                      aria-label={tCommon('more_info')}
                      className={cn(
                        'inline-flex items-center justify-center rounded-full',
                        'hover:text-text-muted text-neutral-400',
                        'focus-visible:outline-brand-primary-600 focus-visible:outline-2 focus-visible:outline-offset-1',
                      )}
                      onPointerDown={(e) => e.stopPropagation()}
                    >
                      <Icon name="Info" size="xs" aria-hidden />
                    </button>
                  </TooltipTrigger>
                  <TooltipContent side="top" className="max-w-xs text-wrap">
                    {col.headerTooltip}
                  </TooltipContent>
                </Tooltip>
              )}
            </div>
          </Table.HeadCell>
        );
      })}
      {hasActions && (
        <Table.HeadCell
          className={cn(
            'text-text-secondary px-4 text-end tracking-wider uppercase',
            densityHeadClass[density],
          )}
        >
          {t('actions')}
        </Table.HeadCell>
      )}
    </Table.Row>
  );

  const renderDataRow = (row: T & { id: string }, rowIndex: number) => {
    const href = hasRowHref ? rowHref(row) : undefined;
    const rowAriaLabel =
      rowSelectLabel?.(row) ?? defaultRowSelectLabel(row, rowIndex, displayColumns);

    return (
      <Table.Row
        key={row.id}
        className={cn(
          'bg-surface-default transition-colors',
          hasRowHref && 'hover:bg-surface-raised relative cursor-pointer',
        )}
        onClick={
          hasRowHref
            ? (e) => {
                if (!href) return;
                if (
                  (e.target as HTMLElement).closest(
                    'a, button, input, label, select, [role="checkbox"], [role="button"]',
                  )
                ) {
                  return;
                }
                window.location.assign(href);
              }
            : undefined
        }
      >
        {selectable && (
          <Table.Cell className={cn('relative z-[1] w-10 px-4', densityCellClass[density])}>
            <Checkbox
              checked={selectedIds.includes(row.id)}
              onCheckedChange={() => handleToggleRow(row.id)}
              aria-label={rowAriaLabel}
            />
          </Table.Cell>
        )}
        {displayColumns.map((col, colIndex) => {
          const cellContent = col.render
            ? col.render(row)
            : String((row as Record<string, unknown>)[col.key] ?? '');

          return (
            <Table.Cell
              key={col.key}
              className={cn(
                'px-4',
                densityCellClass[density],
                alignClass[col.align ?? 'start'],
                col.width,
                col.interactive && 'relative z-[1]',
              )}
            >
              {colIndex === 0 && href ? (
                <a
                  href={href}
                  className={cn(
                    'text-text-primary font-medium',
                    'focus-visible:ring-brand-primary-500 focus-visible:ring-2',
                    'focus-visible:outline-none focus-visible:ring-inset',
                  )}
                >
                  {cellContent}
                </a>
              ) : (
                cellContent
              )}
            </Table.Cell>
          );
        })}
        {hasActions && (
          <Table.Cell className={cn('relative z-[1] px-4 text-end', densityCellClass[density])}>
            <div className="flex items-center justify-end gap-1">
              {actions
                .filter((action) => !action.hidden?.(row))
                .map((action) => (
                  <button
                    key={action.key}
                    type="button"
                    onClick={() => action.onAction(row)}
                    className={cn(
                      'rounded-md px-2.5 py-1 text-xs font-medium transition-colors',
                      'focus-visible:ring-brand-primary-500 focus-visible:ring-2 focus-visible:outline-none',
                      action.variant === 'danger'
                        ? 'text-danger-600 hover:bg-danger-50'
                        : 'text-text-secondary hover:bg-surface-raised',
                    )}
                  >
                    {action.label}
                  </button>
                ))}
            </div>
          </Table.Cell>
        )}
      </Table.Row>
    );
  };

  const tableEl = (
    <TooltipProvider delayDuration={200}>
      <Table className={cn('border-border-default w-full rounded-lg border', className)}>
        {renderColGroup()}
        <Table.Head>{renderHeaderRow()}</Table.Head>
        <Table.Body>{sortedRows.map(renderDataRow)}</Table.Body>
      </Table>
    </TooltipProvider>
  );

  const virtualTableEl = (
    <TooltipProvider delayDuration={200}>
      <div
        ref={virtualRootRef}
        className={cn('border-border-default overflow-hidden rounded-lg border', className)}
      >
        <div className="w-full overflow-x-auto">
          <table className="w-full table-fixed border-collapse text-sm">
            {renderColGroup()}
            <thead className="border-border-default border-b">{renderHeaderRow()}</thead>
          </table>
        </div>
        <VirtualList<T & { id: string }>
          items={sortedRows}
          estimateSize={virtualize?.estimateRowHeight ?? 56}
          overscan={virtualize?.overscan}
          getRowKey={(row) => row.id}
          className={cn('max-h-[min(70vh,40rem)]', virtualize?.maxHeightClassName)}
          renderRow={(row, rowIndex) => (
            <div
              className={cn('bg-surface-default', rowIndex > 0 && 'border-border-default border-t')}
            >
              <table className="w-full table-fixed border-collapse text-sm">
                {renderColGroup()}
                <tbody>{renderDataRow(row, rowIndex)}</tbody>
              </table>
            </div>
          )}
        />
      </div>
    </TooltipProvider>
  );

  const content = (
    <div className="flex flex-col gap-3">
      {showBulkBar && (
        <BulkActionBar
          count={selectedIds.length}
          selectedLabel={bulkSelectedLabel}
          actions={bulkActions}
          onClear={handleClearSelection}
          clearLabel={bulkClearLabel}
        />
      )}
      {shouldVirtualize ? virtualTableEl : tableEl}
    </div>
  );

  if (shouldVirtualize) {
    return content;
  }

  if (stickyHeader) {
    return (
      <div className="max-h-[min(70vh,40rem)] overflow-auto print:max-h-none print:overflow-visible">
        {content}
      </div>
    );
  }

  return content;
}
