import { useEventBus } from './chunk-YXZM3WCF.js';
import { en_default } from './chunk-TSETXL2E.js';
import React2, { createContext, useRef, useEffect, useCallback, useContext, useState, useMemo } from 'react';
import { jsx } from 'react/jsx-runtime';

function useInfiniteScroll(onLoadMore, options = {}) {
  const { rootMargin = "200px", hasMore = true, isLoading = false } = options;
  const observerRef = useRef(null);
  const callbackRef = useRef(onLoadMore);
  callbackRef.current = onLoadMore;
  const hasMoreRef = useRef(hasMore);
  hasMoreRef.current = hasMore;
  const isLoadingRef = useRef(isLoading);
  isLoadingRef.current = isLoading;
  useEffect(() => {
    return () => {
      observerRef.current?.disconnect();
    };
  }, []);
  const sentinelRef = useCallback((node) => {
    observerRef.current?.disconnect();
    if (!node) return;
    observerRef.current = new IntersectionObserver(
      (entries) => {
        const entry = entries[0];
        if (entry.isIntersecting && hasMoreRef.current && !isLoadingRef.current) {
          callbackRef.current();
        }
      },
      { rootMargin }
    );
    observerRef.current.observe(node);
  }, [rootMargin]);
  return { sentinelRef };
}
var { $meta: _meta, ...coreMessages } = en_default;
var coreLocale = coreMessages;
var I18nContext = createContext({
  locale: "en",
  direction: "ltr",
  t: (key) => coreLocale[key] ?? key
  // core locale fallback
});
I18nContext.displayName = "I18nContext";
var I18nProvider = I18nContext.Provider;
function useTranslate() {
  return useContext(I18nContext);
}
function createTranslate(messages) {
  return (key, params) => {
    let msg = messages[key] ?? coreLocale[key] ?? key;
    if (params) {
      for (const [k, v] of Object.entries(params)) {
        msg = msg.split(`{{${k}}}`).join(String(v));
      }
    }
    return msg;
  };
}
var queryStores = /* @__PURE__ */ new Map();
function getOrCreateStore(query) {
  if (!queryStores.has(query)) {
    queryStores.set(query, {
      search: "",
      filters: {},
      sortField: void 0,
      sortDirection: void 0,
      listeners: /* @__PURE__ */ new Set()
    });
  }
  return queryStores.get(query);
}
function useQuerySingleton(query) {
  const [, forceUpdate] = useState({});
  if (!query) {
    return null;
  }
  const store = useMemo(() => getOrCreateStore(query), [query]);
  useMemo(() => {
    const listener = () => forceUpdate({});
    store.listeners.add(listener);
    return () => {
      store.listeners.delete(listener);
    };
  }, [store]);
  const notifyListeners = useCallback(() => {
    store.listeners.forEach((listener) => listener());
  }, [store]);
  const setSearch = useCallback((value) => {
    store.search = value;
    notifyListeners();
  }, [store, notifyListeners]);
  const setFilter = useCallback((key, value) => {
    store.filters = { ...store.filters, [key]: value };
    notifyListeners();
  }, [store, notifyListeners]);
  const clearFilters = useCallback(() => {
    store.filters = {};
    store.search = "";
    notifyListeners();
  }, [store, notifyListeners]);
  const setSort = useCallback((field, direction) => {
    store.sortField = field;
    store.sortDirection = direction;
    notifyListeners();
  }, [store, notifyListeners]);
  return {
    search: store.search,
    setSearch,
    filters: store.filters,
    setFilter,
    clearFilters,
    sortField: store.sortField,
    sortDirection: store.sortDirection,
    setSort
  };
}
function parseQueryBinding(binding) {
  const cleaned = binding.startsWith("@") ? binding.slice(1) : binding;
  const parts = cleaned.split(".");
  return {
    query: parts[0],
    field: parts.length > 1 ? parts.slice(1).join(".") : void 0
  };
}
function useLongPress(onLongPress, options = {}) {
  const { duration = 500, moveThreshold = 10 } = options;
  const timerRef = useRef(null);
  const startPos = useRef({ x: 0, y: 0 });
  const isPressedRef = useRef(false);
  const firedRef = useRef(false);
  const cancel = useCallback(() => {
    if (timerRef.current) {
      clearTimeout(timerRef.current);
      timerRef.current = null;
    }
    isPressedRef.current = false;
  }, []);
  const onPointerDown = useCallback((e) => {
    firedRef.current = false;
    startPos.current = { x: e.clientX, y: e.clientY };
    isPressedRef.current = true;
    timerRef.current = setTimeout(() => {
      firedRef.current = true;
      isPressedRef.current = false;
      onLongPress();
    }, duration);
  }, [duration, onLongPress]);
  const onPointerMove = useCallback((e) => {
    if (!isPressedRef.current) return;
    const dx = e.clientX - startPos.current.x;
    const dy = e.clientY - startPos.current.y;
    if (Math.sqrt(dx * dx + dy * dy) > moveThreshold) {
      cancel();
    }
  }, [moveThreshold, cancel]);
  const onPointerUp = useCallback(() => {
    cancel();
  }, [cancel]);
  const onPointerCancel = useCallback(() => {
    cancel();
  }, [cancel]);
  return {
    onPointerDown,
    onPointerMove,
    onPointerUp,
    onPointerCancel,
    isPressed: isPressedRef.current
  };
}
function useSwipeGesture(callbacks, options = {}) {
  const { threshold = 50, velocityThreshold = 0.3, preventDefault = false } = options;
  const startX = useRef(0);
  const startY = useRef(0);
  const startTime = useRef(0);
  const currentX = useRef(0);
  const tracking = useRef(false);
  const offsetXRef = useRef(0);
  const isSwipingRef = useRef(false);
  const onPointerDown = useCallback((e) => {
    startX.current = e.clientX;
    startY.current = e.clientY;
    currentX.current = e.clientX;
    startTime.current = Date.now();
    tracking.current = true;
    isSwipingRef.current = false;
    offsetXRef.current = 0;
    e.target.setPointerCapture?.(e.pointerId);
  }, []);
  const onPointerMove = useCallback((e) => {
    if (!tracking.current) return;
    if (preventDefault) e.preventDefault();
    currentX.current = e.clientX;
    const dx = e.clientX - startX.current;
    const dy = e.clientY - startY.current;
    if (Math.abs(dx) > 10 && Math.abs(dx) > Math.abs(dy)) {
      isSwipingRef.current = true;
      offsetXRef.current = dx;
    }
  }, [preventDefault]);
  const onPointerUp = useCallback((e) => {
    if (!tracking.current) return;
    tracking.current = false;
    const dx = e.clientX - startX.current;
    const dy = e.clientY - startY.current;
    const elapsed = Date.now() - startTime.current;
    const velocity = Math.abs(dx) / Math.max(elapsed, 1);
    offsetXRef.current = 0;
    isSwipingRef.current = false;
    if (Math.abs(dx) < threshold && velocity < velocityThreshold) return;
    if (Math.abs(dx) > Math.abs(dy)) {
      if (dx < -threshold) callbacks.onSwipeLeft?.();
      else if (dx > threshold) callbacks.onSwipeRight?.();
    } else {
      if (dy < -threshold) callbacks.onSwipeUp?.();
      else if (dy > threshold) callbacks.onSwipeDown?.();
    }
  }, [threshold, velocityThreshold, callbacks]);
  const onPointerCancel = useCallback(() => {
    tracking.current = false;
    offsetXRef.current = 0;
    isSwipingRef.current = false;
  }, []);
  return {
    onPointerDown,
    onPointerMove,
    onPointerUp,
    onPointerCancel,
    offsetX: offsetXRef.current,
    isSwiping: isSwipingRef.current
  };
}
function useDragReorder(initialItems, onReorder) {
  const [items, setItems] = useState(initialItems);
  const [dragIndex, setDragIndex] = useState(-1);
  const [dragOverIndex, setDragOverIndex] = useState(-1);
  const itemsRef = useRef(initialItems);
  if (initialItems !== itemsRef.current) {
    itemsRef.current = initialItems;
    setItems(initialItems);
  }
  const isDragging = dragIndex >= 0;
  const handleDragStart = useCallback((index) => (e) => {
    e.preventDefault();
    setDragIndex(index);
    setDragOverIndex(index);
    e.target.setPointerCapture?.(e.pointerId);
  }, []);
  const handleDragMove = useCallback((index) => (e) => {
    if (dragIndex < 0) return;
    const target = document.elementFromPoint(e.clientX, e.clientY);
    if (!target) return;
    let el = target;
    while (el && !el.dataset.dragIndex) {
      el = el.parentElement;
    }
    if (el?.dataset.dragIndex) {
      const overIndex = parseInt(el.dataset.dragIndex, 10);
      if (!isNaN(overIndex) && overIndex !== dragOverIndex) {
        setDragOverIndex(overIndex);
      }
    }
  }, [dragIndex, dragOverIndex]);
  const handleDragEnd = useCallback(() => {
    if (dragIndex >= 0 && dragOverIndex >= 0 && dragIndex !== dragOverIndex) {
      const newItems = [...items];
      const [movedItem] = newItems.splice(dragIndex, 1);
      newItems.splice(dragOverIndex, 0, movedItem);
      setItems(newItems);
      onReorder(dragIndex, dragOverIndex, items[dragIndex]);
    }
    setDragIndex(-1);
    setDragOverIndex(-1);
  }, [dragIndex, dragOverIndex, items, onReorder]);
  const getDragHandleProps = useCallback((index) => ({
    onPointerDown: handleDragStart(index),
    style: { cursor: "grab", touchAction: "none" },
    "aria-grabbed": dragIndex === index,
    role: "button"
  }), [handleDragStart, dragIndex]);
  const getItemProps = useCallback((index) => ({
    onPointerMove: handleDragMove(index),
    onPointerUp: handleDragEnd,
    "aria-dropeffect": "move",
    "data-drag-index": String(index),
    style: {
      opacity: dragIndex === index ? 0.5 : 1,
      transition: isDragging ? "transform 150ms ease" : void 0,
      transform: isDragging && dragOverIndex >= 0 ? index === dragIndex ? "scale(1.02)" : index > dragIndex && index <= dragOverIndex ? "translateY(-100%)" : index < dragIndex && index >= dragOverIndex ? "translateY(100%)" : void 0 : void 0
    }
  }), [handleDragMove, handleDragEnd, dragIndex, dragOverIndex, isDragging]);
  return {
    items,
    dragIndex,
    dragOverIndex,
    isDragging,
    getDragHandleProps,
    getItemProps
  };
}
function usePullToRefresh(onRefresh, options = {}) {
  const { threshold = 60, maxPull = 120 } = options;
  const [pullDistance, setPullDistance] = useState(0);
  const [isPulling, setIsPulling] = useState(false);
  const [isRefreshing, setIsRefreshing] = useState(false);
  const startY = useRef(0);
  const scrollTopRef = useRef(0);
  const onTouchStart = useCallback((e) => {
    const container = e.currentTarget;
    scrollTopRef.current = container.scrollTop;
    if (scrollTopRef.current <= 0) {
      startY.current = e.touches[0].clientY;
      setIsPulling(true);
    }
  }, []);
  const onTouchMove = useCallback((e) => {
    if (!isPulling || isRefreshing) return;
    const container = e.currentTarget;
    if (container.scrollTop > 0) {
      setPullDistance(0);
      return;
    }
    const dy = e.touches[0].clientY - startY.current;
    if (dy > 0) {
      const distance = Math.min(dy * 0.5, maxPull);
      setPullDistance(distance);
    }
  }, [isPulling, isRefreshing, maxPull]);
  const onTouchEnd = useCallback(() => {
    if (!isPulling) return;
    setIsPulling(false);
    if (pullDistance >= threshold && !isRefreshing) {
      setIsRefreshing(true);
      setPullDistance(threshold);
      onRefresh();
    } else {
      setPullDistance(0);
    }
  }, [isPulling, pullDistance, threshold, isRefreshing, onRefresh]);
  const endRefresh = useCallback(() => {
    setIsRefreshing(false);
    setPullDistance(0);
  }, []);
  const containerProps = {
    onTouchStart,
    onTouchMove,
    onTouchEnd,
    style: {
      transform: pullDistance > 0 ? `translateY(${pullDistance}px)` : void 0,
      transition: isPulling ? "none" : "transform 300ms ease-out"
    }
  };
  return {
    pullDistance,
    isPulling,
    isRefreshing,
    containerProps,
    endRefresh
  };
}
var SelectionContext = createContext(null);
var defaultCompareEntities = (a, b) => {
  if (a === b) return true;
  if (!a || !b) return false;
  if (typeof a === "object" && typeof b === "object") {
    const aId = a.id;
    const bId = b.id;
    return aId !== void 0 && aId === bId;
  }
  return false;
};
function SelectionProvider({
  children,
  debug = false,
  compareEntities = defaultCompareEntities
}) {
  const eventBus = useEventBus();
  const [selected, setSelectedState] = useState(null);
  const setSelected = useCallback(
    (entity) => {
      setSelectedState(entity);
      if (debug) {
        console.log("[SelectionProvider] Selection set:", entity);
      }
    },
    [debug]
  );
  const clearSelection = useCallback(() => {
    setSelectedState(null);
    if (debug) {
      console.log("[SelectionProvider] Selection cleared");
    }
  }, [debug]);
  const isSelected = useCallback(
    (entity) => {
      return compareEntities(selected, entity);
    },
    [selected, compareEntities]
  );
  useEffect(() => {
    const handleSelect = (event) => {
      const row = event.payload?.row;
      if (row) {
        setSelected(row);
        if (debug) {
          console.log(`[SelectionProvider] ${event.type} received:`, row);
        }
      }
    };
    const handleDeselect = (event) => {
      clearSelection();
      if (debug) {
        console.log(`[SelectionProvider] ${event.type} received - clearing selection`);
      }
    };
    const unsubView = eventBus.on("UI:VIEW", handleSelect);
    const unsubSelect = eventBus.on("UI:SELECT", handleSelect);
    const unsubClose = eventBus.on("UI:CLOSE", handleDeselect);
    const unsubDeselect = eventBus.on("UI:DESELECT", handleDeselect);
    const unsubCancel = eventBus.on("UI:CANCEL", handleDeselect);
    return () => {
      unsubView();
      unsubSelect();
      unsubClose();
      unsubDeselect();
      unsubCancel();
    };
  }, [eventBus, setSelected, clearSelection, debug]);
  const contextValue = {
    selected,
    setSelected,
    clearSelection,
    isSelected
  };
  return /* @__PURE__ */ jsx(SelectionContext.Provider, { value: contextValue, children });
}
function useSelection() {
  const context = useContext(SelectionContext);
  if (!context) {
    throw new Error("useSelection must be used within a SelectionProvider");
  }
  return context;
}
function useSelectionOptional() {
  const context = useContext(SelectionContext);
  return context;
}
var EntityDataContext = createContext(null);
function EntityDataProvider({
  adapter,
  children
}) {
  return React2.createElement(
    EntityDataContext.Provider,
    { value: adapter },
    children
  );
}
function useEntityDataAdapter() {
  return useContext(EntityDataContext);
}
var entityDataKeys = {
  all: ["entities"],
  lists: () => [...entityDataKeys.all, "list"],
  list: (entity, filters) => [...entityDataKeys.lists(), entity, filters],
  details: () => [...entityDataKeys.all, "detail"],
  detail: (entity, id) => [...entityDataKeys.details(), entity, id]
};
function useEntityList(entity, options = {}) {
  const { skip = false } = options;
  const adapter = useContext(EntityDataContext);
  const adapterData = useMemo(() => {
    if (!adapter || !entity || skip) return [];
    return adapter.getData(entity);
  }, [adapter, entity, skip, adapter?.isLoading]);
  const [stubData, setStubData] = useState([]);
  const [stubLoading, setStubLoading] = useState(!skip && !!entity && !adapter);
  const [stubError, setStubError] = useState(null);
  useEffect(() => {
    if (adapter || skip || !entity) {
      setStubLoading(false);
      return;
    }
    setStubLoading(true);
    const t = setTimeout(() => {
      setStubData([]);
      setStubLoading(false);
    }, 100);
    return () => clearTimeout(t);
  }, [entity, skip, adapter]);
  if (adapter) {
    return {
      data: adapterData,
      isLoading: adapter.isLoading,
      error: adapter.error ? new Error(adapter.error) : null,
      refetch: () => {
      }
    };
  }
  return { data: stubData, isLoading: stubLoading, error: stubError, refetch: () => {
  } };
}
function useEntity(entity, id) {
  const adapter = useContext(EntityDataContext);
  const adapterData = useMemo(() => {
    if (!adapter || !entity || !id) return null;
    return adapter.getById(entity, id) ?? null;
  }, [adapter, entity, id, adapter?.isLoading]);
  const [stubData, setStubData] = useState(null);
  const [stubLoading, setStubLoading] = useState(!!entity && !!id && !adapter);
  const [stubError, setStubError] = useState(null);
  useEffect(() => {
    if (adapter || !entity || !id) {
      setStubLoading(false);
      return;
    }
    setStubLoading(true);
    const t = setTimeout(() => {
      setStubData(null);
      setStubLoading(false);
    }, 100);
    return () => clearTimeout(t);
  }, [entity, id, adapter]);
  if (adapter) {
    return {
      data: adapterData,
      isLoading: adapter.isLoading,
      error: adapter.error ? new Error(adapter.error) : null
    };
  }
  return { data: stubData, isLoading: stubLoading, error: stubError };
}
function useEntityDetail(entity, id) {
  const result = useEntity(entity, id);
  return { ...result, refetch: () => {
  } };
}
var suspenseCache = /* @__PURE__ */ new Map();
function getSuspenseCacheKey(entity, type, id) {
  return id ? `${type}:${entity}:${id}` : `${type}:${entity}`;
}
function useEntityListSuspense(entity) {
  const adapter = useContext(EntityDataContext);
  if (adapter) {
    if (adapter.isLoading) {
      const cacheKey2 = getSuspenseCacheKey(entity, "list");
      let entry2 = suspenseCache.get(cacheKey2);
      if (!entry2 || entry2.status === "resolved") {
        let resolve;
        const promise = new Promise((r) => {
          resolve = r;
        });
        entry2 = { promise, status: "pending" };
        suspenseCache.set(cacheKey2, entry2);
        const check = setInterval(() => {
          if (!adapter.isLoading) {
            clearInterval(check);
            entry2.status = "resolved";
            resolve();
          }
        }, 50);
      }
      if (entry2.status === "pending") {
        throw entry2.promise;
      }
    }
    if (adapter.error) {
      throw new Error(adapter.error);
    }
    return {
      data: adapter.getData(entity),
      refetch: () => {
      }
    };
  }
  const cacheKey = getSuspenseCacheKey(entity, "list");
  let entry = suspenseCache.get(cacheKey);
  if (!entry) {
    let resolve;
    const promise = new Promise((r) => {
      resolve = r;
      setTimeout(() => {
        entry.status = "resolved";
        resolve();
      }, 100);
    });
    entry = { promise, status: "pending" };
    suspenseCache.set(cacheKey, entry);
  }
  if (entry.status === "pending") {
    throw entry.promise;
  }
  return { data: [], refetch: () => {
  } };
}
function useEntitySuspense(entity, id) {
  const adapter = useContext(EntityDataContext);
  if (adapter) {
    if (adapter.isLoading) {
      const cacheKey2 = getSuspenseCacheKey(entity, "detail", id);
      let entry2 = suspenseCache.get(cacheKey2);
      if (!entry2 || entry2.status === "resolved") {
        let resolve;
        const promise = new Promise((r) => {
          resolve = r;
        });
        entry2 = { promise, status: "pending" };
        suspenseCache.set(cacheKey2, entry2);
        const check = setInterval(() => {
          if (!adapter.isLoading) {
            clearInterval(check);
            entry2.status = "resolved";
            resolve();
          }
        }, 50);
      }
      if (entry2.status === "pending") {
        throw entry2.promise;
      }
    }
    if (adapter.error) {
      throw new Error(adapter.error);
    }
    return {
      data: adapter.getById(entity, id) ?? null,
      refetch: () => {
      }
    };
  }
  const cacheKey = getSuspenseCacheKey(entity, "detail", id);
  let entry = suspenseCache.get(cacheKey);
  if (!entry) {
    let resolve;
    const promise = new Promise((r) => {
      resolve = r;
      setTimeout(() => {
        entry.status = "resolved";
        resolve();
      }, 100);
    });
    entry = { promise, status: "pending" };
    suspenseCache.set(cacheKey, entry);
  }
  if (entry.status === "pending") {
    throw entry.promise;
  }
  return { data: null, refetch: () => {
  } };
}

export { EntityDataProvider, I18nProvider, SelectionContext, SelectionProvider, createTranslate, entityDataKeys, parseQueryBinding, useDragReorder, useEntity, useEntityDataAdapter, useEntityDetail, useEntityList, useEntityListSuspense, useEntitySuspense, useInfiniteScroll, useLongPress, usePullToRefresh, useQuerySingleton, useSelection, useSelectionOptional, useSwipeGesture, useTranslate };
