/**
 * DataTable — WealthX Design System
 *
 * A headless, composable data-table built on \@tanstack/react-table.
 * Composes existing WealthX shadcn components:
 *   - Table, TableHeader, TableBody, TableRow, TableHead, TableCell
 *   - Input (column filter)
 *   - Button (pagination actions, column toggle)
 *   - Checkbox (row selection)
 *   - Select (page-size selector)
 *   - Pagination (page navigation)
 *   - Skeleton (loading state)
 *   - DropdownMenu (column visibility toggle)
 *
 * All colors use design tokens — no hex codes.
 */
import { type ReactElement } from "react";
import * as React from "react";
import {
  type ColumnDef,
  type ColumnFiltersState,
  type PaginationState,
  type SortingState,
  type VisibilityState,
  type RowSelectionState,
  type Table as TanstackTable,
  flexRender,
  getCoreRowModel,
  getFilteredRowModel,
  getPaginationRowModel,
  getSortedRowModel,
  useReactTable,
} from "@tanstack/react-table";
import {
  ArrowUpDown,
  ArrowUp,
  ArrowDown,
  SlidersHorizontal,
} from "lucide-react";
import { cn } from "@/lib/utils";
import {
  Table,
  TableBody,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
} from "@/components/ui/table";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select";
import { PaginationNavButtons } from "@/components/ui/pagination";
import {
  DropdownMenu,
  DropdownMenuCheckboxItem,
  DropdownMenuContent,
  DropdownMenuGroup,
  DropdownMenuLabel,
  DropdownMenuSeparator,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Skeleton } from "@/components/ui/skeleton";

// ---------------------------------------------------------------------------
// Types
// ---------------------------------------------------------------------------

interface DataTableProps<TData, TValue> {
  /** Column definitions — pass ColumnDef[] from \@tanstack/react-table */
  columns: ColumnDef<TData, TValue>[];
  /** Row data */
  data: TData[];
  /** Show a global search/filter input above the table */
  searchKey?: string;
  /** Placeholder for the search input */
  searchPlaceholder?: string;
  /** Enable row selection with checkboxes */
  enableRowSelection?: boolean;
  /** Enable column visibility toggle dropdown */
  enableColumnVisibility?: boolean;
  /** Page size options for the page-size selector */
  pageSizeOptions?: number[];
  /** Show loading skeleton */
  loading?: boolean;
  /** Number of skeleton rows to show while loading */
  skeletonRows?: number;
  /** Callback when row selection changes */
  onRowSelectionChange?: (selection: RowSelectionState) => void;
  /** Additional className for the root wrapper */
  className?: string;
  /** Render a toolbar above the table (replaces default toolbar) */
  toolbar?: (table: TanstackTable<TData>) => React.ReactNode;
  /** Text shown when no results found */
  emptyText?: string;
  /** Enable server-side pagination (disables client-side pagination model) */
  manualPagination?: boolean;
  /** Enable server-side sorting (disables client-side sorting model) */
  manualSorting?: boolean;
  /** Enable server-side filtering (disables client-side filtering model) */
  manualFiltering?: boolean;
  /** Total page count (required for server-side pagination) */
  pageCount?: number;
  /** Total row count (alternative to pageCount for server-side pagination) */
  rowCount?: number;
  /** Controlled sorting state */
  sorting?: SortingState;
  /** Controlled pagination state */
  pagination?: PaginationState;
  /** Controlled column filters state */
  columnFilters?: ColumnFiltersState;
  /** Callback when sorting state changes */
  onSortingChange?: (sorting: SortingState) => void;
  /** Callback when pagination state changes */
  onPaginationChange?: (pagination: PaginationState) => void;
  /** Callback when column filters state changes */
  onColumnFiltersChange?: (filters: ColumnFiltersState) => void;
}

// ---------------------------------------------------------------------------
// Helper: sortable header
// ---------------------------------------------------------------------------

function DataTableColumnHeader({
  column,
  title,
  className,
}: {
  column: {
    getIsSorted: () => false | "asc" | "desc";
    toggleSorting: (desc?: boolean) => void;
    getCanSort: () => boolean;
  };
  title: string;
  className?: string;
}): ReactElement {
  if (!column.getCanSort()) {
    return <span className={className}>{title}</span>;
  }

  const sorted = column.getIsSorted();

  let sortIcon: ReactElement;
  if (sorted === "asc") {
    sortIcon = <ArrowUp className="ml-1 size-3.5" />;
  } else if (sorted === "desc") {
    sortIcon = <ArrowDown className="ml-1 size-3.5" />;
  } else {
    sortIcon = <ArrowUpDown className="ml-1 size-3.5" />;
  }

  return (
    <Button
      className={cn(
        "-ml-3 h-8 text-muted-foreground hover:text-foreground",
        className,
      )}
      onClick={() => {
        column.toggleSorting(sorted === "asc");
      }}
      size="sm"
      variant="ghost"
    >
      {title}
      {sortIcon}
    </Button>
  );
}

// ---------------------------------------------------------------------------
// Helper: selection column
// ---------------------------------------------------------------------------

function getSelectionColumn<TData>(): ColumnDef<TData> {
  return {
    id: "select",
    header: ({ table }) => (
      <Checkbox
        aria-label="Select all"
        checked={table.getIsAllPageRowsSelected()}
        indeterminate={
          table.getIsSomePageRowsSelected() && !table.getIsAllPageRowsSelected()
        }
        onCheckedChange={(checked) => {
          table.toggleAllPageRowsSelected(Boolean(checked));
        }}
      />
    ),
    cell: ({ row }) => (
      <Checkbox
        aria-label="Select row"
        checked={row.getIsSelected()}
        onCheckedChange={(checked) => {
          row.toggleSelected(Boolean(checked));
        }}
      />
    ),
    enableSorting: false,
    enableHiding: false,
  };
}

// ---------------------------------------------------------------------------
// Sub-components
// ---------------------------------------------------------------------------

function DataTableToolbar<TData>({
  table,
  searchKey,
  searchPlaceholder,
  enableColumnVisibility,
}: {
  table: TanstackTable<TData>;
  searchKey?: string;
  searchPlaceholder?: string;
  enableColumnVisibility?: boolean;
}): ReactElement {
  return (
    <div
      className="flex items-center justify-between gap-2 py-4"
      data-slot="data-table-toolbar"
    >
      <div className="flex flex-1 items-center gap-2">
        {searchKey ? (
          <Input
            className="max-w-xs"
            onChange={(e) =>
              table.getColumn(searchKey)?.setFilterValue(e.target.value)
            }
            placeholder={searchPlaceholder ?? `Filter ${searchKey}...`}
            value={table.getColumn(searchKey)?.getFilterValue() as string}
          />
        ) : null}
      </div>
      {enableColumnVisibility ? (
        <DropdownMenu>
          <DropdownMenuTrigger
            render={
              <Button
                className="ml-auto h-8 gap-1.5"
                size="sm"
                variant="outline"
              />
            }
          >
            <SlidersHorizontal className="size-3.5" />
            <span className="hidden sm:inline">Columns</span>
          </DropdownMenuTrigger>
          <DropdownMenuContent>
            <DropdownMenuGroup>
              <DropdownMenuLabel>Toggle columns</DropdownMenuLabel>
              <DropdownMenuSeparator />
              {table
                .getAllColumns()
                .filter((col) => col.getCanHide())
                .map((col) => (
                  <DropdownMenuCheckboxItem
                    checked={col.getIsVisible()}
                    className="capitalize"
                    key={col.id}
                    onCheckedChange={(value) => {
                      col.toggleVisibility(Boolean(value));
                    }}
                  >
                    {col.id}
                  </DropdownMenuCheckboxItem>
                ))}
            </DropdownMenuGroup>
          </DropdownMenuContent>
        </DropdownMenu>
      ) : null}
    </div>
  );
}

function DataTablePagination<TData>({
  table,
  pageSizeOptions = [10, 20, 30, 50],
}: {
  table: TanstackTable<TData>;
  pageSizeOptions?: number[];
}): ReactElement {
  return (
    <div
      className="flex items-center gap-4 py-4"
      data-slot="data-table-pagination"
    >
      {table.getFilteredSelectedRowModel().rows.length > 0 && (
        <span className="text-body-small text-muted-foreground">
          {table.getFilteredSelectedRowModel().rows.length} of{" "}
          {table.getFilteredRowModel().rows.length} row(s) selected.
        </span>
      )}

      <div className="ml-auto flex items-center gap-6">
        <div className="flex items-center gap-2">
          <span className="text-body-small text-muted-foreground whitespace-nowrap">
            Rows per page
          </span>
          <Select
            onValueChange={(value) => table.setPageSize(Number(value))}
            value={`${table.getState().pagination.pageSize}`}
          >
            <SelectTrigger
              aria-label="Rows per page"
              className="w-[70px]"
              size="sm"
            >
              <SelectValue
                placeholder={`${table.getState().pagination.pageSize}`}
              />
            </SelectTrigger>
            <SelectContent>
              {pageSizeOptions.map((pageSize) => (
                <SelectItem key={pageSize} value={`${pageSize}`}>
                  {pageSize}
                </SelectItem>
              ))}
            </SelectContent>
          </Select>
        </div>

        <span className="text-body-small text-muted-foreground whitespace-nowrap">
          Page {table.getState().pagination.pageIndex + 1} of{" "}
          {table.getPageCount()}
        </span>

        <PaginationNavButtons
          hasNext={table.getCanNextPage()}
          hasPrev={table.getCanPreviousPage()}
          onFirst={() => table.setPageIndex(0)}
          onLast={() => table.setPageIndex(table.getPageCount() - 1)}
          onNext={() => table.nextPage()}
          onPrev={() => table.previousPage()}
        />
      </div>
    </div>
  );
}

// ---------------------------------------------------------------------------
// Loading skeleton
// ---------------------------------------------------------------------------

function DataTableSkeleton({
  columnCount,
  rowCount = 5,
}: {
  columnCount: number;
  rowCount?: number;
}): ReactElement {
  return (
    <>
      {Array.from({ length: rowCount }).map((_, rowIdx) => (
        <TableRow key={`skeleton-row-${String(rowIdx)}`}>
          {Array.from({ length: columnCount }).map((_inner, colIdx) => (
            <TableCell
              key={`skeleton-cell-${String(rowIdx)}-${String(colIdx)}`}
            >
              <Skeleton className="h-4 w-3/4" />
            </TableCell>
          ))}
        </TableRow>
      ))}
    </>
  );
}

// ---------------------------------------------------------------------------
// Main DataTable
// ---------------------------------------------------------------------------

function DataTable<TData, TValue>({
  columns: userColumns,
  data,
  searchKey,
  searchPlaceholder,
  enableRowSelection = false,
  enableColumnVisibility = false,
  pageSizeOptions = [10, 20, 30, 50],
  loading = false,
  skeletonRows = 5,
  onRowSelectionChange,
  className,
  toolbar,
  emptyText = "No results.",
  manualPagination = false,
  manualSorting = false,
  manualFiltering = false,
  pageCount,
  rowCount,
  sorting: controlledSorting,
  pagination: controlledPagination,
  columnFilters: controlledColumnFilters,
  onSortingChange: onSortingChangeProp,
  onPaginationChange: onPaginationChangeProp,
  onColumnFiltersChange: onColumnFiltersChangeProp,
}: DataTableProps<TData, TValue>): ReactElement {
  // Internal state (used when not externally controlled)
  const [internalSorting, setInternalSorting] = React.useState<SortingState>(
    [],
  );
  const [internalColumnFilters, setInternalColumnFilters] =
    React.useState<ColumnFiltersState>([]);
  const [internalPagination, setInternalPagination] =
    React.useState<PaginationState>({
      pageIndex: 0,
      pageSize: pageSizeOptions[0] ?? 10,
    });
  const [columnVisibility, setColumnVisibility] =
    React.useState<VisibilityState>({});
  const [rowSelection, setRowSelection] = React.useState<RowSelectionState>({});

  // Resolve controlled vs uncontrolled
  const sorting = controlledSorting ?? internalSorting;
  const columnFilters = controlledColumnFilters ?? internalColumnFilters;
  const pagination = controlledPagination ?? internalPagination;

  // Prepend selection column if enabled
  const resolvedColumns = React.useMemo(() => {
    if (!enableRowSelection) return userColumns;
    return [getSelectionColumn<TData>(), ...userColumns] as ColumnDef<
      TData,
      TValue
    >[];
  }, [userColumns, enableRowSelection]);

  const table = useReactTable({
    data,
    columns: resolvedColumns,
    state: {
      sorting,
      columnFilters,
      columnVisibility,
      rowSelection,
      pagination,
    },
    onSortingChange: (updater) => {
      const next = typeof updater === "function" ? updater(sorting) : updater;
      if (controlledSorting === undefined) setInternalSorting(next);
      onSortingChangeProp?.(next);
    },
    onColumnFiltersChange: (updater) => {
      const next =
        typeof updater === "function" ? updater(columnFilters) : updater;
      if (controlledColumnFilters === undefined) setInternalColumnFilters(next);
      onColumnFiltersChangeProp?.(next);
    },
    onPaginationChange: (updater) => {
      const next =
        typeof updater === "function" ? updater(pagination) : updater;
      if (controlledPagination === undefined) setInternalPagination(next);
      onPaginationChangeProp?.(next);
    },
    onColumnVisibilityChange: setColumnVisibility,
    onRowSelectionChange: (updater) => {
      const next =
        typeof updater === "function" ? updater(rowSelection) : updater;
      setRowSelection(next);
      onRowSelectionChange?.(next);
    },
    getCoreRowModel: getCoreRowModel(),
    ...(manualFiltering
      ? { manualFiltering: true as const }
      : { getFilteredRowModel: getFilteredRowModel() }),
    ...(manualPagination
      ? { manualPagination: true as const }
      : { getPaginationRowModel: getPaginationRowModel() }),
    ...(manualSorting
      ? { manualSorting: true as const }
      : { getSortedRowModel: getSortedRowModel() }),
    ...(pageCount !== undefined && { pageCount }),
    ...(rowCount !== undefined && { rowCount }),
    enableRowSelection,
  });

  function renderTableBody(): ReactElement {
    if (loading) {
      return (
        <DataTableSkeleton
          columnCount={resolvedColumns.length}
          rowCount={skeletonRows}
        />
      );
    }

    if (table.getRowModel().rows.length) {
      return (
        <>
          {table.getRowModel().rows.map((row) => (
            <TableRow
              data-state={row.getIsSelected() ? "selected" : undefined}
              key={row.id}
            >
              {row.getVisibleCells().map((cell) => (
                <TableCell key={cell.id}>
                  {flexRender(cell.column.columnDef.cell, cell.getContext())}
                </TableCell>
              ))}
            </TableRow>
          ))}
        </>
      );
    }

    return (
      <TableRow>
        <TableCell
          className="h-24 text-center text-muted-foreground"
          colSpan={resolvedColumns.length}
        >
          {emptyText}
        </TableCell>
      </TableRow>
    );
  }

  return (
    <div className={cn("w-full font-sans", className)} data-slot="data-table">
      {/* Toolbar */}
      {toolbar
        ? toolbar(table)
        : (searchKey || enableColumnVisibility) && (
            <DataTableToolbar
              enableColumnVisibility={enableColumnVisibility}
              searchKey={searchKey}
              searchPlaceholder={searchPlaceholder}
              table={table}
            />
          )}

      {/* Table */}
      <div className="border border-border">
        <Table>
          <TableHeader>
            {table.getHeaderGroups().map((headerGroup) => (
              <TableRow key={headerGroup.id}>
                {headerGroup.headers.map((header) => (
                  <TableHead key={header.id}>
                    {header.isPlaceholder
                      ? null
                      : flexRender(
                          header.column.columnDef.header,
                          header.getContext(),
                        )}
                  </TableHead>
                ))}
              </TableRow>
            ))}
          </TableHeader>
          <TableBody>{renderTableBody()}</TableBody>
        </Table>
      </div>

      {/* Pagination */}
      <DataTablePagination pageSizeOptions={pageSizeOptions} table={table} />
    </div>
  );
}

export {
  DataTable,
  DataTableToolbar,
  DataTablePagination,
  DataTableSkeleton,
  DataTableColumnHeader,
  getSelectionColumn,
};

// Re-export tanstack types for consumer convenience
export type {
  DataTableProps,
  ColumnDef,
  PaginationState,
  SortingState,
  ColumnFiltersState,
  VisibilityState,
  RowSelectionState,
};
