"use client";

import { useConfirm } from "@/components/ConfirmDialog";

import { useEffect, useMemo, useState } from "react";
import { Search, Loader2, Inbox } from "lucide-react";
import clsx from "clsx";

export type Column<T> = {
  key: string;
  label: string;
  /** Narrow columns sit at their natural width; the rest share what's left. */
  width?: string;
  render: (row: T) => React.ReactNode;
};

/**
 * The shared list used by most admin sections: fetch, search, empty state,
 * and a consistent table. Each page supplies its own columns and actions,
 * so they all behave the same without repeating the plumbing.
 */
export function AdminTable<T extends Record<string, unknown>>({
  endpoint,
  columns,
  searchKeys,
  empty = "Nothing here yet.",
  transform,
  refreshKey,
  actions,
}: {
  endpoint: string;
  columns: Column<T>[];
  searchKeys: string[];
  empty?: string;
  transform?: (data: unknown) => T[];
  refreshKey?: number;
  actions?: (row: T, refresh: () => void) => React.ReactNode;
}) {
  const [rows, setRows] = useState<T[] | null>(null);
  const [query, setQuery] = useState("");
  const [version, setVersion] = useState(0);

  const refresh = () => setVersion((v) => v + 1);

  useEffect(() => {
    let cancelled = false;
    setRows(null);

    fetch(endpoint)
      .then((r) => (r.ok ? r.json() : []))
      .then((d) => {
        if (cancelled) return;
        setRows(transform ? transform(d) : Array.isArray(d) ? (d as T[]) : []);
      })
      .catch(() => !cancelled && setRows([]));

    return () => {
      cancelled = true;
    };
    // transform is defined inline by callers, so it isn't a dependency.
  }, [endpoint, version, refreshKey]);

  const filtered = useMemo(() => {
    if (!rows) return [];
    const q = query.trim().toLowerCase();
    if (!q) return rows;
    return rows.filter((r) =>
      searchKeys.some((k) => {
        const v = r[k];
        if (typeof v === "string") return v.toLowerCase().includes(q);
        if (v && typeof v === "object" && "username" in v) {
          return String((v as { username: string }).username).toLowerCase().includes(q);
        }
        return false;
      })
    );
  }, [rows, query, searchKeys]);

  return (
    <>
      <div className="at-bar">
        <div className="at-search">
          <Search size={14} />
          <input
            value={query}
            onChange={(e) => setQuery(e.target.value)}
            placeholder="Search…"
          />
        </div>
        {rows && (
          <span className="at-count">
            {filtered.length}
            {filtered.length !== rows.length && ` of ${rows.length}`}
          </span>
        )}
      </div>

      {rows === null && (
        <p className="ps-hint">
          <Loader2 size={14} className="animate-spin inline mr-2" />
          Loading…
        </p>
      )}

      {rows && filtered.length === 0 && (
        <div className="at-empty">
          <Inbox size={26} />
          <p>{query ? `Nothing matches “${query}”.` : empty}</p>
        </div>
      )}

      {rows && filtered.length > 0 && (
        <div className="at-wrap">
          <table className="at-table">
            <thead>
              <tr>
                {columns.map((c) => (
                  <th key={c.key} style={c.width ? { width: c.width } : undefined}>
                    {c.label}
                  </th>
                ))}
                {actions && <th className="at-actionhead">Actions</th>}
              </tr>
            </thead>
            <tbody>
              {filtered.map((row, i) => (
                <tr key={String(row.id ?? i)}>
                  {columns.map((c) => (
                    <td key={c.key}>{c.render(row)}</td>
                  ))}
                  {actions && <td className="at-actions">{actions(row, refresh)}</td>}
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      )}
    </>
  );
}

/** Delete button shared by the admin lists. */
export function DeleteButton({
  endpoint,
  id,
  what,
  onDone,
}: {
  endpoint: string;
  id: string;
  what: string;
  onDone: () => void;
}) {
  const confirm = useConfirm();
  const [busy, setBusy] = useState(false);

  return (
    <button
      disabled={busy}
      onClick={async () => {
        const sure = await confirm({
          title: `Delete this ${what}?`,
          body: "This can't be undone.",
          confirmLabel: "Delete",
          danger: true,
        });
        if (!sure) return;
        setBusy(true);
        await fetch(`${endpoint}?id=${id}`, { method: "DELETE" }).catch(() => {});
        setBusy(false);
        onDone();
      }}
      className={clsx("ab-del", busy && "opacity-50")}
      title={`Delete ${what}`}
    >
      {busy ? <Loader2 size={13} className="animate-spin" /> : "Delete"}
    </button>
  );
}
