(function (win, doc) {
  'use strict';

  var CHANNEL = 'vareoncalc_preview';
  var PROTOCOL_VERSION = '1.0';
  var SAFE_AREA_PX = 12;
  var mountNode = null;
  var resizeObserver = null;
  var tooltipResizeObserver = null;
  var tooltipMutationObserver = null;
  var latestConfig = null;
  var readySent = false;
  var tooltipPatchTimer = null;
  var loaderWaitTimer = null;
  var loaderWaitStartedAt = 0;
  var loaderWaitInFlight = false;
  var loaderWaitCallbacks = [];
  var loaderReady = false;
  var loaderTimeoutReported = false;
  var mountQueuedUntilLoaderReady = false;
  var LOADER_READY_POLL_MS = 20;
  var LOADER_READY_TIMEOUT_MS = 5000;
  var previewContext = '';
  var loadedCalcStyles = {};

  function isObject(value) {
    return !!value && typeof value === 'object' && !Array.isArray(value);
  }

  function isDebugEnabled() {
    return !!(win.VAREON_DEBUG || (win.VAREONCALC_CFG && win.VAREONCALC_CFG.debug));
  }

  function log() {
    if (!isDebugEnabled() || !win.console || typeof win.console.log !== 'function') {
      return;
    }
    var args = Array.prototype.slice.call(arguments);
    args.unshift('[Vareon Preview Frame]');
    win.console.log.apply(win.console, args);
  }

  function getParentOrigin() {
    try {
      if (doc.referrer) {
        return new URL(doc.referrer).origin;
      }
    } catch {
      // Ignore malformed referrer.
    }
    return '*';
  }

  function post(type, payload) {
    if (!win.parent || win.parent === win) {
      return;
    }
    try {
      win.parent.postMessage(
        {
          channel: CHANNEL,
          protocolVersion: PROTOCOL_VERSION,
          type: type,
          payload: isObject(payload) ? payload : {}
        },
        getParentOrigin()
      );
    } catch {
      // Ignore postMessage failures.
    }
  }

  function isTrustedParentMessage(event) {
    if (!event || !win.parent || win.parent === win) {
      return false;
    }

    if (event.source !== win.parent) {
      return false;
    }

    var parentOrigin = getParentOrigin();
    if (parentOrigin !== '*' && event.origin !== parentOrigin) {
      return false;
    }

    var data = event.data;
    if (!isObject(data) || data.channel !== CHANNEL || typeof data.type !== 'string') {
      return false;
    }

    return data.protocolVersion === PROTOCOL_VERSION;
  }

  function ensureMountNode() {
    if (mountNode && mountNode.isConnected) {
      return mountNode;
    }
    mountNode = doc.getElementById('vareoncalc-preview-frame-mount');
    return mountNode;
  }

  function measureHeight() {
    var body = doc.body;
    var root = doc.documentElement;
    var height = Math.max(
      body ? body.scrollHeight : 0,
      body ? body.offsetHeight : 0,
      root ? root.scrollHeight : 0,
      root ? root.offsetHeight : 0
    );

    var tooltipPortal = doc.getElementById('vareoncalc-tooltip-portal');
    if (tooltipPortal && tooltipPortal.classList.contains('is-visible')) {
      var rect = tooltipPortal.getBoundingClientRect();
      if (rect.bottom > win.innerHeight) {
        height += Math.ceil(rect.bottom - win.innerHeight) + SAFE_AREA_PX;
      }
      if (rect.top < 0) {
        height += Math.ceil(Math.abs(rect.top)) + SAFE_AREA_PX;
      }
    }

    return Math.max(220, Math.ceil(height + SAFE_AREA_PX));
  }

  function notifyResize() {
    post('vc_preview:resize', { height: measureHeight() });
  }

  function scheduleResize() {
    if (typeof win.requestAnimationFrame === 'function') {
      win.requestAnimationFrame(function () {
        notifyResize();
      });
      return;
    }
    setTimeout(notifyResize, 0);
  }

  function observeTooltipPortal() {
    var tooltipPortal = doc.getElementById('vareoncalc-tooltip-portal');
    if (!tooltipPortal) {
      return;
    }

    tooltipPortal.style.maxWidth = 'min(360px, calc(100vw - 24px))';
    tooltipPortal.style.setProperty('--vareoncalc-tooltip-safe-area', SAFE_AREA_PX + 'px');

    if (tooltipResizeObserver) {
      tooltipResizeObserver.disconnect();
    }
    tooltipResizeObserver = new ResizeObserver(function () {
      scheduleResize();
    });
    tooltipResizeObserver.observe(tooltipPortal);
  }

  function bindResizeObservers() {
    if (typeof ResizeObserver !== 'function') {
      setInterval(notifyResize, 400);
      return;
    }

    if (resizeObserver) {
      resizeObserver.disconnect();
    }

    resizeObserver = new ResizeObserver(function () {
      scheduleResize();
    });

    if (doc.body) {
      resizeObserver.observe(doc.body);
    }
    if (doc.documentElement) {
      resizeObserver.observe(doc.documentElement);
    }
    if (ensureMountNode()) {
      resizeObserver.observe(mountNode);
    }

    if (tooltipMutationObserver) {
      tooltipMutationObserver.disconnect();
    }
    tooltipMutationObserver = new MutationObserver(function () {
      observeTooltipPortal();
      scheduleResize();
    });
    tooltipMutationObserver.observe(doc.body, { childList: true, subtree: true, attributes: true });
  }

  function patchTooltipPortalPositioning() {
    if (tooltipPatchTimer) {
      clearTimeout(tooltipPatchTimer);
      tooltipPatchTimer = null;
    }

    var portalApi = win.vareoncalcTooltipPortal;
    if (!portalApi || typeof portalApi.position !== 'function') {
      tooltipPatchTimer = setTimeout(patchTooltipPortalPositioning, 200);
      return;
    }

    if (portalApi.__vareoncalcSafeAreaPatched) {
      return;
    }

    var originalPosition = portalApi.position.bind(portalApi);
    portalApi.position = function () {
      originalPosition();

      var trigger = portalApi._activeTrigger;
      var portalEl = portalApi._portalEl;
      if (!trigger || !portalEl) {
        return;
      }

      var triggerRect = trigger.getBoundingClientRect();
      var tooltipRect = portalEl.getBoundingClientRect();
      var viewportW = win.innerWidth || doc.documentElement.clientWidth || 0;
      var viewportH = win.innerHeight || doc.documentElement.clientHeight || 0;
      var left = parseFloat(portalEl.style.left || '0');
      var top = parseFloat(portalEl.style.top || '0');
      var gap = 10;

      // Vertical pivot: prefer top, flip to bottom if clipped.
      if (top < SAFE_AREA_PX) {
        top = triggerRect.bottom + gap;
        portalEl.dataset.placement = 'bottom';
      } else if (top + tooltipRect.height > viewportH - SAFE_AREA_PX) {
        top = triggerRect.top - tooltipRect.height - gap;
        portalEl.dataset.placement = 'top';
      }

      // Horizontal pivot: choose side with better room before clamping.
      if (left < SAFE_AREA_PX) {
        left = Math.min(triggerRect.left, viewportW - tooltipRect.width - SAFE_AREA_PX);
      } else if (left + tooltipRect.width > viewportW - SAFE_AREA_PX) {
        left = Math.max(SAFE_AREA_PX, triggerRect.right - tooltipRect.width);
      }

      left = Math.max(SAFE_AREA_PX, Math.min(left, viewportW - tooltipRect.width - SAFE_AREA_PX));
      top = Math.max(SAFE_AREA_PX, Math.min(top, viewportH - tooltipRect.height - SAFE_AREA_PX));

      portalEl.style.left = Math.round(left) + 'px';
      portalEl.style.top = Math.round(top) + 'px';
      scheduleResize();
    };

    portalApi.__vareoncalcSafeAreaPatched = true;
  }

  function reportLoaderTimeout() {
    if (loaderTimeoutReported) {
      return;
    }
    loaderTimeoutReported = true;
    post('vc_preview:error', { message: 'Preview loader did not become ready in time.' });
  }

  function flushLoaderWaitCallbacks(isReady) {
    if (loaderWaitTimer) {
      clearTimeout(loaderWaitTimer);
      loaderWaitTimer = null;
    }
    loaderWaitInFlight = false;
    var callbacks = loaderWaitCallbacks.slice();
    loaderWaitCallbacks = [];
    callbacks.forEach(function (callback) {
      try {
        callback(!!isReady);
      } catch {
        // Ignore callback errors.
      }
    });
  }

  function waitForLoaderReady(callback) {
    if (typeof callback === 'function') {
      loaderWaitCallbacks.push(callback);
    }

    if (loaderReady || typeof win.vareoncalcMount === 'function') {
      loaderReady = true;
      flushLoaderWaitCallbacks(true);
      return;
    }

    if (loaderWaitInFlight) {
      return;
    }

    loaderWaitInFlight = true;
    loaderWaitStartedAt = Date.now();

    function pollForLoaderReady() {
      if (typeof win.vareoncalcMount === 'function') {
        loaderReady = true;
        flushLoaderWaitCallbacks(true);
        return;
      }

      if ((Date.now() - loaderWaitStartedAt) >= LOADER_READY_TIMEOUT_MS) {
        flushLoaderWaitCallbacks(false);
        return;
      }

      loaderWaitTimer = setTimeout(pollForLoaderReady, LOADER_READY_POLL_MS);
    }

    pollForLoaderReady();
  }

  function sanitizeCalculatorType(value) {
    if (typeof value !== 'string') {
      return '';
    }
    return value.toLowerCase().replace(/[^a-z0-9\-]/g, '');
  }

  function getPreviewContext() {
    if (previewContext) {
      return previewContext;
    }
    try {
      previewContext = new URL(win.location.href).searchParams.get('context') || '';
    } catch {
      previewContext = '';
    }
    return previewContext;
  }

  function shouldUseMinifiedAssets() {
    var cfg = win.VAREONCALC_CFG || {};
    if (typeof cfg.useMinifiedAssets === 'boolean') {
      return cfg.useMinifiedAssets;
    }
    return false;
  }

  function resolveAssetRelativePath(relativePath) {
    var normalizedPath = String(relativePath || '').replace(/^\/+/, '');
    if (
      shouldUseMinifiedAssets()
      && /\.(css|js)$/i.test(normalizedPath)
      && !/\.min\.(css|js)$/i.test(normalizedPath)
    ) {
      return normalizedPath.replace(/\.(css|js)$/i, '.min.$1');
    }
    return normalizedPath;
  }

  function getCalculatorStyleHref(calcType) {
    var cfg = win.VAREONCALC_CFG || {};
    if (typeof cfg.pluginUrl !== 'string' || cfg.pluginUrl === '') {
      return '';
    }

    var baseUrl = cfg.pluginUrl;
    if (baseUrl.charAt(baseUrl.length - 1) !== '/') {
      baseUrl += '/';
    }

    var href = baseUrl + resolveAssetRelativePath('assets/calculators/' + calcType + '.css');
    var version = cfg.assetVersion || cfg.version || '';
    if (version) {
      href += '?v=' + encodeURIComponent(String(version));
    }
    return href;
  }

  function ensureCalculatorStyle(calcType) {
    if ('editor' !== getPreviewContext()) {
      return;
    }

    var normalizedType = sanitizeCalculatorType(calcType || '');
    if (!normalizedType) {
      normalizedType = 'bmi';
    }

    if (loadedCalcStyles[normalizedType]) {
      return;
    }

    var styleId = 'vareoncalc-preview-style-' + normalizedType;
    if (doc.getElementById(styleId)) {
      loadedCalcStyles[normalizedType] = true;
      return;
    }

    var href = getCalculatorStyleHref(normalizedType);
    if (!href) {
      return;
    }

    var link = doc.createElement('link');
    link.id = styleId;
    link.rel = 'stylesheet';
    link.href = href;
    link.setAttribute('data-vareoncalc-preview-style', '1');
    doc.head.appendChild(link);

    loadedCalcStyles[normalizedType] = true;
  }

  function mountCalculator(config) {
    var node = ensureMountNode();
    if (!node) {
      post('vc_preview:error', { message: 'Preview mount node not found.' });
      return;
    }

    latestConfig = isObject(config) ? config : latestConfig;
    if (isDebugEnabled()) {
      post('vc_preview:log', { message: 'frame-config', config: latestConfig });
    }
    ensureCalculatorStyle(latestConfig && latestConfig.type ? latestConfig.type : 'bmi');
    node.setAttribute('data-config', JSON.stringify(latestConfig));
    node.removeAttribute('data-vareoncalc-ready');
    node.innerHTML = '';

    if (typeof win.vareoncalcMount !== 'function') {
      if (!mountQueuedUntilLoaderReady) {
        mountQueuedUntilLoaderReady = true;
        waitForLoaderReady(function (isReady) {
          mountQueuedUntilLoaderReady = false;
          if (!isReady) {
            reportLoaderTimeout();
            return;
          }
          if (latestConfig) {
            mountCalculator(latestConfig);
          }
        });
      }
      return;
    }

    try {
      win.vareoncalcMount(node);
      scheduleResize();
    } catch (error) {
      post('vc_preview:error', {
        message: (error && error.message) ? error.message : 'Failed to mount calculator preview.'
      });
    }
  }

  function handleMessage(event) {
    if (!isTrustedParentMessage(event)) {
      return;
    }

    var data = event.data;

    if (data.type === 'vc_preview:init' || data.type === 'vc_preview:update') {
      var payload = isObject(data.payload) ? data.payload : {};
      if (isObject(payload.config)) {
        mountCalculator(payload.config);
      }
      return;
    }
  }

  function init() {
    ensureMountNode();
    bindResizeObservers();
    observeTooltipPortal();
    patchTooltipPortalPositioning();
    win.addEventListener('message', handleMessage, false);
    win.addEventListener('resize', scheduleResize, false);

    // Handshake immediately so parent can stream config without waiting on module imports.
    if (!readySent) {
      readySent = true;
      post('vc_preview:ready', { protocolVersion: PROTOCOL_VERSION });
      if (isDebugEnabled()) {
        post('vc_preview:log', { message: 'Preview frame ready.' });
      }
    }

    // Warm loader readiness in the background to reduce first-mount latency.
    waitForLoaderReady(function (isReady) {
      if (!isReady || !latestConfig || mountQueuedUntilLoaderReady) {
        return;
      }
      mountCalculator(latestConfig);
    });
  }

  if (doc.readyState === 'loading') {
    doc.addEventListener('DOMContentLoaded', init, { once: true });
  } else {
    init();
  }
})(window, document);
