'use strict';

/**
 * Módulo independente de zoom do mapa mundial.
 *
 * Responsabilidades deste ficheiro:
 * - aproximar e afastar o mapa com a roda do rato;
 * - manter o ponto sob o cursor fixo durante o zoom;
 * - atualizar as dimensões de deslocação sem alterar coordenadas do jogo;
 * - disponibilizar conversões entre píxeis visuais e píxeis base do mapa.
 */
(() => {
  const MIN_ZOOM = 0.50;
  const MAX_ZOOM = 1.80;
  const ZOOM_STEP = 0.10;
  const DEFAULT_ZOOM = 1;

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

  function normalizeZoom(value) {
    const parsed = Number(value);
    const safe = Number.isFinite(parsed) ? parsed : DEFAULT_ZOOM;
    return Math.round(clamp(safe, MIN_ZOOM, MAX_ZOOM) * 100) / 100;
  }

  function create(options = {}) {
    const {
      viewport,
      stage,
      surface,
      baseWidth,
      baseHeight,
      initialZoom = DEFAULT_ZOOM,
      onChange = null
    } = options;

    if (!viewport || !stage || !surface) {
      throw new Error('O módulo de zoom não encontrou os elementos do mapa mundial.');
    }

    const width = Math.max(1, Number(baseWidth) || 1);
    const height = Math.max(1, Number(baseHeight) || 1);
    let zoom = normalizeZoom(initialZoom);
    let destroyed = false;

    function updateLayout() {
      stage.style.width = `${Math.round(width * zoom)}px`;
      stage.style.height = `${Math.round(height * zoom)}px`;
      surface.style.width = `${width}px`;
      surface.style.height = `${height}px`;
      surface.style.transform = `scale(${zoom})`;
      viewport.dataset.worldZoom = String(zoom);
    }

    function viewportAnchorFromEvent(event) {
      const rect = viewport.getBoundingClientRect();
      return {
        x: clamp(event.clientX - rect.left, 0, viewport.clientWidth),
        y: clamp(event.clientY - rect.top, 0, viewport.clientHeight)
      };
    }

    function setZoom(nextZoom, options = {}) {
      if (destroyed) return zoom;

      const previousZoom = zoom;
      const normalized = normalizeZoom(nextZoom);
      if (normalized === previousZoom) return zoom;

      const anchorX = Number.isFinite(options.anchorX) ? options.anchorX : viewport.clientWidth / 2;
      const anchorY = Number.isFinite(options.anchorY) ? options.anchorY : viewport.clientHeight / 2;
      const basePixelX = (viewport.scrollLeft + anchorX) / previousZoom;
      const basePixelY = (viewport.scrollTop + anchorY) / previousZoom;

      zoom = normalized;
      updateLayout();

      viewport.scrollLeft = basePixelX * zoom - anchorX;
      viewport.scrollTop = basePixelY * zoom - anchorY;

      if (typeof onChange === 'function') onChange(zoom);
      return zoom;
    }

    function onWheel(event) {
      event.preventDefault();
      const anchor = viewportAnchorFromEvent(event);
      const direction = event.deltaY < 0 ? 1 : -1;
      setZoom(zoom + direction * ZOOM_STEP, {
        anchorX: anchor.x,
        anchorY: anchor.y
      });
    }

    function getViewportCenterBasePixel() {
      return {
        x: (viewport.scrollLeft + viewport.clientWidth / 2) / zoom,
        y: (viewport.scrollTop + viewport.clientHeight / 2) / zoom
      };
    }

    function centerOnBasePixel(basePixelX, basePixelY, smooth = false) {
      const left = clamp(
        Number(basePixelX) * zoom - viewport.clientWidth / 2,
        0,
        Math.max(0, width * zoom - viewport.clientWidth)
      );
      const top = clamp(
        Number(basePixelY) * zoom - viewport.clientHeight / 2,
        0,
        Math.max(0, height * zoom - viewport.clientHeight)
      );
      viewport.scrollTo({ left, top, behavior: smooth ? 'smooth' : 'auto' });
    }

    function destroy() {
      if (destroyed) return;
      destroyed = true;
      viewport.removeEventListener('wheel', onWheel);
      delete viewport.__realmWorldMapZoom;
    }

    updateLayout();
    viewport.addEventListener('wheel', onWheel, { passive: false });

    const controller = {
      getZoom: () => zoom,
      setZoom,
      getViewportCenterBasePixel,
      centerOnBasePixel,
      scaledPixelToBase: value => Number(value) / zoom,
      basePixelToScaled: value => Number(value) * zoom,
      destroy
    };

    viewport.__realmWorldMapZoom = controller;
    return controller;
  }

  globalThis.RealmWorldMapZoom = Object.freeze({
    create,
    normalizeZoom,
    MIN_ZOOM,
    MAX_ZOOM,
    ZOOM_STEP,
    DEFAULT_ZOOM
  });
})();
