// @wuys/map · interactions/mouse.js
// Wires mouse interactions (drag, wheel zoom, click) to nav commands + events.

import { NAV_ZOOM_MIN, NAV_ZOOM_MAX, NAV_LAT_MAX } from "../core/state.js";

export function attachMouse({ target, state, events, nav, renderer, animator }) {
  let dragging = false;
  let dragStart = null;
  let pixelMoved = 0;

  target.addEventListener("wheel", (e) => {
    e.preventDefault();
    // Snappy direct zoom — one event, one discrete step, repaint, done.
    // Same feel as drag/arrows: no animator, no easing, no inertia, no
    // extra projection math per event.
    const factor = e.deltaY < 0 ? 1.4 : 1 / 1.4;
    const next = Math.max(NAV_ZOOM_MIN, Math.min(NAV_ZOOM_MAX, state.nav.zoom * factor));
    if (next === state.nav.zoom) return;
    state.nav.zoom = next;
    state.nav.tZoom = next;
    animator.snap();
    renderer.patch();
    events.emit("nav:change", state.nav);
  }, { passive: false });

  target.addEventListener("mousedown", (e) => {
    if (e.button !== 0) return;
    if (e.target.closest("[data-no-drag]")) return;
    animator.snap();
    dragging = true;
    pixelMoved = 0;
    dragStart = { x: e.clientX, y: e.clientY, lon: state.nav.lon, lat: state.nav.lat };
    target.classList.add("wuys-map-dragging");
    // Tell the renderer to skip the expensive flag clipPath updates while
    // we're actively dragging — biggest single perf win on the globe.
    state._isDragging = true;
  });

  window.addEventListener("mousemove", (e) => {
    if (!dragging) {
      // Hover handling
      const hit = renderer.hitTest(e.clientX, e.clientY);
      if (hit) {
        if (state.interactions.hoveredIso !== hit.iso) {
          const prev = state.interactions.hoveredIso;
          state.interactions.hoveredIso = hit.iso;
          if (prev) events.emit("country:leave", prev);
          events.emit("country:hover", hit.iso);
        }
      } else if (state.interactions.hoveredIso) {
        const prev = state.interactions.hoveredIso;
        state.interactions.hoveredIso = null;
        events.emit("country:leave", prev);
      }
      return;
    }
    const dx = e.clientX - dragStart.x;
    const dy = e.clientY - dragStart.y;
    pixelMoved = Math.max(pixelMoved, Math.hypot(dx, dy));
    if (state.view.mode === "flat") {
      // Flat drag: convert pixel delta to lon/lat delta via inverse projection at start point
      const startScreen = renderer.worldToScreen({ lon: dragStart.lon, lat: dragStart.lat });
      const target = renderer.screenToWorld({ x: startScreen.x + dx, y: startScreen.y + dy });
      state.nav.lon = dragStart.lon - (target.lon - dragStart.lon);
      state.nav.lat = Math.max(-NAV_LAT_MAX, Math.min(NAV_LAT_MAX, dragStart.lat - (target.lat - dragStart.lat)));
    } else {
      // Globe: degrees per pixel, inverse-proportional to zoom (so high zoom = less sensitive).
      // Drag direction is "grab and pull" — drag right pulls the globe east under
      // your finger, so the camera moves west: lon DECREASES with positive dx.
      const sensDeg = 0.28 / Math.max(0.5, state.nav.zoom);
      state.nav.lon = dragStart.lon - dx * sensDeg;
      state.nav.lat = Math.max(-NAV_LAT_MAX, Math.min(NAV_LAT_MAX, dragStart.lat + dy * sensDeg));
    }
    state.nav.tLon = state.nav.lon;
    state.nav.tLat = state.nav.lat;
    // Paint immediately — drag updates state directly and never goes through
    // the animator, so without this the map wouldn't visibly track the cursor.
    renderer.patch();
    events.emit("nav:change", state.nav);
  });

  window.addEventListener("mouseup", () => {
    if (!dragging) return;
    dragging = false;
    target.classList.remove("wuys-map-dragging");
    // Drag ended — re-enable flag rendering and force a single repaint so the
    // flags snap back into their correct (now-final) clip shapes.
    state._isDragging = false;
    renderer.patch();
    if (pixelMoved > 5) {
      // Swallow the click that follows a drag
      const swallow = (ev) => { ev.stopPropagation(); ev.preventDefault(); target.removeEventListener("click", swallow, true); };
      target.addEventListener("click", swallow, true);
    }
  });

  target.addEventListener("click", (e) => {
    if (e.defaultPrevented) return;
    const hit = renderer.hitTest(e.clientX, e.clientY);
    if (hit) events.emit("country:click", hit.iso);
  });
}
