'use client';
import React from 'react';
import {
  ColumnDef,
  ColumnPinningState,
  ColumnSizingState,
  SortingState,
  VisibilityState,
  flexRender,
  getCoreRowModel,
  useReactTable,
  getSortedRowModel,
  getPaginationRowModel,
} from '@tanstack/react-table';

import { Table, TableBody, TableCell, TableRow } from '@/components/ui/table';
import { Skeleton } from '@/components/ui/skeleton';
import type { PermissionInput } from '@/hooks/usePermissions';
import { PermissionGuard } from '@/guards';

import TopHeader from './Header';
import TableHeader from './TableHeader';
import { TableFooter } from './Footer';
import { TableActionButton } from './TableActionButton';
import { ColumnViewOptions } from './ColumnViewOptions';
import { getPinnedColumnStyles } from './columnStyles';
import { cn } from '@/lib/utils';

function shouldTruncateCell(column: {
  id: string;
  columnDef: { meta?: unknown };
}) {
  if (column.id === 'actions') return false;
  const meta = column.columnDef.meta as
    | { disableTruncate?: boolean }
    | undefined;
  return !meta?.disableTruncate;
}

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

export interface DataTableAction<TData> {
  label: string | ((item: TData) => string);
  icon: React.ReactNode;
  onClick: (item: TData) => void;
  permission?: PermissionInput;
  disabled?: (item: TData) => boolean;
  hidden?: (item: TData) => boolean;
  className?: string;
}

export interface DataTableProps<TData, TValue> {
  columns: ColumnDef<TData, TValue>[];
  data: TData[];
  title?: string;
  onAddNew?: () => void;
  addButtonText?: string;
  isLoading?: boolean;
  actions?: DataTableAction<TData>[];
  /** Sticky-pin the actions column (use with actionsAlign). */
  actionsSticky?: boolean;
  actionsAlign?: ColumnAlign;
  toolbar?: React.ReactNode;
  emptyTitle?: string;
  emptyDescription?: string;
  pagination?: {
    skip: number;
    limit: number;
    totalItems?: number;
    onPageChange: (newSkip: number) => void;
    onLimitChange: (newLimit: number) => void;
  };
  sorting?: SortingState;
  onSortingChange?: (sorting: SortingState) => void;
  enableColumnControls?: boolean;
  onRowClick?: (row: TData) => void;
  /** Minimum width (px) for every column unless overridden in the column def. */
  minColumnSize?: number;
}

export function DataTable<TData, TValue>(props: DataTableProps<TData, TValue>) {
  const { actions, ...rest } = props;

  if (!actions?.length) {
    return <DataTableContent {...props} />;
  }

  const actionPermissions = actions.map((action) => action.permission);

  return (
    <PermissionGuard
      permissions={actionPermissions}
      match="any"
      fallback={<DataTableContent {...rest} actions={undefined} />}
    >
      <DataTableContent {...props} />
    </PermissionGuard>
  );
}

function DataTableContent<TData, TValue>({
  columns: initialColumns,
  data,
  title,
  onAddNew,
  addButtonText,
  isLoading = false,
  actions,
  actionsSticky = false,
  actionsAlign = 'right',
  toolbar,
  emptyTitle = 'No results found.',
  emptyDescription = 'Try adjusting your filters or search terms.',
  pagination,
  onRowClick,
  sorting: controlledSorting,
  onSortingChange,
  enableColumnControls = true,
  minColumnSize = 120,
}: DataTableProps<TData, TValue>) {
  const [rowSelection, setRowSelection] = React.useState({});
  const [columnVisibility, setColumnVisibility] =
    React.useState<VisibilityState>({});
  const [columnSizing, setColumnSizing] = React.useState<ColumnSizingState>({});
  const [internalSorting, setInternalSorting] = React.useState<SortingState>(
    [],
  );
  const sorting = controlledSorting ?? internalSorting;
  const isManualSorting = Boolean(onSortingChange);
  const handleSortingChange = React.useCallback(
    (updater: SortingState | ((old: SortingState) => SortingState)) => {
      const nextSorting =
        typeof updater === 'function' ? updater(sorting) : updater;
      if (onSortingChange) {
        onSortingChange(nextSorting);
        return;
      }
      setInternalSorting(nextSorting);
    },
    [onSortingChange, sorting],
  );

  const columns = React.useMemo(() => {
    const cols: ColumnDef<TData, TValue>[] = [...initialColumns];

    if (actions?.length) {
      cols.push({
        id: 'actions',
        size: 110,
        minSize: 90,
        enableHiding: false,
        enableSorting: false,
        enablePinning: false,
        header: () => <div className="text-right">Actions</div>,
        cell: ({ row }) => {
          const item = row.original;
          return (
            <div className="flex justify-start gap-2">
              {actions.map((action) => {
                if (action.hidden?.(item)) return null;

                const label =
                  typeof action.label === 'function'
                    ? action.label(item)
                    : action.label;

                return (
                  <TableActionButton
                    key={label}
                    label={label}
                    permission={action.permission}
                    className={action.className}
                    disabled={action.disabled?.(item)}
                    onClick={(event) => {
                      event.stopPropagation();
                      action.onClick(item);
                    }}
                  >
                    {action.icon}
                  </TableActionButton>
                );
              })}
            </div>
          );
        },
      });
    }
    return cols;
  }, [actions, initialColumns]);

  const columnPinning = React.useMemo<ColumnPinningState>(
    () =>
      actionsSticky && actions?.length ? { [actionsAlign]: ['actions'] } : {},
    [actions, actionsAlign, actionsSticky],
  );

  const table = useReactTable({
    data,
    columns,
    getCoreRowModel: getCoreRowModel(),
    onRowSelectionChange: setRowSelection,
    onColumnVisibilityChange: setColumnVisibility,
    onColumnSizingChange: setColumnSizing,
    onSortingChange: handleSortingChange,
    getSortedRowModel: getSortedRowModel(),
    getPaginationRowModel: getPaginationRowModel(),
    enableColumnResizing: true,
    enableColumnPinning: true,
    columnResizeMode: 'onChange',
    defaultColumn: {
      minSize: minColumnSize,
      size: minColumnSize,
    },
    manualPagination: !!pagination,
    manualSorting: isManualSorting,
    pageCount: pagination
      ? Math.ceil((pagination.totalItems ?? data.length) / pagination.limit)
      : undefined,
    state: {
      rowSelection,
      columnVisibility,
      columnPinning,
      columnSizing,
      sorting,
      ...(pagination
        ? {
            pagination: {
              pageIndex: Math.floor(pagination.skip / pagination.limit),
              pageSize: pagination.limit,
            },
          }
        : {}),
    },
    onPaginationChange: pagination
      ? (updater) => {
          const currentState = {
            pageIndex: Math.floor(pagination.skip / pagination.limit),
            pageSize: pagination.limit,
          };
          const newState =
            typeof updater === 'function' ? updater(currentState) : updater;
          const pageSizeChanged = newState.pageSize !== pagination.limit;

          if (pageSizeChanged) {
            pagination.onLimitChange(newState.pageSize);
            pagination.onPageChange(0);
            return;
          }

          pagination.onPageChange(newState.pageIndex * newState.pageSize);
        }
      : undefined,
  });

  return (
    <>
      <section className="space-y-3">
        <TopHeader
          title={onAddNew ? title : undefined}
          itemCount={pagination?.totalItems ?? data.length}
          onAddNew={onAddNew}
          addButtonText={addButtonText}
          toolbar={toolbar}
          tableTools={
            enableColumnControls ? <ColumnViewOptions table={table} /> : null
          }
        />

        <div className="overflow-hidden rounded-lg border bg-background">
          <Table
            containerClassName="max-h-[calc(100vh-350px)] overflow-auto"
            className="table-fixed border-separate border-spacing-0"
            style={{ width: '100%', minWidth: table.getTotalSize() }}
          >
            <TableHeader table={table} />
            <TableBody>
              {isLoading ? (
                Array.from({ length: 5 }).map((_, idx) => (
                  <TableRow key={idx}>
                    {columns.map((_, colIdx) => (
                      <TableCell
                        key={colIdx}
                        className="h-13 border-b border-border px-4 py-3"
                      >
                        <Skeleton className="h-4 w-full max-w-35" />
                      </TableCell>
                    ))}
                  </TableRow>
                ))
              ) : table.getRowModel().rows?.length ? (
                table.getRowModel().rows.map((row) => (
                  <TableRow
                    key={row.id}
                    data-state={row.getIsSelected() && 'selected'}
                  >
                    {row.getVisibleCells().map((cell) => {
                      const truncate = shouldTruncateCell(cell.column);
                      const pinned = cell.column.getIsPinned();
                      const isActionsCell = cell.column.id === 'actions';
                      const isRowClickable = Boolean(
                        onRowClick && !isActionsCell,
                      );
                      const content = flexRender(
                        cell.column.columnDef.cell,
                        cell.getContext(),
                      );

                      return (
                        <TableCell
                          key={cell.id}
                          style={getPinnedColumnStyles(cell.column)}
                          onClick={
                            isRowClickable
                              ? () => onRowClick?.(row.original)
                              : undefined
                          }
                          className={cn(
                            'h-13 max-w-0 overflow-hidden border-b border-border px-4 py-3 text-sm',
                            pinned && 'bg-background',
                            isRowClickable && 'cursor-pointer',
                            isActionsCell && 'cursor-default',
                          )}
                        >
                          {truncate ? (
                            <div className="truncate">{content}</div>
                          ) : (
                            content
                          )}
                        </TableCell>
                      );
                    })}
                  </TableRow>
                ))
              ) : (
                <TableRow>
                  <TableCell
                    colSpan={columns.length}
                    className="h-28 text-center"
                  >
                    <div className="flex flex-col items-center justify-center text-muted-foreground gap-1">
                      <p className="text-sm font-medium">{emptyTitle}</p>
                      <p className="text-xs">{emptyDescription}</p>
                    </div>
                  </TableCell>
                </TableRow>
              )}
            </TableBody>
          </Table>
        </div>
        <TableFooter
          table={table}
          totalItems={pagination?.totalItems ?? data.length}
          pageSize={pagination?.limit}
          onPageSizeChange={pagination?.onLimitChange}
        />
      </section>
    </>
  );
}
