"use client";

import * as React from "react";
import {
  ChevronDownIcon,
  ChevronUpIcon,
  ChevronsUpDownIcon,
} from "lucide-react";

import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";
import { EmptyState } from "@/components/ui/empty-state";
import { ErrorState } from "@/components/ui/error-state";
import { Skeleton } from "@/components/ui/skeleton";
import {
  TableBody,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
} from "@/components/ui/table";
import { Typography } from "@/components/ui/typography";
import { useTranslation } from "@/hooks/useTranslation";
import { cn } from "@/lib/utils";

import { DataTablePagination } from "./data-table-pagination";
import { RowActions } from "./data-table-row-actions";
import { DataTableSearch } from "./data-table-search";
import { DEFAULT_DATA_TABLE_QUERY } from "./use-data-table-query";
import type {
  DataTableColumn,
  DataTableQuery,
  DataTableRowAction,
} from "./types";

const ALIGN_CLASSES = {
  start: "text-start",
  center: "text-center",
  end: "text-end",
} as const;

interface DataTableProps<TRow> {
  columns: DataTableColumn<TRow>[];
  data: TRow[];
  /** Stable unique id per row — selection and React keys depend on it */
  getRowId: (row: TRow) => string;

  /**
   * "client" (default): the DataTable searches/sorts/paginates `data` itself.
   * "server": the parent owns the query — pass `query`, `onQueryChange`, and
   * `totalCount`, and hand the query to your API (see useDataTableQuery).
   */
  mode?: "client" | "server";
  query?: DataTableQuery;
  onQueryChange?: (query: DataTableQuery) => void;
  /** Total row count across all pages (server mode) */
  totalCount?: number;

  /** Show the debounced search box */
  searchable?: boolean;
  searchPlaceholder?: string;
  /** Client mode: text to match the search against (required for client search) */
  searchText?: (row: TRow) => string;
  /** Extra toolbar content (filter selects, chips, buttons…) beside the search */
  toolbar?: React.ReactNode;

  /** Multi-select rows (checkbox column). Selection persists across pages. */
  selectable?: boolean;
  /** Controlled selection — omit to let the table manage it internally */
  selectedIds?: string[];
  onSelectionChange?: (ids: string[]) => void;
  /** Rendered in the toolbar while rows are selected (bulk actions) */
  bulkActions?: (selectedIds: string[]) => React.ReactNode;

  /** Per-row actions menu (ellipsis at the row's inline-end) */
  rowActions?: (row: TRow) => DataTableRowAction<TRow>[];
  onRowClick?: (row: TRow) => void;

  loading?: boolean;
  error?: boolean;
  onRetry?: () => void;
  /** Overrides the default EmptyState */
  emptyState?: React.ReactNode;

  /** Defaults to [10, 20, 50]; the first entry is the initial page size */
  pageSizeOptions?: number[];
  showPagination?: boolean;
  /**
   * Height class for the vertical scroll area (default "max-h-[70vh]").
   * The header row stays fixed while the body scrolls vertically; horizontal
   * scrolling moves header and body together (single scroll container +
   * sticky header cells).
   */
  maxHeightClassName?: string;
  className?: string;
}

/**
 * Reusable list table for all portal screens (orders, providers, settlements,
 * team, disputes…). Desktop renders a real table; below `md` it renders the
 * same rows as cards (first column = card title, remaining columns as
 * label/value lines). RTL-safe throughout — only logical classes.
 */
function DataTable<TRow>({
  columns,
  data,
  getRowId,
  mode = "client",
  query: externalQuery,
  onQueryChange,
  totalCount,
  searchable = false,
  searchPlaceholder,
  searchText,
  toolbar,
  selectable = false,
  selectedIds,
  onSelectionChange,
  bulkActions,
  rowActions,
  onRowClick,
  loading = false,
  error = false,
  onRetry,
  emptyState,
  pageSizeOptions = [10, 20, 50],
  showPagination = true,
  maxHeightClassName,
  className,
}: DataTableProps<TRow>) {
  const { t } = useTranslation();
  const isServer = mode === "server";

  // ── Query state (internal for client mode, controlled for server mode) ─────
  const [internalQuery, setInternalQuery] = React.useState<DataTableQuery>({
    ...DEFAULT_DATA_TABLE_QUERY,
    pageSize: pageSizeOptions[0] ?? DEFAULT_DATA_TABLE_QUERY.pageSize,
  });
  const query = isServer ? (externalQuery ?? internalQuery) : internalQuery;

  function setQuery(next: DataTableQuery) {
    if (isServer) onQueryChange?.(next);
    else setInternalQuery(next);
  }

  // ── Selection state (controlled or internal) ────────────────────────────────
  const [internalSelected, setInternalSelected] = React.useState<string[]>([]);
  const selected = selectedIds ?? internalSelected;

  function setSelected(ids: string[]) {
    if (selectedIds === undefined) setInternalSelected(ids);
    onSelectionChange?.(ids);
  }

  // ── Client-mode processing: search → sort → paginate ───────────────────────
  const processed = React.useMemo(() => {
    if (isServer) return data;

    let rows = data;
    const searchTerm = query.search.trim().toLowerCase();
    if (searchTerm && searchText) {
      rows = rows.filter((row) =>
        searchText(row).toLowerCase().includes(searchTerm),
      );
    }

    if (query.sort) {
      const { key, direction } = query.sort;
      const column = columns.find((c) => c.key === key);
      const getValue =
        column?.sortValue ??
        ((row: TRow) =>
          (row as Record<string, unknown>)[key] as string | number | null);
      const factor = direction === "asc" ? 1 : -1;
      rows = [...rows].sort(
        (a, b) => compareValues(getValue(a), getValue(b)) * factor,
      );
    }

    return rows;
  }, [isServer, data, query.search, query.sort, columns, searchText]);

  const total = isServer ? (totalCount ?? data.length) : processed.length;
  const pageCount = Math.max(1, Math.ceil(total / query.pageSize));
  const safePage = Math.min(query.page, pageCount);

  const pageRows = React.useMemo(() => {
    if (isServer) return data;
    const start = (safePage - 1) * query.pageSize;
    return processed.slice(start, start + query.pageSize);
  }, [isServer, data, processed, safePage, query.pageSize]);

  // ── Selection helpers (page-scoped select-all) ──────────────────────────────
  const pageRowIds = pageRows.map(getRowId);
  const allPageSelected =
    pageRowIds.length > 0 && pageRowIds.every((id) => selected.includes(id));
  const somePageSelected = pageRowIds.some((id) => selected.includes(id));

  function toggleSelectAll(checked: boolean) {
    if (checked) setSelected([...new Set([...selected, ...pageRowIds])]);
    else setSelected(selected.filter((id) => !pageRowIds.includes(id)));
  }

  function toggleRowSelected(id: string, checked: boolean) {
    if (checked) setSelected([...selected, id]);
    else setSelected(selected.filter((s) => s !== id));
  }

  // ── Sorting ─────────────────────────────────────────────────────────────────
  function toggleSort(key: string) {
    const current = query.sort;
    const next =
      !current || current.key !== key
        ? ({ key, direction: "asc" } as const)
        : current.direction === "asc"
          ? ({ key, direction: "desc" } as const)
          : null;
    setQuery({ ...query, sort: next, page: 1 });
  }

  const showSearch = searchable && (isServer || !!searchText);
  const showToolbar = showSearch || !!toolbar || selected.length > 0;
  const hasActiveSearch = query.search.trim() !== "";
  const isEmpty = !loading && !error && pageRows.length === 0;
  const skeletonRowCount = Math.min(query.pageSize, 6);

  return (
    <div className={cn("flex w-full flex-col gap-3", className)}>
      {/* ── Toolbar: search + filters + bulk actions ─────────────────────── */}
      {showToolbar && (
        <div className="flex flex-col gap-3 sm:flex-row sm:flex-wrap sm:items-center sm:justify-between">
          <div className="flex flex-1 flex-col gap-3 sm:flex-row sm:items-center">
            {showSearch && (
              <DataTableSearch
                value={query.search}
                placeholder={searchPlaceholder}
                onChange={(search) => setQuery({ ...query, search, page: 1 })}
              />
            )}
            {toolbar}
          </div>
          {selected.length > 0 && (
            <div className="flex items-center gap-2">
              <Typography as="span" variant="body" color="secondary">
                {t("dataTable.selectedCount", { count: selected.length })}
              </Typography>
              <Button variant="ghost" size="sm" onClick={() => setSelected([])}>
                {t("dataTable.clearSelection")}
              </Button>
              {bulkActions?.(selected)}
            </div>
          )}
        </div>
      )}

      {/* ── Error / empty states ─────────────────────────────────────────── */}
      {error ? (
        <div className="rounded-[12px] border border-border bg-white">
          <ErrorState onRetry={onRetry} />
        </div>
      ) : isEmpty ? (
        <div className="rounded-[12px] border border-border bg-white">
          {emptyState ?? (
            <EmptyState
              title={t(
                hasActiveSearch
                  ? "dataTable.noResultsTitle"
                  : "dataTable.emptyTitle",
              )}
              description={t(
                hasActiveSearch
                  ? "dataTable.noResultsDescription"
                  : "dataTable.emptyDescription",
              )}
            />
          )}
        </div>
      ) : (
        <>
          {/* ── Desktop table (md+): sticky header, body scrolls ──────────── */}
          <div className="hidden overflow-hidden rounded-[12px] border border-border bg-white md:block">
            <div
              className={cn(
                "relative w-full overflow-auto",
                maxHeightClassName ?? "max-h-[70vh]",
              )}
            >
              <table className="w-full caption-bottom text-sm">
                <TableHeader>
                  <TableRow className="hover:bg-transparent">
                    {selectable && (
                      <TableHead className={cn(STICKY_HEADER_CLASSES, "w-10")}>
                        <Checkbox
                          checked={allPageSelected}
                          indeterminate={!allPageSelected && somePageSelected}
                          aria-label={t("dataTable.selectAll")}
                          onCheckedChange={(checked) =>
                            toggleSelectAll(!!checked)
                          }
                        />
                      </TableHead>
                    )}
                    {columns.map((column) => (
                      <TableHead
                        key={column.key}
                        className={cn(
                          STICKY_HEADER_CLASSES,
                          ALIGN_CLASSES[column.align ?? "start"],
                          column.className,
                        )}
                      >
                        {column.sortable ? (
                          <button
                            type="button"
                            className="inline-flex items-center gap-1 select-none"
                            onClick={() => toggleSort(column.key)}
                          >
                            <Typography
                              as="span"
                              variant="body"
                              className="font-medium"
                            >
                              {column.header}
                            </Typography>
                            <SortIcon
                              direction={
                                query.sort?.key === column.key
                                  ? query.sort.direction
                                  : null
                              }
                            />
                          </button>
                        ) : (
                          <Typography
                            as="span"
                            variant="body"
                            className="font-medium"
                          >
                            {column.header}
                          </Typography>
                        )}
                      </TableHead>
                    ))}
                    {rowActions && (
                      <TableHead
                        className={cn(
                          STICKY_HEADER_CLASSES,
                          "w-px whitespace-nowrap text-end",
                        )}
                      >
                        <Typography
                          as="span"
                          variant="caption"
                          className="sr-only"
                        >
                          {t("dataTable.actions")}
                        </Typography>
                      </TableHead>
                    )}
                  </TableRow>
                </TableHeader>
                <TableBody>
                  {loading
                    ? Array.from({ length: skeletonRowCount }).map(
                        (_, index) => (
                          <TableRow key={index}>
                            {selectable && (
                              <TableCell>
                                <Skeleton className="size-4 rounded-[4px]" />
                              </TableCell>
                            )}
                            {columns.map((column) => (
                              <TableCell key={column.key}>
                                <Skeleton className="h-4 w-full max-w-24" />
                              </TableCell>
                            ))}
                            {rowActions && (
                              <TableCell className="w-px whitespace-nowrap text-end">
                                <Skeleton className="ms-auto size-8 rounded-md" />
                              </TableCell>
                            )}
                          </TableRow>
                        ),
                      )
                    : pageRows.map((row, index) => {
                        const id = getRowId(row);
                        const isSelected = selected.includes(id);
                        return (
                          <TableRow
                            key={id}
                            data-state={isSelected ? "selected" : undefined}
                            className={cn(onRowClick && "cursor-pointer")}
                            onClick={
                              onRowClick ? () => onRowClick(row) : undefined
                            }
                          >
                            {selectable && (
                              <TableCell
                                className="w-10"
                                onClick={(event) => event.stopPropagation()}
                              >
                                <Checkbox
                                  checked={isSelected}
                                  aria-label={t("dataTable.selectRow")}
                                  onCheckedChange={(checked) =>
                                    toggleRowSelected(id, !!checked)
                                  }
                                />
                              </TableCell>
                            )}
                            {columns.map((column) => (
                              <TableCell
                                key={column.key}
                                className={cn(
                                  ALIGN_CLASSES[column.align ?? "start"],
                                  column.className,
                                )}
                              >
                                {column.cell(row, index)}
                              </TableCell>
                            ))}
                            {rowActions && (
                              <TableCell
                                className="w-px whitespace-nowrap text-end"
                                onClick={(event) => event.stopPropagation()}
                              >
                                <RowActions
                                  row={row}
                                  actions={rowActions(row)}
                                />
                              </TableCell>
                            )}
                          </TableRow>
                        );
                      })}
                </TableBody>
              </table>
            </div>
          </div>

          {/* ── Mobile card list (< md) ───────────────────────────────────── */}
          <div className="flex flex-col gap-3 md:hidden">
            {loading
              ? Array.from({ length: 3 }).map((_, index) => (
                  <div
                    key={index}
                    className="flex flex-col gap-3 rounded-[12px] border border-border bg-white p-4"
                  >
                    <Skeleton className="h-5 w-2/3" />
                    <Skeleton className="h-4 w-full" />
                    <Skeleton className="h-4 w-1/2" />
                  </div>
                ))
              : pageRows.map((row, index) => {
                  const id = getRowId(row);
                  const isSelected = selected.includes(id);
                  const [primaryColumn, ...restColumns] = columns;
                  return (
                    <div
                      key={id}
                      className={cn(
                        "flex flex-col gap-3 rounded-[12px] border border-border bg-white p-4",
                        isSelected && "border-primary-blue",
                        onRowClick && "cursor-pointer",
                      )}
                      onClick={onRowClick ? () => onRowClick(row) : undefined}
                    >
                      <div className="flex items-center gap-3">
                        {selectable && (
                          <span onClick={(event) => event.stopPropagation()}>
                            <Checkbox
                              checked={isSelected}
                              aria-label={t("dataTable.selectRow")}
                              onCheckedChange={(checked) =>
                                toggleRowSelected(id, !!checked)
                              }
                            />
                          </span>
                        )}
                        <div className="min-w-0 flex-1">
                          {primaryColumn?.cell(row, index)}
                        </div>
                        {rowActions && (
                          <span onClick={(event) => event.stopPropagation()}>
                            <RowActions row={row} actions={rowActions(row)} />
                          </span>
                        )}
                      </div>
                      {restColumns
                        .filter((column) => !column.hideOnMobile)
                        .map((column) => (
                          <div
                            key={column.key}
                            className="flex items-center justify-between gap-2"
                          >
                            <Typography
                              as="span"
                              variant="caption"
                              color="secondary"
                            >
                              {column.mobileLabel ?? column.header}
                            </Typography>
                            <div className="min-w-0 text-end">
                              {column.cell(row, index)}
                            </div>
                          </div>
                        ))}
                    </div>
                  );
                })}
          </div>
        </>
      )}

      {/* ── Pagination footer ────────────────────────────────────────────── */}
      {showPagination && !error && total > 0 && (
        <DataTablePagination
          page={safePage}
          pageSize={query.pageSize}
          pageCount={pageCount}
          pageSizeOptions={pageSizeOptions}
          disabled={loading}
          onPageChange={(page) => setQuery({ ...query, page })}
          onPageSizeChange={(pageSize) =>
            setQuery({ ...query, pageSize, page: 1 })
          }
        />
      )}
    </div>
  );
}

/**
 * Sticky header cells: the header stays fixed while the body scrolls
 * vertically, and scrolls WITH the body horizontally (same scroll container).
 * The bottom line is a shadow, not a border — borders on sticky cells
 * disappear under `border-collapse` when scrolled.
 */
const STICKY_HEADER_CLASSES =
  "sticky top-0 z-10 bg-white shadow-[0_1px_0_0_var(--border)]";

function SortIcon({ direction }: { direction: "asc" | "desc" | null }) {
  if (direction === "asc") return <ChevronUpIcon className="size-3.5" />;
  if (direction === "desc") return <ChevronDownIcon className="size-3.5" />;
  return <ChevronsUpDownIcon className="size-3.5 text-secondary-text/50" />;
}

function compareValues(
  a: string | number | Date | null | undefined,
  b: string | number | Date | null | undefined,
): number {
  const aEmpty = a === null || a === undefined || a === "";
  const bEmpty = b === null || b === undefined || b === "";
  if (aEmpty && bEmpty) return 0;
  if (aEmpty) return 1; // empty values sort last
  if (bEmpty) return -1;
  if (typeof a === "number" && typeof b === "number") return a - b;
  if (a instanceof Date && b instanceof Date) return a.getTime() - b.getTime();
  return String(a).localeCompare(String(b));
}

export { DataTable };
export type { DataTableProps };
