"use client";

import { ArrowDown, ArrowUp, ArrowUpDown } from "lucide-react";
import * as React from "react";

import { cn } from "@/lib/utils";

export type TableSortDirection = "asc" | "desc";
export type TableSortState<K extends string = string> = { key: K; direction: TableSortDirection } | null;
export type TableSortValue = string | number | null | undefined;

function isEmptySortValue(value: TableSortValue): value is null | undefined | "" {
  return value == null || value === "" || (typeof value === "number" && Number.isNaN(value));
}

/** Empty values always sort last, whichever direction is active. */
export function compareTableSortValues(a: TableSortValue, b: TableSortValue, direction: TableSortDirection): number {
  const aEmpty = isEmptySortValue(a);
  const bEmpty = isEmptySortValue(b);
  if (aEmpty || bEmpty) return aEmpty === bEmpty ? 0 : aEmpty ? 1 : -1;

  const result =
    typeof a === "number" && typeof b === "number"
      ? a - b
      : String(a).localeCompare(String(b), undefined, { numeric: true, sensitivity: "base" });
  return direction === "asc" ? result : -result;
}

/**
 * Click-to-sort state for a click-in table: first click on a column sorts ascending, the next descending.
 * Pass `accessors` as a module-level constant so the sorted rows stay memoised.
 */
export function useTableSort<T, K extends string>(
  rows: readonly T[],
  accessors: Record<K, (row: T) => TableSortValue>,
  initial: TableSortState<K> = null,
) {
  const [sort, setSort] = React.useState<TableSortState<K>>(initial);

  const toggle = React.useCallback((key: K) => {
    setSort((prev) =>
      prev?.key === key ? { key, direction: prev.direction === "asc" ? "desc" : "asc" } : { key, direction: "asc" },
    );
  }, []);

  const sorted = React.useMemo(() => {
    if (!sort) return rows;
    const read = accessors[sort.key];
    return rows
      .map((row, index) => ({ row, index }))
      .sort((x, y) => compareTableSortValues(read(x.row), read(y.row), sort.direction) || x.index - y.index)
      .map(({ row }) => row);
  }, [rows, sort, accessors]);

  const directionFor = React.useCallback(
    (key: K): TableSortDirection | null => (sort?.key === key ? sort.direction : null),
    [sort],
  );

  return { rows: sorted, sort, toggle, directionFor };
}

/**
 * Own scroll area for a click-in table. The header sticks to this container (a sticky header cannot
 * escape an `overflow-x-auto` ancestor, so the table needs its own vertical scroller too).
 */
export function StickyTableScroll({ className, children }: { className?: string; children: React.ReactNode }) {
  return (
    <div
      className={cn(
        "max-h-[70dvh] overflow-auto rounded-xl border border-border/60 bg-card shadow-sm",
        className,
      )}
    >
      {children}
    </div>
  );
}

/** Frozen header cell (stays pinned while the table body scrolls); sortable when `onSort` is given. */
export function SortableTh({
  label,
  direction,
  onSort,
  align = "left",
  className,
}: {
  label: React.ReactNode;
  direction?: TableSortDirection | null;
  onSort?: () => void;
  align?: "left" | "right";
  className?: string;
}) {
  const Icon = direction === "asc" ? ArrowUp : direction === "desc" ? ArrowDown : ArrowUpDown;
  const cellClass = cn(
    "sticky top-0 z-10 bg-sky-950 px-3 py-2.5 text-[11px] font-semibold uppercase tracking-wide whitespace-nowrap text-white",
    align === "right" ? "text-right" : "text-left",
    className,
  );

  if (!onSort) {
    return <th className={cellClass}>{label}</th>;
  }

  return (
    <th
      className={cellClass}
      aria-sort={direction === "asc" ? "ascending" : direction === "desc" ? "descending" : "none"}
    >
      <button
        type="button"
        onClick={onSort}
        className={cn(
          "inline-flex cursor-pointer items-center gap-1 uppercase tracking-wide hover:opacity-80",
          align === "right" && "w-full justify-end",
        )}
      >
        <span>{label}</span>
        <Icon className={cn("size-3 shrink-0", direction ? "opacity-100" : "opacity-60")} aria-hidden />
      </button>
    </th>
  );
}
