/**
 * Pan + zoom "cover" crop inside a square viewport, export inner frame (inset %) to a square blob.
 * Exposes window.AntaraAvatarSquareCrop for admin + login wizard.
 */
(function (global) {
  "use strict";

  var DEFAULT_FRAME_INSET = 0.1;
  var EXPORT_SIZE = 512;
  var JPEG_QUALITY = 0.92;

  function clamp(n, a, b) {
    return Math.max(a, Math.min(b, n));
  }

  function readState(viewport) {
    return viewport._antaraAvatarCrop || null;
  }

  function writeState(viewport, st) {
    viewport._antaraAvatarCrop = st;
  }

  function mediaNaturalSize(el) {
    if (!el) return { w: 0, h: 0 };
    if (el.tagName === "VIDEO") {
      return { w: el.videoWidth || 0, h: el.videoHeight || 0 };
    }
    return { w: el.naturalWidth || 0, h: el.naturalHeight || 0 };
  }

  function layoutFromZoomMedia(el, viewport, zoomPct, panX, panY) {
    var V = viewport.clientWidth;
    if (V <= 0) return null;
    var dims = mediaNaturalSize(el);
    var nw = dims.w;
    var nh = dims.h;
    if (!nw || !nh) return null;
    var cover = Math.max(V / nw, V / nh);
    var S = cover * (zoomPct / 100);
    var displayW = nw * S;
    var displayH = nh * S;
    var x = (V - displayW) / 2 + panX;
    var y = (V - displayH) / 2 + panY;
    var minX = V - displayW;
    var maxX = 0;
    var minY = V - displayH;
    var maxY = 0;
    x = clamp(x, minX, maxX);
    y = clamp(y, minY, maxY);
    var effPanX = x - (V - displayW) / 2;
    var effPanY = y - (V - displayH) / 2;
    return {
      V: V,
      S: S,
      x: x,
      y: y,
      displayW: displayW,
      displayH: displayH,
      nw: nw,
      nh: nh,
      panX: effPanX,
      panY: effPanY,
    };
  }

  function layoutFromZoom(img, viewport, zoomPct, panX, panY) {
    return layoutFromZoomMedia(img, viewport, zoomPct, panX, panY);
  }

  function applyLayoutMedia(el, viewport, zoomPct, panX, panY) {
    var L = layoutFromZoomMedia(el, viewport, zoomPct, panX, panY);
    if (!L) return null;
    el.style.position = "absolute";
    el.style.left = L.x + "px";
    el.style.top = L.y + "px";
    el.style.width = L.displayW + "px";
    el.style.height = L.displayH + "px";
    el.style.maxWidth = "none";
    return Object.assign(L, { zoomPct: zoomPct });
  }

  function applyLayout(img, viewport, zoomPct, panX, panY) {
    return applyLayoutMedia(img, viewport, zoomPct, panX, panY);
  }

  /** Lee posición/tamaño actuales del DOM (fallback si el estado interno no se guardó). */
  function layoutFromDom(img, viewport) {
    var V = viewport.clientWidth;
    var dims = mediaNaturalSize(img);
    if (V <= 0 || !dims.w || !dims.h) return null;
    var vpRect = viewport.getBoundingClientRect();
    var ir = img.getBoundingClientRect();
    var displayW = ir.width;
    var displayH = ir.height;
    if (displayW <= 0 || displayH <= 0) return null;
    var x = ir.left - vpRect.left;
    var y = ir.top - vpRect.top;
    var nw = dims.w;
    var nh = dims.h;
    var S = displayW / nw;
    return {
      V: V,
      S: S,
      x: x,
      y: y,
      displayW: displayW,
      displayH: displayH,
      nw: nw,
      nh: nh,
      panX: x - (V - displayW) / 2,
      panY: y - (V - displayH) / 2,
    };
  }

  function readZoomPct(zoomRange, st) {
    if (st && isFinite(st.zoomPct)) return st.zoomPct;
    if (zoomRange) {
      var z = parseFloat(zoomRange.value);
      if (isFinite(z) && z >= 1) return z;
    }
    return 100;
  }

  function resolveExportLayout(img, viewport, options) {
    options = options || {};
    var st = readState(viewport);
    var inset = st && st.frameInsetRatio != null ? st.frameInsetRatio : DEFAULT_FRAME_INSET;
    var z = readZoomPct(options.zoomRange, st);
    var panX = st ? st.panX : 0;
    var panY = st ? st.panY : 0;
    var L = st && st.layout ? st.layout : layoutFromZoom(img, viewport, z, panX, panY);
    if (!L || !L.nw || !L.nh) {
      L = layoutFromZoom(img, viewport, z, panX, panY);
    }
    if (!L || !L.nw || !L.nh) {
      L = layoutFromDom(img, viewport);
    }
    return { L: L, inset: inset };
  }

  function bindSquareAvatarCrop(opts) {
    var viewport = opts.viewport;
    var img = opts.img;
    var zoomRange = opts.zoomRange;
    var frameInsetRatio = opts.frameInsetRatio != null ? opts.frameInsetRatio : DEFAULT_FRAME_INSET;

    var panX = 0;
    var panY = 0;
    var dragging = false;
    var startClientX = 0;
    var startClientY = 0;
    var startPanX = 0;
    var startPanY = 0;
    var activePointerId = null;

    function getZoom() {
      var z = zoomRange ? parseFloat(zoomRange.value) : 100;
      if (!isFinite(z) || z < 1) z = 100;
      return z;
    }

    function sync() {
      if (viewport.clientWidth <= 0) return;
      var dims = mediaNaturalSize(img);
      if (!dims.w) return;
      var z = getZoom();
      var L = applyLayoutMedia(img, viewport, z, panX, panY);
      if (L) {
        panX = L.panX;
        panY = L.panY;
        writeState(viewport, {
          frameInsetRatio: frameInsetRatio,
          zoomPct: z,
          panX: panX,
          panY: panY,
          layout: L,
        });
      }
    }

    function onMediaReady() {
      panX = 0;
      panY = 0;
      if (zoomRange) zoomRange.value = "100";
      sync();
    }

    function onZoomInput() {
      sync();
    }

    function pointerDown(e) {
      if (e.button != null && e.button !== 0) return;
      dragging = true;
      activePointerId = e.pointerId;
      viewport.classList.add("is-dragging");
      try {
        viewport.setPointerCapture(e.pointerId);
      } catch (_) {}
      startClientX = e.clientX;
      startClientY = e.clientY;
      startPanX = panX;
      startPanY = panY;
    }

    function pointerMove(e) {
      if (!dragging || e.pointerId !== activePointerId) return;
      panX = startPanX + (e.clientX - startClientX);
      panY = startPanY + (e.clientY - startClientY);
      sync();
    }

    function pointerUp(e) {
      if (e.pointerId !== activePointerId) return;
      dragging = false;
      activePointerId = null;
      viewport.classList.remove("is-dragging");
      try {
        viewport.releasePointerCapture(e.pointerId);
      } catch (_) {}
    }

    img.addEventListener("load", onMediaReady);
    if (img.tagName === "VIDEO") {
      img.addEventListener("loadeddata", onMediaReady);
    }
    if (zoomRange) zoomRange.addEventListener("input", onZoomInput);
    viewport.addEventListener("pointerdown", pointerDown);
    viewport.addEventListener("pointermove", pointerMove);
    viewport.addEventListener("pointerup", pointerUp);
    viewport.addEventListener("pointercancel", pointerUp);

    var ro =
      typeof ResizeObserver !== "undefined"
        ? new ResizeObserver(function () {
            sync();
          })
        : null;
    if (ro) ro.observe(viewport);

    if (img.tagName === "VIDEO") {
      if (img.readyState >= 2) onMediaReady();
    } else if (img.complete && mediaNaturalSize(img).w) {
      onMediaReady();
    } else {
      sync();
    }

    return function unbind() {
      img.removeEventListener("load", onMediaReady);
      if (img.tagName === "VIDEO") {
        img.removeEventListener("loadeddata", onMediaReady);
      }
      if (zoomRange) zoomRange.removeEventListener("input", onZoomInput);
      viewport.removeEventListener("pointerdown", pointerDown);
      viewport.removeEventListener("pointermove", pointerMove);
      viewport.removeEventListener("pointerup", pointerUp);
      viewport.removeEventListener("pointercancel", pointerUp);
      if (ro) ro.disconnect();
      viewport.classList.remove("is-dragging");
      writeState(viewport, null);
    };
  }

  /**
   * @param {HTMLImageElement} img
   * @param {HTMLElement} viewport
   * @param {{ mime?: string, quality?: number, outSize?: number }} [options]
   * @returns {Promise<Blob>}
   */
  function exportSquareAvatarBlob(img, viewport, options) {
    options = options || {};
    var mime = options.mime || "image/jpeg";
    var quality = options.quality != null ? options.quality : JPEG_QUALITY;
    var outSize = options.outSize != null ? options.outSize : EXPORT_SIZE;
    var resolved = resolveExportLayout(img, viewport, options);
    var L = resolved.L;
    var inset = resolved.inset;
    if (!L || !L.nw || !L.nh) {
      return Promise.reject(new Error("Sin imagen para exportar. Espera a que cargue la vista previa."));
    }
    var V = L.V;
    var inner = V * (1 - 2 * inset);
    var vx0 = V * inset;
    var vy0 = V * inset;

    var sx0 = ((vx0 - L.x) / L.displayW) * L.nw;
    var sy0 = ((vy0 - L.y) / L.displayH) * L.nh;
    var sw = (inner / L.displayW) * L.nw;
    var sh = (inner / L.displayH) * L.nh;

    sx0 = clamp(sx0, 0, L.nw);
    sy0 = clamp(sy0, 0, L.nh);
    sw = clamp(sw, 1, L.nw - sx0);
    sh = clamp(sh, 1, L.nh - sy0);

    var canvas = document.createElement("canvas");
    canvas.width = outSize;
    canvas.height = outSize;
    var ctx = canvas.getContext("2d");
    if (!ctx) return Promise.reject(new Error("Canvas no disponible."));
    ctx.drawImage(img, sx0, sy0, sw, sh, 0, 0, outSize, outSize);

    return new Promise(function (resolve, reject) {
      canvas.toBlob(
        function (blob) {
          if (!blob) reject(new Error("No se pudo generar la imagen."));
          else resolve(blob);
        },
        mime,
        mime === "image/jpeg" || mime === "image/webp" ? quality : undefined
      );
    });
  }

  function exportMediaFrame(viewport, options) {
    options = options || {};
    var st = readState(viewport);
    var inset = st && st.frameInsetRatio != null ? st.frameInsetRatio : DEFAULT_FRAME_INSET;
    var z = readZoomPct(options.zoomRange, st);
    return {
      panX: st ? st.panX : 0,
      panY: st ? st.panY : 0,
      zoomPct: z,
      frameInsetRatio: inset,
    };
  }

  global.AntaraAvatarSquareCrop = {
    bindSquareAvatarCrop: bindSquareAvatarCrop,
    exportSquareAvatarBlob: exportSquareAvatarBlob,
    exportMediaFrame: exportMediaFrame,
    applyLayoutMedia: applyLayoutMedia,
    layoutFromZoomMedia: layoutFromZoomMedia,
    mediaNaturalSize: mediaNaturalSize,
  };
})(typeof window !== "undefined" ? window : this);
