"use client";

import {useMemo, useState} from "react";
import {ArrowDownUp, ChevronLeft, ChevronRight, Search} from "lucide-react";
import {cn} from "@/lib/utils";

export type DataTableColumn<T> = {
  key: string;
  header: string;
  accessor: (row: T) => string | number | boolean | null | undefined;
  cell?: (row: T) => React.ReactNode;
  sortable?: boolean;
  className?: string;
};

export type DataTableFilter<T> = {
  key: string;
  label: string;
  options: Array<{label: string; value: string}>;
  getValue: (row: T) => string;
};

type DataTableProps<T> = {
  data: T[];
  columns: Array<DataTableColumn<T>>;
  filters?: Array<DataTableFilter<T>>;
  searchPlaceholder?: string;
  pageSize?: number;
  emptyMessage: string;
};

function normalizeValue(value: string | number | boolean | null | undefined) {
  if (value === null || value === undefined) return "";
  return String(value).toLowerCase();
}

export function DataTable<T>({
  data,
  columns,
  filters = [],
  searchPlaceholder,
  pageSize = 8,
  emptyMessage
}: DataTableProps<T>) {
  const [query, setQuery] = useState("");
  const [page, setPage] = useState(1);
  const [sortState, setSortState] = useState<{key: string; direction: "asc" | "desc"} | null>(null);
  const [filterValues, setFilterValues] = useState<Record<string, string>>(
    Object.fromEntries(filters.map((filter) => [filter.key, "all"]))
  );

  const filteredRows = useMemo(() => {
    const loweredQuery = query.trim().toLowerCase();

    const nextRows = data.filter((row) => {
      const matchesSearch =
        loweredQuery.length === 0 ||
        columns.some((column) => normalizeValue(column.accessor(row)).includes(loweredQuery));

      if (!matchesSearch) return false;

      return filters.every((filter) => {
        const selectedValue = filterValues[filter.key] ?? "all";
        if (selectedValue === "all") return true;
        return filter.getValue(row) === selectedValue;
      });
    });

    if (!sortState) return nextRows;

    const sortColumn = columns.find((column) => column.key === sortState.key);
    if (!sortColumn) return nextRows;

    return [...nextRows].sort((left, right) => {
      const leftValue = normalizeValue(sortColumn.accessor(left));
      const rightValue = normalizeValue(sortColumn.accessor(right));
      if (leftValue === rightValue) return 0;
      const result = leftValue > rightValue ? 1 : -1;
      return sortState.direction === "asc" ? result : -result;
    });
  }, [columns, data, filterValues, filters, query, sortState]);

  const totalPages = Math.max(1, Math.ceil(filteredRows.length / pageSize));
  const currentPage = Math.min(page, totalPages);
  const paginatedRows = filteredRows.slice((currentPage - 1) * pageSize, currentPage * pageSize);

  function toggleSort(key: string) {
    setPage(1);
    setSortState((current) => {
      if (!current || current.key !== key) return {key, direction: "asc"};
      if (current.direction === "asc") return {key, direction: "desc"};
      return null;
    });
  }

  return (
    <div className="space-y-4 rounded-2xl border border-slate-200 bg-white p-4 shadow-sm">
      <div className="flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between">
        <label className="relative flex w-full max-w-md items-center">
          <Search className="pointer-events-none absolute inset-y-0 start-3 my-auto size-4 text-slate-400" />
          <input
            className="h-10 w-full rounded-lg border border-slate-200 bg-white ps-10 pe-3 text-sm outline-none ring-0 transition placeholder:text-slate-400 focus:border-slate-400 focus:ring-2 focus:ring-slate-200"
            onChange={(e) => { setPage(1); setQuery(e.target.value); }}
            placeholder={searchPlaceholder}
            value={query}
          />
        </label>
        {filters.length > 0 && (
          <div className="flex flex-wrap items-center gap-3">
            {filters.map((filter) => (
              <label className="flex min-w-40 flex-col gap-1 text-sm" key={filter.key}>
                <span className="font-medium text-slate-600">{filter.label}</span>
                <select
                  className="h-10 rounded-lg border border-slate-200 bg-white px-3 text-sm outline-none transition focus:border-slate-400 focus:ring-2 focus:ring-slate-200"
                  onChange={(e) => { setPage(1); setFilterValues((c) => ({...c, [filter.key]: e.target.value})); }}
                  value={filterValues[filter.key] ?? "all"}
                >
                  <option value="all">All</option>
                  {filter.options.map((opt) => <option key={opt.value} value={opt.value}>{opt.label}</option>)}
                </select>
              </label>
            ))}
          </div>
        )}
      </div>

      <div className="overflow-x-auto">
        <table className="min-w-full border-separate border-spacing-0 text-sm">
          <thead>
            <tr>
              {columns.map((col) => (
                <th
                  key={col.key}
                  className={cn("border-b border-slate-200 px-3 py-3 text-start font-semibold text-slate-700", col.className)}
                >
                  {col.sortable ? (
                    <button className="inline-flex items-center gap-2 text-start transition hover:text-slate-950" onClick={() => toggleSort(col.key)} type="button">
                      {col.header}
                      <ArrowDownUp className="size-4" />
                    </button>
                  ) : col.header}
                </th>
              ))}
            </tr>
          </thead>
          <tbody>
            {paginatedRows.length > 0 ? (
              paginatedRows.map((row, idx) => (
                <tr className="transition hover:bg-slate-50" key={idx}>
                  {columns.map((col) => (
                    <td key={col.key} className={cn("border-b border-slate-100 px-3 py-3 align-top", col.className)}>
                      {col.cell ? col.cell(row) : String(col.accessor(row) ?? "—")}
                    </td>
                  ))}
                </tr>
              ))
            ) : (
              <tr><td className="px-3 py-8 text-center text-slate-500" colSpan={columns.length}>{emptyMessage}</td></tr>
            )}
          </tbody>
        </table>
      </div>

      <div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
        <p className="text-sm text-slate-500">{filteredRows.length} results · page {currentPage} of {totalPages}</p>
        <div className="flex items-center gap-2">
          <button className="inline-flex h-9 items-center gap-2 rounded-lg border border-slate-200 px-3 text-sm font-medium text-slate-700 transition hover:bg-slate-100 disabled:opacity-50" disabled={currentPage <= 1} onClick={() => setPage((c) => Math.max(1, c - 1))} type="button">
            <ChevronLeft className="size-4" /> Prev
          </button>
          <button className="inline-flex h-9 items-center gap-2 rounded-lg border border-slate-200 px-3 text-sm font-medium text-slate-700 transition hover:bg-slate-100 disabled:opacity-50" disabled={currentPage >= totalPages} onClick={() => setPage((c) => Math.min(totalPages, c + 1))} type="button">
            Next <ChevronRight className="size-4" />
          </button>
        </div>
      </div>
    </div>
  );
}
