'use strict';

(() => {
  const MIN_ZOOM = 0.28;
  const MAX_ZOOM = 1.65;
  const STEP = 0.10;

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

  function normalizeZoom(value) {
    return Math.round(clamp(Number(value) || 1, MIN_ZOOM, MAX_ZOOM) * 100) / 100;
  }

  function ensureCamera(profile) {
    if (!profile.castleState) profile.castleState = {};
    if (!profile.castleState.camera || typeof profile.castleState.camera !== 'object') {
      profile.castleState.camera = { zoom: 1, scrollLeft: null, scrollTop: null };
    }
    profile.castleState.camera.zoom = normalizeZoom(profile.castleState.camera.zoom);
    return profile.castleState.camera;
  }

  function mount({ viewport, stage, world, profile, ctx, center }) {
    if (!viewport || !stage || !world || !profile) return false;
    const camera = ensureCamera(profile);
    const baseWidth = Number(world.dataset.baseWidth) || world.offsetWidth || 1800;
    const baseHeight = Number(world.dataset.baseHeight) || world.offsetHeight || 1100;
    let zoom = camera.zoom;
    let persistTimer = null;
    let dragging = false;
    let startX = 0;
    let startY = 0;
    let startScrollLeft = 0;
    let startScrollTop = 0;

    const persistCamera = () => {
      camera.zoom = zoom;
      camera.scrollLeft = Math.round(viewport.scrollLeft);
      camera.scrollTop = Math.round(viewport.scrollTop);
      if (typeof ctx?.persist === 'function') ctx.persist(true);
    };

    const schedulePersist = () => {
      clearTimeout(persistTimer);
      persistTimer = setTimeout(persistCamera, 180);
    };

    const applyZoom = (nextZoom, clientX = null, clientY = null) => {
      nextZoom = normalizeZoom(nextZoom);
      if (nextZoom === zoom) return;
      const rect = viewport.getBoundingClientRect();
      const anchorX = clientX == null ? viewport.clientWidth / 2 : clientX - rect.left;
      const anchorY = clientY == null ? viewport.clientHeight / 2 : clientY - rect.top;
      const worldX = (viewport.scrollLeft + anchorX) / zoom;
      const worldY = (viewport.scrollTop + anchorY) / zoom;
      zoom = nextZoom;
      camera.zoom = zoom;
      stage.style.width = `${Math.round(baseWidth * zoom)}px`;
      stage.style.height = `${Math.round(baseHeight * zoom)}px`;
      world.style.transform = `scale(${zoom})`;
      viewport.scrollLeft = worldX * zoom - anchorX;
      viewport.scrollTop = worldY * zoom - anchorY;
      const output = document.querySelector('[data-castle-zoom-output]');
      if (output) output.textContent = `${Math.round(zoom * 100)}%`;
      schedulePersist();
    };

    stage.style.width = `${Math.round(baseWidth * zoom)}px`;
    stage.style.height = `${Math.round(baseHeight * zoom)}px`;
    world.style.transform = `scale(${zoom})`;

    requestAnimationFrame(() => {
      if (Number.isFinite(camera.scrollLeft) && Number.isFinite(camera.scrollTop)) {
        viewport.scrollLeft = camera.scrollLeft;
        viewport.scrollTop = camera.scrollTop;
      } else if (center) {
        viewport.scrollLeft = Math.max(0, center.x * zoom - viewport.clientWidth / 2);
        viewport.scrollTop = Math.max(0, center.y * zoom - viewport.clientHeight / 2);
      }
    });

    viewport.addEventListener('wheel', event => {
      event.preventDefault();
      applyZoom(zoom + (event.deltaY < 0 ? STEP : -STEP), event.clientX, event.clientY);
    }, { passive: false });

    viewport.addEventListener('pointerdown', event => {
      if (event.button !== 0 || event.target.closest('button,input,select,textarea')) return;
      dragging = true;
      startX = event.clientX;
      startY = event.clientY;
      startScrollLeft = viewport.scrollLeft;
      startScrollTop = viewport.scrollTop;
      viewport.classList.add('is-dragging');
      viewport.setPointerCapture?.(event.pointerId);
    });

    viewport.addEventListener('pointermove', event => {
      if (!dragging) return;
      viewport.scrollLeft = startScrollLeft - (event.clientX - startX);
      viewport.scrollTop = startScrollTop - (event.clientY - startY);
    });

    const stopDrag = event => {
      if (!dragging) return;
      dragging = false;
      viewport.classList.remove('is-dragging');
      try { viewport.releasePointerCapture?.(event.pointerId); } catch (_) {}
      schedulePersist();
    };
    viewport.addEventListener('pointerup', stopDrag);
    viewport.addEventListener('pointercancel', stopDrag);
    viewport.addEventListener('scroll', schedulePersist, { passive: true });

    document.querySelectorAll('[data-castle-camera]').forEach(button => {
      button.addEventListener('click', () => {
        const command = button.dataset.castleCamera;
        if (command === 'in') applyZoom(zoom + STEP);
        if (command === 'out') applyZoom(zoom - STEP);
        if (command === 'home' && center) {
          viewport.scrollTo({
            left: Math.max(0, center.x * zoom - viewport.clientWidth / 2),
            top: Math.max(0, center.y * zoom - viewport.clientHeight / 2),
            behavior: 'smooth'
          });
          schedulePersist();
        }
      });
    });

    return true;
  }

  globalThis.ReinosCastleCamera = {
    MIN_ZOOM,
    MAX_ZOOM,
    normalizeZoom,
    ensureCamera,
    mount
  };
})();
