// =================================================================
// ui-data.jsx — Data display components.
//
//   - DataTable: sortable headers, optional row selection, sticky head,
//                pagination, density modes, empty state, row-click handler.
//   - Pagination: standalone footer if needed elsewhere.
//
// Column shape:
//   { key, label, w?, align?, mono?, sortable?, accessor?, render? }
//
// `accessor(row)` returns the raw value used for sorting/filtering.
// `render(row)` returns the React node for display.
// =================================================================

const DataTable = ({
  columns, rows, keyField = "id",
  selectable = false,
  selected, onSelectChange,           // controlled selection: Set | array of keys
  rowsPerPage = 0,                    // 0 disables pagination
  defaultSort,                        // { key, dir: "asc"|"desc" }
  stickyHeader = false,
  empty,
  onRowClick,
  density = "default",                // default | compact
  rowClassName,
  footer,
}) => {
  const [sort, setSort] = React.useState(defaultSort || { key: null, dir: "asc" });
  const [page, setPage] = React.useState(0);
  React.useEffect(() => { setPage(0); }, [rows.length, sort.key, sort.dir]);

  const _selected = selectable
    ? (selected instanceof Set ? selected : new Set(selected || []))
    : new Set();

  const setSel = (next) => onSelectChange && onSelectChange(next);

  const sortedRows = React.useMemo(() => {
    if (!sort.key) return rows;
    const col = columns.find(c => c.key === sort.key);
    if (!col || col.sortable === false) return rows;
    const get = col.accessor || ((r) => r[col.key]);
    const dir = sort.dir === "asc" ? 1 : -1;
    return [...rows].sort((a, b) => {
      const va = get(a), vb = get(b);
      if (va == null && vb == null) return 0;
      if (va == null) return -1 * dir;
      if (vb == null) return 1 * dir;
      if (typeof va === "number" && typeof vb === "number") return (va - vb) * dir;
      return String(va).localeCompare(String(vb)) * dir;
    });
  }, [rows, sort, columns]);

  const totalPages = rowsPerPage ? Math.max(1, Math.ceil(sortedRows.length / rowsPerPage)) : 1;
  const view = rowsPerPage
    ? sortedRows.slice(page * rowsPerPage, (page + 1) * rowsPerPage)
    : sortedRows;

  const allSelected = selectable && view.length > 0 && view.every(r => _selected.has(r[keyField]));
  const someSelected = selectable && view.some(r => _selected.has(r[keyField])) && !allSelected;

  const toggleAll = () => {
    const next = new Set(_selected);
    if (allSelected) view.forEach(r => next.delete(r[keyField]));
    else view.forEach(r => next.add(r[keyField]));
    setSel(next);
  };
  const toggleRow = (k) => {
    const next = new Set(_selected);
    if (next.has(k)) next.delete(k); else next.add(k);
    setSel(next);
  };

  const clickHeader = (col) => {
    if (col.sortable === false) return;
    if (sort.key === col.key) setSort({ key: col.key, dir: sort.dir === "asc" ? "desc" : "asc" });
    else setSort({ key: col.key, dir: "asc" });
  };

  return (
    <div className={`hx-dt hx-dt-${density} ${stickyHeader ? "sticky" : ""}`}>
      <div className="hx-dt-scroll">
        <table>
          <thead>
            <tr>
              {selectable && (
                <th className="hx-dt-th-sel">
                  <SelHeadCheckbox checked={allSelected} indeterminate={someSelected} onChange={toggleAll}/>
                </th>
              )}
              {columns.map(c => {
                const sorted = sort.key === c.key;
                return (
                  <th key={c.key} style={{ width: c.w, textAlign: c.align || "left" }}
                      className={c.sortable !== false ? "sortable" : ""}
                      onClick={() => clickHeader(c)}>
                    <span className="hx-dt-th-inner">
                      {c.label}
                      {c.sortable !== false && (
                        <span className={`hx-dt-sort ${sorted ? "on" : ""} ${sorted ? sort.dir : ""}`}>
                          <svg width="10" height="12" viewBox="0 0 10 12" fill="none">
                            <path d="M5 0 L9 5 H1 Z" fill="currentColor" opacity={sorted && sort.dir === "asc" ? "1" : "0.25"}/>
                            <path d="M5 12 L9 7 H1 Z" fill="currentColor" opacity={sorted && sort.dir === "desc" ? "1" : "0.25"}/>
                          </svg>
                        </span>
                      )}
                    </span>
                  </th>
                );
              })}
            </tr>
          </thead>
          <tbody>
            {view.length === 0 ? (
              <tr><td colSpan={columns.length + (selectable ? 1 : 0)} className="hx-dt-empty">
                {empty || "No results."}
              </td></tr>
            ) : view.map((r, i) => {
              const k = r[keyField] != null ? r[keyField] : i;
              const isSel = selectable && _selected.has(k);
              return (
                <tr key={k}
                    className={`${isSel ? "selected" : ""} ${onRowClick ? "clickable" : ""} ${rowClassName ? rowClassName(r) : ""}`}
                    onClick={onRowClick ? (e) => {
                      if (e.target.closest("button, a, label, input, .hx-dt-cell-stop")) return;
                      onRowClick(r);
                    } : undefined}>
                  {selectable && (
                    <td className="hx-dt-td-sel" onClick={(e) => e.stopPropagation()}>
                      <Checkbox checked={isSel} onChange={() => toggleRow(k)}/>
                    </td>
                  )}
                  {columns.map(c => (
                    <td key={c.key}
                        style={{ textAlign: c.align || "left", fontFamily: c.mono ? "var(--mono)" : undefined, fontSize: c.mono ? 12.5 : undefined }}>
                      {c.render ? c.render(r) : r[c.key]}
                    </td>
                  ))}
                </tr>
              );
            })}
          </tbody>
        </table>
      </div>
      {(rowsPerPage > 0 || footer) && (
        <div className="hx-dt-foot">
          {footer || (
            <Pagination
              page={page} pages={totalPages}
              onPage={setPage}
              total={sortedRows.length}
              rangeStart={page * rowsPerPage}
              rangeEnd={Math.min((page + 1) * rowsPerPage, sortedRows.length)}
            />
          )}
        </div>
      )}
      <style>{`
        .hx-dt {
          border: 1px solid var(--line);
          border-radius: var(--r-md);
          background: var(--panel);
          overflow: hidden;
        }
        .hx-dt-scroll { overflow-x: auto; }
        .hx-dt table { width: 100%; border-collapse: collapse; font-size: 13.5px; }
        .hx-dt thead th {
          text-align: left;
          padding: 11px 16px;
          color: var(--muted); font-weight: 500; font-size: 12px;
          letter-spacing: .02em; text-transform: uppercase;
          background: var(--panel-2); border-bottom: 1px solid var(--line);
          user-select: none; white-space: nowrap;
        }
        .hx-dt thead th.sortable { cursor: pointer; transition: background .12s; }
        .hx-dt thead th.sortable:hover { background: var(--hover); color: var(--ink-2); }
        .hx-dt.sticky thead th { position: sticky; top: 0; z-index: 1; }
        .hx-dt-th-inner { display: inline-flex; align-items: center; gap: 6px; }
        .hx-dt-sort { display: inline-flex; color: var(--muted-2); }
        .hx-dt-sort.on { color: var(--accent); }

        .hx-dt tbody td {
          padding: 14px 16px; border-bottom: 1px solid var(--line-2);
          color: var(--ink-2); vertical-align: middle;
        }
        .hx-dt-compact tbody td, .hx-dt-compact thead th { padding: 8px 14px; }
        .hx-dt tbody tr:last-child td { border-bottom: none; }
        .hx-dt tbody tr.clickable { cursor: pointer; }
        .hx-dt tbody tr:hover { background: var(--hover); }
        .hx-dt tbody tr.selected { background: color-mix(in oklab, var(--accent) 6%, var(--panel)); }
        .hx-dt-empty { text-align: center !important; color: var(--muted); padding: 56px 20px !important; }
        .hx-dt-th-sel, .hx-dt-td-sel { width: 44px; padding-left: 16px !important; padding-right: 0 !important; }

        .hx-dt-foot { padding: 10px 16px; border-top: 1px solid var(--line-2); background: var(--panel-2); }
      `}</style>
    </div>
  );
};

const SelHeadCheckbox = ({ checked, indeterminate, onChange }) => {
  const ref = React.useRef(null);
  React.useEffect(() => { if (ref.current) ref.current.indeterminate = !!indeterminate; }, [indeterminate]);
  return (
    <label className="hx-cb hx-cb-headcell">
      <span className={`hx-cb-box ${checked ? "on" : indeterminate ? "indet" : ""}`}>
        {checked && <IconCheck size={11}/>}
        {!checked && indeterminate && <span className="hx-cb-dash"/>}
      </span>
      <input ref={ref} type="checkbox" checked={checked}
             onChange={(e) => onChange(e.target.checked)}
             style={{ position: "absolute", opacity: 0, pointerEvents: "none" }}/>
      <style>{`
        .hx-cb-headcell .hx-cb-box.indet { background: var(--accent); border-color: var(--accent); }
        .hx-cb-dash { width: 9px; height: 1.5px; background: white; display: block; }
      `}</style>
    </label>
  );
};

// ---------- Pagination ----------
const Pagination = ({ page, pages, onPage, total, rangeStart, rangeEnd }) => (
  <div className="hx-pgn">
    <span className="hx-pgn-info">
      {total != null && rangeEnd != null
        ? <>Showing <strong>{rangeStart + 1}</strong>–<strong>{rangeEnd}</strong> of <strong>{total}</strong></>
        : <>Page <strong>{page + 1}</strong> of <strong>{pages}</strong></>}
    </span>
    <div className="hx-pgn-ctrls">
      <button type="button" onClick={() => onPage(0)} disabled={page === 0} aria-label="First page">⟪</button>
      <button type="button" onClick={() => onPage(Math.max(0, page - 1))} disabled={page === 0} aria-label="Previous">‹</button>
      <span className="hx-pgn-page">{page + 1} / {pages}</span>
      <button type="button" onClick={() => onPage(Math.min(pages - 1, page + 1))} disabled={page >= pages - 1} aria-label="Next">›</button>
      <button type="button" onClick={() => onPage(pages - 1)} disabled={page >= pages - 1} aria-label="Last page">⟫</button>
    </div>
    <style>{`
      .hx-pgn { display: flex; justify-content: space-between; align-items: center; gap: 16px; flex-wrap: wrap; }
      .hx-pgn-info { font-size: 12.5px; color: var(--muted); }
      .hx-pgn-info strong { color: var(--ink); font-weight: 600; font-variant-numeric: tabular-nums; }
      .hx-pgn-ctrls { display: inline-flex; gap: 4px; align-items: center; }
      .hx-pgn-ctrls button {
        width: 30px; height: 30px;
        background: var(--panel); border: 1px solid var(--line); border-radius: 6px;
        cursor: pointer; color: var(--ink-2); font: inherit; font-size: 14px;
        display: inline-flex; align-items: center; justify-content: center;
        transition: background .12s, border-color .12s;
      }
      .hx-pgn-ctrls button:hover:not(:disabled) { background: var(--hover); border-color: color-mix(in oklab, var(--ink) 18%, var(--line)); }
      .hx-pgn-ctrls button:disabled { opacity: .4; cursor: not-allowed; }
      .hx-pgn-page { font-size: 12.5px; color: var(--ink-2); padding: 0 8px; font-variant-numeric: tabular-nums; }
    `}</style>
  </div>
);

Object.assign(window, { DataTable, Pagination });
