"use client";

import { ReactNode, useMemo, useState } from "react";
import {
  ChevronLeftIcon,
  ChevronRightIcon,
  CloseXIcon,
  ExportIcon,
  FilterIcon,
  SearchIcon,
  SortIcon,
} from "../dashboard/icons";

export type Column<T> = {
  key: string;
  label: string;
  sortable?: boolean;
  render?: (row: T) => ReactNode;
  sortValue?: (row: T) => string | number;
  width?: string;
};

export type ExternalPagination = {
  currentPage: number;
  totalPages: number;
  onPageChange: (page: number) => void;
};

type SortDir = "asc" | "desc";

export type DataTableProps<T> = {
  columns: Column<T>[];
  data: T[];
  rowKey: (row: T) => string;
  searchFields?: (keyof T)[];
  searchPlaceholder?: string;
  searchValue?: string;
  onSearchChange?: (value: string) => void;
  customFilter?: (row: T, query: string) => boolean;
  showCheckboxes?: boolean;
  selectedIds?: Set<string>;
  onSelectionChange?: (ids: Set<string>) => void;
  renderRowActions?: (row: T, index: number) => ReactNode;
  showFilters?: boolean;
  onFiltersClick?: () => void;
  filtersSlot?: ReactNode;
  showExport?: boolean;
  onExportClick?: () => void;
  exportSlot?: ReactNode;
  toolbarLeftExtra?: ReactNode;
  pageSize?: number;
  onPageSizeChange?: (size: number) => void;
  pageSizeOptions?: number[];
  externalPagination?: ExternalPagination;
  emptyMessage?: string;
  renderBulkActionBar?: (selectedCount: number, clearSelection: () => void) => ReactNode;
};

export default function DataTable<T>({
  columns,
  data,
  rowKey,
  searchFields = [],
  searchPlaceholder = "Search here...",
  searchValue,
  onSearchChange,
  customFilter,
  showCheckboxes = true,
  selectedIds,
  onSelectionChange,
  renderRowActions,
  showFilters = false,
  onFiltersClick,
  filtersSlot,
  showExport = false,
  onExportClick,
  exportSlot,
  toolbarLeftExtra,
  pageSize = 12,
  onPageSizeChange,
  pageSizeOptions = [12, 24, 48],
  externalPagination,
  emptyMessage = "No records found.",
  renderBulkActionBar,
}: DataTableProps<T>) {
  const [internalSearch, setInternalSearch] = useState("");
  const search = searchValue !== undefined ? searchValue : internalSearch;
  const setSearch = (val: string) => {
    if (onSearchChange) onSearchChange(val);
    else setInternalSearch(val);
  };
  const [sortKey, setSortKey] = useState<string | null>(null);
  const [sortDir, setSortDir] = useState<SortDir>("asc");
  const [internalSelected, setInternalSelected] = useState<Set<string>>(new Set());
  const [internalPage, setInternalPage] = useState(1);

  const selected = selectedIds ?? internalSelected;
  const setSelected = (next: Set<string>) => {
    if (!selectedIds) setInternalSelected(next);
    onSelectionChange?.(next);
  };

  const filtered = useMemo(() => {
    let result = [...data];
    const query = search.trim().toLowerCase();

    if (query && (searchFields.length > 0 || customFilter)) {
      result = result.filter((row) => {
        if (customFilter) return customFilter(row, query);
        return searchFields.some((field) => String(row[field] ?? "").toLowerCase().includes(query));
      });
    }

    if (sortKey) {
      const column = columns.find((col) => col.key === sortKey);
      result.sort((a, b) => {
        const aVal = column?.sortValue ? column.sortValue(a) : (a as Record<string, unknown>)[sortKey];
        const bVal = column?.sortValue ? column.sortValue(b) : (b as Record<string, unknown>)[sortKey];
        if (typeof aVal === "number" && typeof bVal === "number") {
          return sortDir === "asc" ? aVal - bVal : bVal - aVal;
        }
        return sortDir === "asc"
          ? String(aVal).localeCompare(String(bVal))
          : String(bVal).localeCompare(String(aVal));
      });
    }

    return result;
  }, [data, search, searchFields, customFilter, sortKey, sortDir, columns]);

  const handleSort = (key: string) => {
    const column = columns.find((col) => col.key === key);
    if (!column?.sortable) return;
    if (sortKey === key) {
      setSortDir((prev) => (prev === "asc" ? "desc" : "asc"));
    } else {
      setSortKey(key);
      setSortDir("asc");
    }
  };

  const allSelected = filtered.length > 0 && filtered.every((row) => selected.has(rowKey(row)));

  const toggleAll = () => {
    if (allSelected) {
      setSelected(new Set());
    } else {
      setSelected(new Set(filtered.map((row) => rowKey(row))));
    }
  };

  const toggleRow = (id: string) => {
    const next = new Set(selected);
    if (next.has(id)) next.delete(id);
    else next.add(id);
    setSelected(next);
  };

  const currentPage = externalPagination?.currentPage ?? internalPage;
  const totalPages = externalPagination?.totalPages ?? 1;
  const setPage = (page: number) => {
    if (externalPagination) externalPagination.onPageChange(page);
    else setInternalPage(page);
  };

  const pageNumbers = useMemo(() => {
    if (totalPages <= 3) return Array.from({ length: totalPages }, (_, i) => i + 1);
    return [1, 2, 3];
  }, [totalPages]);

  const colSpan = columns.length + (showCheckboxes ? 1 : 0) + (renderRowActions ? 1 : 0);

  return (
    <div className="min-w-0 rounded-xl border border-[#E4E7EC] bg-white">
      {selected.size > 0 && renderBulkActionBar ? (
        <div className="flex flex-wrap items-center justify-between gap-3 rounded-t-xl border-b border-[#E4E7EC] bg-[#EAF1FF] px-4 py-3">
          <div className="flex items-center gap-2">
            <span className="flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-[#1657D9]">
              <svg width="11" height="11" viewBox="0 0 20 20" fill="none" aria-hidden="true">
                <path d="M4 10.5 8 14.5 16 6" stroke="white" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round" />
              </svg>
            </span>
            <span className="text-[13px] font-semibold text-[#1657D9]">
              {String(selected.size).padStart(2, "0")} Selections
            </span>
          </div>

          <div className="flex items-center gap-3">
            {renderBulkActionBar(selected.size, () => setSelected(new Set()))}
            <button
              type="button"
              onClick={() => setSelected(new Set())}
              className="flex h-7 w-7 items-center justify-center rounded-full text-[#667085] transition-colors hover:bg-white/60"
              aria-label="Clear selection"
            >
              <CloseXIcon className="h-3.5 w-3.5" />
            </button>
          </div>
        </div>
      ) : (
        (searchFields.length > 0 || customFilter || showFilters || showExport || toolbarLeftExtra) && (
          <div className="relative z-30 flex flex-wrap items-center justify-between gap-3 rounded-t-xl border-b border-[#E4E7EC] p-4">
            <div className="flex items-center gap-3">
              <div className="flex h-10 w-[296px] items-center gap-1.5 rounded-lg border border-[#D0D5DD] px-2.5">
                <SearchIcon className="h-3.5 w-3.5 shrink-0 text-[#98A2B3]" />
                <input
                  type="text"
                  value={search}
                  onChange={(e) => setSearch(e.target.value)}
                  placeholder={searchPlaceholder}
                  className="w-full border-0 bg-transparent text-[13px] text-[#101828] outline-none placeholder:text-[#98A2B3]"
                />
              </div>
              {toolbarLeftExtra}
            </div>

            <div className="flex items-center gap-3">
              {showFilters && (
                <div className="relative z-50">
                  <button
                    type="button"
                    onClick={onFiltersClick}
                    className="flex h-10 items-center gap-2 rounded-lg border border-[#D0D5DD] px-4 text-[13px] font-medium text-[#344054] transition-colors hover:bg-[#F5F7F9]"
                  >
                    <FilterIcon />
                    Filters
                  </button>
                  {filtersSlot}
                </div>
              )}
              {showExport && (
                <div className="relative z-40">
                  <button
                    type="button"
                    onClick={onExportClick}
                    className="flex h-10 items-center gap-2 rounded-lg border border-[#D0D5DD] px-4 text-[13px] font-medium text-[#344054] transition-colors hover:bg-[#F5F7F9]"
                  >
                    <ExportIcon />
                    Export
                  </button>
                  {exportSlot}
                </div>
              )}
            </div>
          </div>
        )
      )}

      <div className="overflow-x-auto min-h-[280px]">
        <table className="w-full text-sm">
          <thead>
            <tr className="border-b border-[#E4E7EC] bg-[#EAF1FF]">
              {showCheckboxes && (
                <th className="w-10 px-4 py-3">
                  <input
                    type="checkbox"
                    checked={allSelected}
                    onChange={toggleAll}
                    className="h-3.5 w-3.5 accent-[#1657D9]"
                  />
                </th>
              )}
              {columns.map((col) => (
                <th
                  key={col.key}
                  onClick={() => handleSort(col.key)}
                  style={{ width: col.width, minWidth: col.width }}
                  className={`whitespace-nowrap px-4 py-3 text-left text-[13px] font-medium text-[#475467] ${
                    col.sortable ? "cursor-pointer select-none" : ""
                  }`}
                >
                  {col.sortable ? (
                    <span className="flex w-full items-center justify-between">
                      {col.label}
                      <SortIcon />
                    </span>
                  ) : (
                    col.label
                  )}
                </th>
              ))}
              {renderRowActions && (
                <th className="whitespace-nowrap px-4 py-3 text-left text-[13px] font-medium text-[#475467]">
                  Action
                </th>
              )}
            </tr>
          </thead>
          <tbody>
            {filtered.length === 0 ? (
              <tr>
                <td colSpan={colSpan} className="px-4 py-12 text-center text-[13px] text-[#667085]">
                  {emptyMessage}
                </td>
              </tr>
            ) : (
              filtered.map((row, index) => {
                const id = rowKey(row);
                return (
                  <tr key={id} className="border-b border-[#F2F4F7] last:border-b-0 hover:bg-[#F9FBFF]">
                    {showCheckboxes && (
                      <td className="px-4 py-3">
                        <input
                          type="checkbox"
                          checked={selected.has(id)}
                          onChange={() => toggleRow(id)}
                          className="h-3.5 w-3.5 accent-[#1657D9]"
                        />
                      </td>
                    )}
                    {columns.map((col) => (
                      <td key={col.key} style={{ width: col.width, minWidth: col.width }} className="px-4 py-3">
                        {col.render ? col.render(row) : String((row as Record<string, unknown>)[col.key] ?? "")}
                      </td>
                    ))}
                    {renderRowActions && (
                      <td className="relative px-4 py-3">{renderRowActions(row, index)}</td>
                    )}
                  </tr>
                );
              })
            )}
          </tbody>
        </table>
      </div>

      <div className="flex flex-wrap items-center justify-between gap-3 border-t border-[#E4E7EC] px-4 py-3">
        <div className="flex items-center gap-2 text-[13px] text-[#475467]">
          Page Entries
          <select
            value={pageSize}
            onChange={(e) => onPageSizeChange?.(Number(e.target.value))}
            className="h-8 rounded-md border border-[#D0D5DD] px-2 text-[13px] text-[#101828] outline-none"
          >
            {pageSizeOptions.map((size) => (
              <option key={size} value={size}>
                {size}
              </option>
            ))}
          </select>
        </div>

        {totalPages > 1 && (
          <div className="flex items-center gap-1">
            <button
              type="button"
              onClick={() => setPage(Math.max(1, currentPage - 1))}
              className="flex h-8 w-8 items-center justify-center rounded-md text-[#667085] transition-colors hover:bg-[#F2F4F7]"
              aria-label="Previous page"
            >
              <ChevronLeftIcon />
            </button>
            {pageNumbers.map((num) => (
              <button
                key={num}
                type="button"
                onClick={() => setPage(num)}
                className={`flex h-8 w-8 items-center justify-center rounded-md text-[13px] font-medium transition-colors ${
                  currentPage === num ? "border border-[#1657D9] text-[#1657D9]" : "text-[#344054] hover:bg-[#F2F4F7]"
                }`}
              >
                {num}
              </button>
            ))}
            {totalPages > 3 && (
              <>
                <span className="px-1 text-[13px] text-[#98A2B3]">...</span>
                <button
                  type="button"
                  onClick={() => setPage(totalPages)}
                  className={`flex h-8 w-8 items-center justify-center rounded-md text-[13px] font-medium transition-colors ${
                    currentPage === totalPages
                      ? "border border-[#1657D9] text-[#1657D9]"
                      : "text-[#344054] hover:bg-[#F2F4F7]"
                  }`}
                >
                  {totalPages}
                </button>
              </>
            )}
            <button
              type="button"
              onClick={() => setPage(Math.min(totalPages, currentPage + 1))}
              className="flex h-8 w-8 items-center justify-center rounded-md bg-[#1657D9] text-white transition-colors hover:bg-[#1445B0]"
              aria-label="Next page"
            >
              <ChevronRightIcon />
            </button>
          </div>
        )}
      </div>
    </div>
  );
}
