"use client";

// src/components/data-table/use-table-state.ts
import * as React from "react";

// src/lib/date-filter.ts
function parseRowDateToYmd(raw) {
  const t = raw.trim();
  if (!t || t === "\u2014" || t === "-") return null;
  const d = new Date(t);
  if (Number.isNaN(d.getTime())) return null;
  const y = d.getFullYear();
  const m = String(d.getMonth() + 1).padStart(2, "0");
  const day = String(d.getDate()).padStart(2, "0");
  return `${y}-${m}-${day}`;
}

// src/components/data-table/use-table-state.ts
var _filterId = 0;
function nextFilterId() {
  return `f-${++_filterId}`;
}
var REFLOW_VIEWPORT_MQ = "(max-height: 640px)";
function subscribeReflowViewport(callback) {
  if (typeof window === "undefined") return () => {
  };
  const mql = window.matchMedia(REFLOW_VIEWPORT_MQ);
  mql.addEventListener("change", callback);
  return () => mql.removeEventListener("change", callback);
}
function getReflowViewportSnapshot() {
  if (typeof window === "undefined") return false;
  return window.matchMedia(REFLOW_VIEWPORT_MQ).matches;
}
function getServerReflowViewportSnapshot() {
  return false;
}
function digitsOnly(s) {
  return s.replace(/\D/g, "");
}
function buildDefaultWidths(columns) {
  const map = {};
  for (const col of columns) {
    if (col.width !== void 0) map[col.key] = col.width;
  }
  return map;
}
function buildDefaultPins(columns) {
  const map = {};
  for (const col of columns) {
    if (col.defaultPin) map[col.key] = col.defaultPin;
  }
  return map;
}
function compareUnknownSort(a, b) {
  if (a === b) return 0;
  if (a == null && b == null) return 0;
  if (a == null) return 1;
  if (b == null) return -1;
  if (typeof a === "number" && typeof b === "number") return a < b ? -1 : a > b ? 1 : 0;
  if (typeof a === "string" && typeof b === "string") return a < b ? -1 : a > b ? 1 : 0;
  const as = String(a);
  const bs = String(b);
  return as < bs ? -1 : as > bs ? 1 : 0;
}
function buildLockedPins(columns) {
  const map = {};
  for (const col of columns) {
    if (col.lockPin && col.defaultPin) map[col.key] = col.defaultPin;
  }
  return map;
}
function useTableState(data, columns, defaultSort, paginationOverride, syncedSearchFromUrl) {
  const [sortRules, setSortRules] = React.useState(() => {
    if (defaultSort) {
      return [{ id: "sort-default", fieldKey: defaultSort.key, direction: defaultSort.dir }];
    }
    return [];
  });
  const primarySort = sortRules[0] ?? null;
  const sortKey = primarySort?.fieldKey ?? "";
  const sortDir = primarySort?.direction ?? "asc";
  const addSortRule = React.useCallback((fieldKey) => {
    setSortRules((prev) => {
      if (prev.some((r) => r.fieldKey === fieldKey)) return prev;
      return [{ id: `sort-${Date.now()}`, fieldKey, direction: "asc" }, ...prev];
    });
  }, [setSortRules]);
  const removeSortRule = React.useCallback((id) => {
    setSortRules((prev) => prev.filter((r) => r.id !== id));
  }, [setSortRules]);
  const toggleSortDir = React.useCallback((id) => {
    setSortRules((prev) => prev.map(
      (r) => r.id === id ? { ...r, direction: r.direction === "asc" ? "desc" : "asc" } : r
    ));
  }, [setSortRules]);
  const handleSortByKey = React.useCallback((colKey) => {
    setSortRules((prev) => {
      const idx = prev.findIndex((r) => r.fieldKey === colKey);
      if (idx === 0) {
        return prev.map((r, i) => i === 0 ? { ...r, direction: r.direction === "asc" ? "desc" : "asc" } : r);
      }
      const filtered = prev.filter((r) => r.fieldKey !== colKey);
      return [{ id: `sort-${Date.now()}`, fieldKey: colKey, direction: "asc" }, ...filtered];
    });
  }, [setSortRules]);
  const [search, setSearch] = React.useState(
    () => syncedSearchFromUrl !== void 0 ? syncedSearchFromUrl.trim() : ""
  );
  const [searchOpen, setSearchOpen] = React.useState(
    () => syncedSearchFromUrl !== void 0 && Boolean(syncedSearchFromUrl.trim())
  );
  const searchRef = React.useRef(null);
  const [activeFilters, setActiveFilters] = React.useState([]);
  const [filterConnectors, setFilterConnectors] = React.useState({});
  const [openFilterId, setOpenFilterId] = React.useState(null);
  const [filterBarVisible, setFilterBarVisible] = React.useState(true);
  const [drawerExpandedFilters, setDrawerExpandedFilters] = React.useState(/* @__PURE__ */ new Set());
  React.useEffect(() => {
    if (syncedSearchFromUrl === void 0) return;
    const next = syncedSearchFromUrl.trim();
    setSearch(next);
    setSearchOpen(next.length > 0);
  }, [syncedSearchFromUrl]);
  const toggleConnector = React.useCallback((leftId) => {
    setFilterConnectors((prev) => ({ ...prev, [leftId]: prev[leftId] === "or" ? "and" : "or" }));
  }, [setFilterConnectors]);
  function getConnector(leftId) {
    return filterConnectors[leftId] ?? "and";
  }
  const addFilter = React.useCallback((fieldKey, fromDrawer = false) => {
    const col = columns.find((c) => c.key === fieldKey);
    if (!col?.filter) return;
    const id = nextFilterId();
    const f = col.filter;
    const firstOperator = (() => {
      if (f.type === "select" || f.type === "date") {
        const pick = f.operators?.find((o) => o === "is" || o === "is_not");
        return pick ?? "is";
      }
      return f.operators?.[0] ?? "contains";
    })();
    const newFilter = { id, fieldKey, operator: firstOperator, values: [] };
    setActiveFilters((prev) => [...prev, newFilter]);
    if (fromDrawer) {
      setDrawerExpandedFilters(() => /* @__PURE__ */ new Set([id]));
    } else {
      setOpenFilterId(id);
      setFilterBarVisible(true);
    }
  }, [columns, setActiveFilters, setDrawerExpandedFilters, setOpenFilterId, setFilterBarVisible]);
  const updateFilter = React.useCallback((id, patch) => {
    let shouldShowFilterBar = false;
    setActiveFilters((prev) => {
      const next = prev.map((f) => {
        if (f.id !== id) return f;
        const merged = { ...f, ...patch };
        const col = columns.find((c) => c.key === merged.fieldKey);
        if (merged.values.length > 0) {
          shouldShowFilterBar = col?.filter?.type === "text" ? (merged.values[0] ?? "").trim().length > 0 : true;
        }
        return merged;
      });
      return next;
    });
    if (shouldShowFilterBar) setFilterBarVisible(true);
  }, [columns, setActiveFilters, setFilterBarVisible]);
  const removeFilter = React.useCallback((id) => {
    setActiveFilters((prev) => {
      const idx = prev.findIndex((f) => f.id === id);
      const next = prev.filter((f) => f.id !== id);
      setFilterConnectors((prevC) => {
        const c = { ...prevC };
        if (idx > 0 && next.length > 0) {
          const leftId = prev[idx - 1].id;
          c[leftId] = prevC[id] ?? prevC[leftId] ?? "and";
        }
        delete c[id];
        return c;
      });
      return next;
    });
    setOpenFilterId((prev) => prev === id ? null : prev);
  }, [setActiveFilters, setFilterConnectors, setOpenFilterId]);
  const [groupBy, setGroupBy] = React.useState(null);
  const [colMenuSearch, setColMenuSearch] = React.useState({});
  const [selected, setSelected] = React.useState(/* @__PURE__ */ new Set());
  const [colWidths, setColWidths] = React.useState(() => buildDefaultWidths(columns));
  const resizeRef = React.useRef(null);
  const [colOrder, setColOrder] = React.useState(() => columns.map((c) => c.key));
  const [colPins, setColPins] = React.useState(() => buildDefaultPins(columns));
  const lockedPins = React.useMemo(() => buildLockedPins(columns), [columns]);
  const [colWrap, setColWrap] = React.useState({});
  const [sheetOpen, setSheetOpen] = React.useState(false);
  const [sheetInitialPanel, setSheetInitialPanel] = React.useState(null);
  const [showGridlines, setShowGridlines] = React.useState(true);
  const [rowHeight, setRowHeight] = React.useState("default");
  const [hiddenCols, setHiddenCols] = React.useState(/* @__PURE__ */ new Set());
  const toggleColVisibility = React.useCallback((key) => {
    setHiddenCols((prev) => {
      const next = new Set(prev);
      if (next.has(key)) next.delete(key);
      else next.add(key);
      return next;
    });
  }, [setHiddenCols]);
  const moveCol = React.useCallback((key, dir) => {
    setColOrder((prev) => {
      const lockedLeft = columns.filter((c) => c.lockPin && c.defaultPin === "left").map((c) => c.key);
      const lockedRight = columns.filter((c) => c.lockPin && c.defaultPin === "right").map((c) => c.key);
      const orderable = prev.filter((k) => !lockedLeft.includes(k) && !lockedRight.includes(k));
      const idx = orderable.indexOf(key);
      if (dir === "up" && idx <= 0) return prev;
      if (dir === "down" && idx >= orderable.length - 1) return prev;
      const next = [...orderable];
      const swap = dir === "up" ? idx - 1 : idx + 1;
      [next[idx], next[swap]] = [next[swap], next[idx]];
      return [...lockedLeft, ...next, ...lockedRight];
    });
  }, [columns, setColOrder]);
  const draggedKey = React.useRef(null);
  const [dragOverKey, setDragOverKey] = React.useState(null);
  const scrollRef = React.useRef(null);
  const [scrolled, setScrolled] = React.useState(false);
  const [scrollEnd, setScrollEnd] = React.useState(false);
  const [isOverflowing, setIsOverflowing] = React.useState(false);
  const isReflowViewport = React.useSyncExternalStore(
    subscribeReflowViewport,
    getReflowViewportSnapshot,
    getServerReflowViewportSnapshot
  );
  const [hoveredRow, setHoveredRow] = React.useState(null);
  const columnsByKey = React.useMemo(() => {
    const map = /* @__PURE__ */ new Map();
    for (const col of columns) map.set(col.key, col);
    return map;
  }, [columns]);
  const searchableTextCache = React.useRef(/* @__PURE__ */ new WeakMap());
  const getSearchableText = React.useCallback((row) => {
    const cache = searchableTextCache.current;
    const cached = cache.get(row);
    if (cached !== void 0) return cached;
    let blob = "";
    for (const v of Object.values(row)) {
      if (v == null) continue;
      blob += String(v).toLowerCase() + "\n";
    }
    cache.set(row, blob);
    return blob;
  }, []);
  const lowerValueCache = React.useRef(/* @__PURE__ */ new WeakMap());
  const getLowerValue = React.useCallback((row, key) => {
    const wm = lowerValueCache.current;
    let perRow = wm.get(row);
    if (!perRow) {
      perRow = /* @__PURE__ */ new Map();
      wm.set(row, perRow);
    }
    const cached = perRow.get(key);
    if (cached !== void 0) return cached;
    const computed = String(row[key] ?? "").toLowerCase();
    perRow.set(key, computed);
    return computed;
  }, []);
  React.useEffect(() => {
    searchableTextCache.current = /* @__PURE__ */ new WeakMap();
    lowerValueCache.current = /* @__PURE__ */ new WeakMap();
  }, [data]);
  const rows = React.useMemo(() => {
    let result = data.slice();
    const q = search.trim().toLowerCase();
    if (q) {
      result = result.filter((r) => getSearchableText(r).includes(q));
    }
    const activeWithValues = activeFilters.filter((f) => {
      if (f.values.length === 0) return false;
      const col = columnsByKey.get(f.fieldKey);
      if (col?.filter?.type === "text") {
        return (f.values[0] ?? "").trim().length > 0;
      }
      return true;
    });
    if (activeWithValues.length > 0) {
      const compiled = [];
      for (const f of activeWithValues) {
        const col = columnsByKey.get(f.fieldKey);
        if (!col?.filter) continue;
        if (col.filter.type === "select") {
          compiled.push({
            col,
            id: f.id,
            type: "select",
            operator: f.operator,
            selectValues: new Set(f.values)
          });
        } else if (col.filter.type === "date") {
          compiled.push({
            col,
            id: f.id,
            type: "date",
            operator: f.operator,
            dateTarget: f.values[0]
          });
        } else {
          const raw = f.values[0] ?? "";
          const isDigitsMask = col.filter.textMask === "phone" || col.filter.textMask === "zip";
          compiled.push({
            col,
            id: f.id,
            type: "text",
            operator: f.operator,
            isDigitsMask,
            digitsNeedle: isDigitsMask ? digitsOnly(raw) : void 0,
            textNeedle: !isDigitsMask ? raw.toLowerCase() : void 0
          });
        }
      }
      const matchesCompiled = (r, f) => {
        const rowVal = String(r[f.col.key] ?? "");
        if (f.type === "select") {
          const hit = f.selectValues.has(rowVal);
          return f.operator === "is" ? hit : !hit;
        }
        if (f.type === "date") {
          if (!f.dateTarget) return true;
          const rowYmd = parseRowDateToYmd(rowVal);
          const op = f.operator === "is_not" ? "is_not" : "is";
          if (rowYmd === null) return op === "is_not";
          return op === "is" ? rowYmd === f.dateTarget : rowYmd !== f.dateTarget;
        }
        if (f.isDigitsMask) {
          if (!f.digitsNeedle) return true;
          const hay2 = digitsOnly(rowVal);
          return f.operator === "contains" ? hay2.includes(f.digitsNeedle) : !hay2.includes(f.digitsNeedle);
        }
        if (!f.textNeedle) return true;
        const hay = getLowerValue(r, f.col.key);
        return f.operator === "contains" ? hay.includes(f.textNeedle) : !hay.includes(f.textNeedle);
      };
      if (compiled.length > 0) {
        result = result.filter((r) => {
          let res = matchesCompiled(r, compiled[0]);
          for (let i = 1; i < compiled.length; i++) {
            const connector = filterConnectors[compiled[i - 1].id] ?? "and";
            const match = matchesCompiled(r, compiled[i]);
            res = connector === "and" ? res && match : res || match;
          }
          return res;
        });
      }
    }
    const colMenuEntries = [];
    for (const [key, raw] of Object.entries(colMenuSearch)) {
      const trimmed = raw.trim();
      if (trimmed) colMenuEntries.push({ key, lower: trimmed.toLowerCase() });
    }
    if (colMenuEntries.length > 0) {
      result = result.filter((r) => {
        for (const { key, lower } of colMenuEntries) {
          if (!getLowerValue(r, key).includes(lower)) return false;
        }
        return true;
      });
    }
    if (sortRules.length > 0) {
      const resolved = [];
      for (const rule of sortRules) {
        const col = columnsByKey.get(rule.fieldKey);
        const sk = col?.sortKey ?? col?.key;
        if (sk) resolved.push({ sk, dir: rule.direction });
      }
      if (resolved.length > 0) {
        result.sort((a, b) => {
          for (let i = 0; i < resolved.length; i++) {
            const { sk, dir } = resolved[i];
            const cmp = compareUnknownSort(a[sk], b[sk]);
            if (cmp !== 0) return dir === "asc" ? cmp : -cmp;
          }
          return 0;
        });
      }
    }
    return result;
  }, [
    data,
    search,
    activeFilters,
    filterConnectors,
    colMenuSearch,
    sortRules,
    columnsByKey,
    getSearchableText,
    getLowerValue
  ]);
  const pagedRows = React.useMemo(() => {
    if (!paginationOverride || paginationOverride.pageSize <= 0) return rows;
    const { page, pageSize } = paginationOverride;
    const safePage = Math.max(1, page);
    return rows.slice((safePage - 1) * pageSize, safePage * pageSize);
  }, [rows, paginationOverride?.page, paginationOverride?.pageSize]);
  const groupedRows = React.useMemo(() => {
    if (!groupBy) return [{ groupKey: null, groupLabel: null, rows }];
    const groups = /* @__PURE__ */ new Map();
    rows.forEach((row) => {
      const val = String(row[groupBy] ?? "\u2014");
      if (!groups.has(val)) groups.set(val, []);
      groups.get(val).push(row);
    });
    return [...groups.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([key, groupRows]) => ({ groupKey: key, groupLabel: key, rows: groupRows }));
  }, [rows, groupBy]);
  const displayCols = React.useMemo(() => {
    const leftPinned = [];
    const free = [];
    const rightPinned = [];
    for (const k of colOrder) {
      const pin = colPins[k];
      if (pin === "left") leftPinned.push(k);
      else if (pin === "right") rightPinned.push(k);
      else free.push(k);
    }
    const ordered = [...leftPinned, ...free, ...rightPinned];
    const out = [];
    for (const k of ordered) {
      if (hiddenCols.has(k)) continue;
      const col = columnsByKey.get(k);
      if (col) out.push(col);
    }
    return out;
  }, [colOrder, colPins, hiddenCols, columnsByKey]);
  const totalWidth = React.useMemo(
    () => displayCols.reduce((s, c) => s + (colWidths[c.key] ?? c.width ?? 100), 0),
    [displayCols, colWidths]
  );
  const LOCKED_KEYS = React.useMemo(() => new Set(Object.keys(lockedPins)), [lockedPins]);
  const effectivePins = React.useMemo(() => {
    if (isReflowViewport || !isOverflowing) return {};
    const result = {};
    for (const [key, pin] of Object.entries(colPins)) {
      result[key] = pin;
    }
    return result;
  }, [colPins, isOverflowing, isReflowViewport]);
  function startResize(key, e) {
    e.preventDefault();
    e.stopPropagation();
    const minW = columns.find((c) => c.key === key)?.minWidth ?? 60;
    const startW = colWidths[key] ?? (columns.find((c) => c.key === key)?.width ?? 100);
    resizeRef.current = { key, startX: e.clientX, startW };
    const onMove = (ev) => {
      if (!resizeRef.current) return;
      const { key: k, startX, startW: sw } = resizeRef.current;
      setColWidths((p) => ({ ...p, [k]: Math.max(minW, sw + ev.clientX - startX) }));
    };
    const onUp = () => {
      resizeRef.current = null;
      document.removeEventListener("mousemove", onMove);
      document.removeEventListener("mouseup", onUp);
    };
    document.addEventListener("mousemove", onMove);
    document.addEventListener("mouseup", onUp);
  }
  function handleDragStart(key, e) {
    draggedKey.current = key;
    e.dataTransfer.effectAllowed = "move";
  }
  function handleDragOver(key, e) {
    e.preventDefault();
    e.dataTransfer.dropEffect = "move";
    if (draggedKey.current && draggedKey.current !== key) setDragOverKey(key);
  }
  function handleDrop(key) {
    if (!draggedKey.current || draggedKey.current === key) {
      setDragOverKey(null);
      return;
    }
    const order = [...colOrder];
    const from = order.indexOf(draggedKey.current);
    const to = order.indexOf(key);
    order.splice(from, 1);
    order.splice(to, 0, draggedKey.current);
    setColOrder(order);
    draggedKey.current = null;
    setDragOverKey(null);
  }
  function handleDragEnd() {
    draggedKey.current = null;
    setDragOverKey(null);
  }
  function pinColumn(key, pin) {
    setColPins((p) => ({ ...p, [key]: pin }));
  }
  function unpinColumn(key) {
    if (lockedPins[key]) return;
    setColPins((p) => {
      const n = { ...p };
      delete n[key];
      return n;
    });
  }
  function toggleWrap(key) {
    setColWrap((p) => ({ ...p, [key]: !p[key] }));
  }
  const checkOverflow = React.useCallback(() => {
    const el = scrollRef.current;
    if (!el) return;
    setIsOverflowing(totalWidth > el.clientWidth + 1);
  }, [totalWidth]);
  function handleScroll() {
    const el = scrollRef.current;
    if (!el) return;
    setScrolled(el.scrollLeft > 1);
    setScrollEnd(el.scrollLeft >= el.scrollWidth - el.clientWidth - 1);
    setIsOverflowing(totalWidth > el.clientWidth + 1);
  }
  React.useLayoutEffect(() => {
    checkOverflow();
  }, [checkOverflow]);
  function getRowId(row, index, getIdFn) {
    return getIdFn ? getIdFn(row, index) : row.id ?? index;
  }
  const toggleRow = React.useCallback((id) => {
    setSelected((prev) => {
      const next = new Set(prev);
      if (next.has(id)) next.delete(id);
      else next.add(id);
      return next;
    });
  }, [setSelected]);
  const toggleAll = React.useCallback((allRowIds) => {
    setSelected((prev) => prev.size === allRowIds.length ? /* @__PURE__ */ new Set() : new Set(allRowIds));
  }, [setSelected]);
  const stickyOffsets = React.useMemo(() => {
    const left = /* @__PURE__ */ new Map();
    const right = /* @__PURE__ */ new Map();
    let leftOffset = 0;
    for (const col of displayCols) {
      if (effectivePins[col.key] !== "left") break;
      left.set(col.key, leftOffset);
      leftOffset += colWidths[col.key] ?? col.width ?? 100;
    }
    let rightOffset = 0;
    for (let i = displayCols.length - 1; i >= 0; i--) {
      const col = displayCols[i];
      if (effectivePins[col.key] !== "right") break;
      right.set(col.key, rightOffset);
      rightOffset += colWidths[col.key] ?? col.width ?? 100;
    }
    return { left, right };
  }, [displayCols, effectivePins, colWidths]);
  const getStickyLeft = React.useCallback((key) => {
    return stickyOffsets.left.get(key) ?? 0;
  }, [stickyOffsets]);
  const getStickyRight = React.useCallback((key) => {
    return stickyOffsets.right.get(key) ?? 0;
  }, [stickyOffsets]);
  const stickyStyle = React.useCallback(
    (key, isHeader = false) => {
      if (isReflowViewport) return {};
      const pin = effectivePins[key];
      if (pin === "left") {
        return isHeader ? { position: "sticky", left: stickyOffsets.left.get(key) ?? 0, top: 0 } : { position: "sticky", left: stickyOffsets.left.get(key) ?? 0 };
      }
      if (pin === "right") {
        return isHeader ? { position: "sticky", right: stickyOffsets.right.get(key) ?? 0, top: 0 } : { position: "sticky", right: stickyOffsets.right.get(key) ?? 0 };
      }
      return isHeader ? { position: "sticky", top: 0 } : {};
    },
    [effectivePins, isReflowViewport, stickyOffsets]
  );
  return {
    // Sort
    sortRules,
    setSortRules,
    sortKey,
    sortDir,
    addSortRule,
    removeSortRule,
    toggleSortDir,
    handleSortByKey,
    // Filters
    search,
    setSearch,
    searchOpen,
    setSearchOpen,
    searchRef,
    activeFilters,
    setActiveFilters,
    filterConnectors,
    setFilterConnectors,
    toggleConnector,
    getConnector,
    openFilterId,
    setOpenFilterId,
    filterBarVisible,
    setFilterBarVisible,
    drawerExpandedFilters,
    setDrawerExpandedFilters,
    addFilter,
    updateFilter,
    removeFilter,
    // Group
    groupBy,
    setGroupBy,
    // Column quick-search
    colMenuSearch,
    setColMenuSearch,
    // Selection
    selected,
    setSelected,
    toggleRow,
    toggleAll,
    getRowId,
    // Column widths / order / pins / wrap
    colWidths,
    setColWidths,
    resizeRef,
    startResize,
    colOrder,
    setColOrder,
    moveCol,
    colPins,
    setColPins,
    lockedPins,
    LOCKED_KEYS,
    pinColumn,
    unpinColumn,
    colWrap,
    setColWrap,
    toggleWrap,
    // Drag-to-reorder
    draggedKey,
    dragOverKey,
    handleDragStart,
    handleDragOver,
    handleDrop,
    handleDragEnd,
    // Scroll
    scrollRef,
    scrolled,
    scrollEnd,
    isOverflowing,
    checkOverflow,
    handleScroll,
    // Hover
    hoveredRow,
    setHoveredRow,
    // Derived
    rows,
    pagedRows,
    groupedRows,
    effectivePins,
    displayCols,
    isReflowViewport,
    getStickyLeft,
    getStickyRight,
    stickyStyle,
    totalWidth,
    // Display settings
    sheetOpen,
    setSheetOpen,
    sheetInitialPanel,
    setSheetInitialPanel,
    showGridlines,
    setShowGridlines,
    rowHeight,
    setRowHeight,
    hiddenCols,
    setHiddenCols,
    toggleColVisibility
  };
}
export {
  useTableState
};
//# sourceMappingURL=use-table-state.js.map