import { useEffect, useRef, useState } from "preact/hooks";

function clamp(v, min, max) {
  return Math.max(min, Math.min(max, v));
}

export default function useDraggableFloating(storageKey, opts = {}) {
  const margin = Number.isFinite(opts.margin) ? opts.margin : 12;

  const ref = useRef(null);

  const [pos, setPos] = useState(() => {
    if (typeof window === "undefined") return { x: null, y: null };
    try {
      const raw = localStorage.getItem(storageKey);
      return raw ? JSON.parse(raw) : { x: null, y: null };
    } catch {
      return { x: null, y: null };
    }
  });

  const stateRef = useRef({
    dragging: false,
    pointerId: null,
    startX: 0,
    startY: 0,
    originX: 0,
    originY: 0,
  });

  function getBounds(el) {
    const rect = el.getBoundingClientRect();
    const maxX = window.innerWidth - rect.width - margin;
    const maxY = window.innerHeight - rect.height - margin;
    return { maxX, maxY };
  }

  function persist(next) {
    try {
      localStorage.setItem(storageKey, JSON.stringify(next));
    } catch {}
  }

  function onPointerDown(e) {
    if (e.pointerType === "mouse" && e.button !== 0) return;

    e.preventDefault();
    e.stopPropagation();

    const el = ref.current;
    if (!el) return;

    el.setPointerCapture?.(e.pointerId);

    const st = stateRef.current;
    st.dragging = true;
    st.pointerId = e.pointerId;
    st.startX = e.clientX;
    st.startY = e.clientY;

    const rect = el.getBoundingClientRect();
    st.originX = pos.x == null ? rect.left : pos.x;
    st.originY = pos.y == null ? rect.top : pos.y;
  }

  function onPointerMove(e) {
    const st = stateRef.current;
    if (!st.dragging || st.pointerId !== e.pointerId) return;

    const el = ref.current;
    if (!el) return;

    const dx = e.clientX - st.startX;
    const dy = e.clientY - st.startY;

    const { maxX, maxY } = getBounds(el);

    const next = {
      x: clamp(st.originX + dx, margin, maxX),
      y: clamp(st.originY + dy, margin, maxY),
    };

    setPos(next);
  }

  function onPointerUp(e) {
    const st = stateRef.current;
    if (!st.dragging || st.pointerId !== e.pointerId) return;

    st.dragging = false;
    st.pointerId = null;

    // zapis po puszczeniu
    persist(pos);
  }

  function reset() {
    const next = { x: null, y: null };
    setPos(next);
    try {
      localStorage.removeItem(storageKey);
    } catch {}
  }

  // docinanie na resize
  useEffect(() => {
    function onResize() {
      const el = ref.current;
      if (!el || pos.x == null || pos.y == null) return;

      const { maxX, maxY } = getBounds(el);
      const next = {
        x: clamp(pos.x, margin, maxX),
        y: clamp(pos.y, margin, maxY),
      };

      if (next.x !== pos.x || next.y !== pos.y) {
        setPos(next);
        persist(next);
      }
    }

    window.addEventListener("resize", onResize);
    return () => window.removeEventListener("resize", onResize);
  }, [pos.x, pos.y]);

  const style =
    pos.x == null || pos.y == null
      ? undefined
      : `left:${pos.x}px; top:${pos.y}px; right:auto; bottom:auto;`;

  const handlers = {
    onPointerDown,
    onPointerMove,
    onPointerUp,
    onPointerCancel: onPointerUp,
  };

  return { ref, style, handlers, pos, setPos, reset };
}
