// Client-side list search + column sort helpers for dashboard tables.
// Babel CDN (no ES modules) — exported on window like shared.jsx.

const { useState } = React;

function getFieldValue(item, field) {
  if (typeof field === "function") return field(item);
  if (typeof field !== "string" || !field) return undefined;
  return field.split(".").reduce((acc, key) => (acc == null ? acc : acc[key]), item);
}

function fieldToString(item, field) {
  const value = getFieldValue(item, field);
  if (value == null) return "";
  if (typeof value === "object") return "";
  return String(value);
}

function queryNeedles(query) {
  const raw = String(query || "").trim();
  if (!raw) return [];
  const needles = [raw.toLowerCase()];
  const tmeC = raw.match(/t\.me\/c\/(\d+)/i);
  if (tmeC) needles.push(tmeC[1]);
  const prefixed = raw.match(/-?100(\d{6,})/);
  if (prefixed) needles.push(prefixed[1]);
  return Array.from(new Set(needles));
}

function matchQuery(item, query, fields) {
  const q = String(query || "").trim();
  if (!q) return true;
  if (!fields || !fields.length) return true;
  const needles = queryNeedles(q);
  return needles.some((needle) => fields.some((field) => (
    fieldToString(item, field).toLowerCase().includes(needle.toLowerCase())
  )));
}

function toSortValue(raw) {
  if (raw == null || raw === "") return { empty: true, v: null, type: "empty" };
  if (typeof raw === "number") {
    if (!Number.isFinite(raw)) return { empty: true, v: null, type: "empty" };
    return { empty: false, v: raw, type: "number" };
  }
  if (typeof raw === "boolean") return { empty: false, v: raw ? 1 : 0, type: "number" };
  if (raw instanceof Date) {
    const t = raw.getTime();
    if (!Number.isFinite(t)) return { empty: true, v: null, type: "empty" };
    return { empty: false, v: t, type: "number" };
  }
  const s = String(raw);
  if (/^-?\d+(\.\d+)?$/.test(s.trim())) {
    const n = Number(s);
    if (Number.isFinite(n)) return { empty: false, v: n, type: "number" };
  }
  return { empty: false, v: s.toLowerCase(), type: "string" };
}

function sortItems(items, { key, dir, getters } = {}) {
  const list = Array.isArray(items) ? items.slice() : [];
  const getter = (getters && key && typeof getters[key] === "function")
    ? getters[key]
    : (row) => (key ? row[key] : null);
  const mul = dir === "desc" ? -1 : 1;
  list.sort((a, b) => {
    const av = toSortValue(getter(a));
    const bv = toSortValue(getter(b));
    if (av.empty && bv.empty) return 0;
    if (av.empty) return 1;
    if (bv.empty) return -1;
    if (av.type === "number" && bv.type === "number") return (av.v - bv.v) * mul;
    return String(av.v).localeCompare(String(bv.v), undefined, { numeric: true, sensitivity: "base" }) * mul;
  });
  return list;
}

function objectIdTimeMs(id) {
  const hex = id && typeof id === "object" && typeof id.toString === "function"
    ? id.toString()
    : String(id || "");
  if (!/^[a-fA-F0-9]{24}$/.test(hex)) return null;
  const t = parseInt(hex.slice(0, 8), 16) * 1000;
  return Number.isFinite(t) ? t : null;
}

function modifiedAtMs(item) {
  if (item == null || typeof item !== "object") return null;
  const raw = item.updated_at || item.updatedAt || item.created_at || item.createdAt;
  if (raw != null && raw !== "") {
    const t = raw instanceof Date ? raw.getTime() : Date.parse(raw);
    if (Number.isFinite(t)) return t;
  }
  return objectIdTimeMs(item._id);
}

function formatModifiedDate(item) {
  const t = modifiedAtMs(item);
  if (t == null) return "—";
  const d = new Date(t);
  const pad = (n) => String(n).padStart(2, "0");
  return `${pad(d.getDate())}/${pad(d.getMonth() + 1)}/${d.getFullYear()} ${pad(d.getHours())}:${pad(d.getMinutes())}`;
}

function useListSort(defaultKey, defaultDir = "asc") {
  const [sortKey, setSortKey] = useState(defaultKey);
  const [sortDir, setSortDir] = useState(defaultDir);
  const toggleSort = (column) => {
    if (column === sortKey) {
      setSortDir((d) => (d === "asc" ? "desc" : "asc"));
    } else {
      setSortKey(column);
      setSortDir("asc");
    }
  };
  return { sortKey, sortDir, toggleSort };
}

const SortableTh = ({ label, column, sortKey, sortDir, onToggle, className, align }) => {
  const active = sortKey === column;
  const ariaSort = !active ? "none" : (sortDir === "desc" ? "descending" : "ascending");
  const cls = ["sortable", className, active ? "sorted" : ""].filter(Boolean).join(" ");
  return (
    <th className={cls} aria-sort={ariaSort} style={align === "right" ? { textAlign: "right" } : undefined}>
      <button type="button" className="sort-btn" onClick={() => onToggle(column)}>
        <span>{label}</span>
        {active
          ? (sortDir === "desc"
            ? <I.ArrowDown size={12} className="sort-caret" />
            : <I.ArrowUp size={12} className="sort-caret" />)
          : <I.ArrowUp size={12} className="sort-caret idle" />}
      </button>
    </th>
  );
};

const ListSearch = ({ value = "", onChange, placeholder = "Search…" }) => (
  <div className="search list-search">
    <I.Search className="icon" />
    <input
      className="input"
      placeholder={placeholder}
      value={value}
      onChange={(e) => onChange?.(e.target.value)}
    />
  </div>
);

Object.assign(window, { matchQuery, sortItems, useListSort, SortableTh, modifiedAtMs, formatModifiedDate, ListSearch });
