(function initVisaFlowXNotificationHandler(global) {
  "use strict";

  const { Sounds } = global.VisaFlowX;
  const AUDIO_STATE_KEY = "vfx.audio";
  const FALLBACK_NOTIFICATION_INTERVAL_MS = 8000;

  // ── Audio state ───────────────────────────────────────────────
  let alarm         = null;
  let muted         = false;
  let audioUnlocked = false;   // true after first successful play / user interaction
  let audioReady    = false;   // meet "Store: audioReady = true"
  let audioState    = "AUDIO_BLOCKED"; // "AUDIO_BLOCKED" | "AUDIO_READY"
  let pendingOtpAlarm = false; // replay alarm on next unlock
  let audioContext = null;
  let lastFallbackNotificationAt = 0;

  function persistAudioState() {
    if (typeof chrome === "undefined" || !chrome?.storage?.local) return Promise.resolve();
    return chrome.storage.local.set({
      [AUDIO_STATE_KEY]: {
        audioUnlocked,
        audioReady,
        audioState,
        updatedAt: Date.now()
      }
    }).catch(() => {});
  }

  async function restoreAudioState() {
    if (typeof chrome === "undefined" || !chrome?.storage?.local) return;
    const result = await chrome.storage.local.get(AUDIO_STATE_KEY).catch(() => ({}));
    const saved = result[AUDIO_STATE_KEY];
    if (!saved) return;
    audioUnlocked = saved.audioUnlocked === true;
    audioReady = saved.audioReady === true;
    audioState = saved.audioState === "AUDIO_READY" ? "AUDIO_READY" : "AUDIO_BLOCKED";
  }

  function markAudioState(nextState, unlocked) {
    audioState = nextState;
    audioUnlocked = unlocked === true;
    audioReady = unlocked === true;
    persistAudioState();
  }

  function hasActivePageGesture(event) {
    if (event && event.isTrusted !== true) return false;
    const activation = global.navigator?.userActivation;
    if (activation) return activation.isActive === true || activation.hasBeenActive === true;
    return event?.isTrusted === true;
  }

  function markBlockedFallback(options = {}) {
    markAudioState("AUDIO_BLOCKED", false);
    if (options.pendingOtp === true) pendingOtpAlarm = true;
    if (options.notify === true) {
      _sendFallbackNotification({ force: options.force === true });
      _flashBadge();
      _dispatchPopupPulse();
      _vibrate();
    }
    return {
      ok: false,
      blocked: true,
      fallback: options.notify === true,
      error: "Audio autoplay blocked by browser. Using fallback notification until audio is enabled."
    };
  }

  // ── AudioContext unlock ───────────────────────────────────────
  // Called only from real page gestures. Popup/runtime messages cannot grant
  // autoplay permission for the IVAC page, so they must not resume this context.
  async function unlockAudioContext(event) {
    if (!hasActivePageGesture(event)) {
      return { ok: false, ignored: true, reason: "audio_unlock_requires_page_gesture", audioState };
    }

    const retryPendingAlarm = pendingOtpAlarm;
    let unlocked = false;
    try {
      const AudioContextClass = global.AudioContext || global.webkitAudioContext;
      if (AudioContextClass) {
        audioContext = audioContext || new AudioContextClass();
        if (audioContext.state === "suspended") {
          await audioContext.resume();
        }
        unlocked = audioContext.state === "running";
      }
    } catch (_) {}

    if (unlocked || audioUnlocked) {
      markAudioState("AUDIO_READY", true);
      console.log("[VisaFlowX][NotificationHandler] AudioContext unlocked");
      console.log("[VisaFlowX][NotificationHandler] Audio unlocked");

      if (retryPendingAlarm) {
        forcePlayOtpAlarm().catch(() => {});
      }
      return { ok: true, audioState };
    }

    markAudioState("AUDIO_BLOCKED", false);
    if (retryPendingAlarm) {
      forcePlayOtpAlarm().catch(() => {});
    }
    return { ok: false, blocked: true, audioState };
  }

  // Create unlockAudioEngine() alias to comply with exact instruction
  function unlockAudioEngine(event) {
    return unlockAudioContext(event);
  }

  // Attach one-time unlock listeners to the document (content script context)
  function attachUnlockListeners() {
    const opts = { once: false, capture: true, passive: true };
    const handler = (event) => {
      if (event?.isTrusted !== true) return;
      unlockAudioContext(event).catch(() => {});
    };
    if (typeof document !== "undefined") {
      document.addEventListener("pointerdown", handler, opts);
      document.addEventListener("keydown", handler, opts);
      document.addEventListener("touchstart", handler, opts);
    }
  }
  // Attach at module init
  if (typeof document !== "undefined") {
    attachUnlockListeners();
  }
  restoreAudioState().catch(() => {});

  // ── Internal: try to play audio once ────────────────────────
  async function _tryPlay(volume) {
    if (!audioUnlocked && !hasActivePageGesture()) {
      return markBlockedFallback();
    }
    _stopAudioElement();
    alarm        = new Audio(Sounds.alarmUrl());
    alarm.loop   = true;
    alarm.volume = muted ? 0 : Sounds.clampVolume(volume);
    await alarm.play();
    markAudioState("AUDIO_READY", true);
    return { ok: true };
  }

  // ── playAlarm (existing API — non-breaking) ───────────────────
  async function playAlarm({ volume = 1, muted: shouldMute = false } = {}) {
    muted = shouldMute === true;
    try {
      return await _tryPlay(volume);
    } catch (err) {
      // Browser autoplay blocked
      pendingOtpAlarm = false; // regular alarm, not OTP
      return markBlockedFallback();
    }
  }

  // ── forcePlayOtpAlarm ─────────────────────────────────────────
  // Attempts to play the OTP alarm. If blocked, marks pendingOtpAlarm=true
  // so it fires automatically after the first user interaction.
  // Also dispatches fallback notifications immediately.
  let _otpAlarmRetryTimer = null;

  async function forcePlayOtpAlarm(options = {}) {
    const volume = options.volume ?? 1;
    muted = options.muted === true ? true : muted;

    // Clear any existing OTP retry loop
    if (_otpAlarmRetryTimer) clearTimeout(_otpAlarmRetryTimer);
    _otpAlarmRetryTimer = null;

    if (!audioUnlocked && !hasActivePageGesture()) {
      console.log("[VisaFlowX][NotificationHandler] Audio pending until page interaction");
      markBlockedFallback({ pendingOtp: true, notify: true, force: true });
      return {
        ok: false,
        blocked: true,
        fallback: true,
        error: "Audio autoplay blocked by browser. Using fallback notification until audio is enabled."
      };
    }

    try {
      await _tryPlay(volume);
      console.log("[VisaFlowX][NotificationHandler] OTP alarm playing");
      console.log("[VisaFlowX][NotificationHandler] Playing OTP alarm");
      pendingOtpAlarm = false;
      return { ok: true };
    } catch (err) {
      pendingOtpAlarm = true;

      console.log("[VisaFlowX][NotificationHandler] Audio blocked by browser. Using visual OTP alert");
      console.log("[VisaFlowX][NotificationHandler] Using fallback notification until audio is enabled");

      // ── Fallback 1: Browser notification ────────────────────
      _sendFallbackNotification({ force: true });

      // ── Fallback 2: Badge flash via service worker ───────────
      _flashBadge();

      // ── Fallback 3: Popup pulse — dispatch custom event ──────
      _dispatchPopupPulse();
      _vibrate();

      // Retry only after a trusted page gesture unlocks audio.

      return {
        ok:       false,
        blocked:  true,
        fallback: true,
        error:    "Audio autoplay blocked by browser. Retrying alarm playback. Using fallback notification until audio is enabled."
      };
    }
  }

  // Create playOtpAlert() alias to comply with exact instruction
  async function playOtpAlert(options = {}) {
    return await forcePlayOtpAlarm(options);
  }

  function _scheduleOtpRetry(volume) {
    if (_otpAlarmRetryTimer) clearTimeout(_otpAlarmRetryTimer);
    _otpAlarmRetryTimer = setTimeout(async () => {
      _otpAlarmRetryTimer = null;
      if (!pendingOtpAlarm) return; // already dismissed
      if (!audioUnlocked && !hasActivePageGesture()) {
        _sendFallbackNotification();
        _dispatchPopupPulse();
        if (pendingOtpAlarm) _scheduleOtpRetry(volume);
        return;
      }
      try {
        await _tryPlay(volume);
        pendingOtpAlarm = false;
        console.log("[VisaFlowX][NotificationHandler] OTP alarm resumed after retry");
      } catch (_) {
        // Still blocked — keep dispatching fallbacks and reschedule
        markAudioState("AUDIO_BLOCKED", false);
        _sendFallbackNotification();
        _dispatchPopupPulse();
        _vibrate();
      }
    }, 2000);
  }

  function _sendFallbackNotification(options = {}) {
    const now = Date.now();
    if (!options.force && now - lastFallbackNotificationAt < FALLBACK_NOTIFICATION_INTERVAL_MS) return;
    lastFallbackNotificationAt = now;
    if (typeof chrome !== "undefined" && chrome?.runtime?.sendMessage) {
      chrome.runtime.sendMessage({
        type:    "OTP_FALLBACK_NOTIFICATION",
        message: "OTP page detected — complete your verification now!"
      }).catch(() => {});
    }
  }

  function _flashBadge() {
    if (typeof chrome !== "undefined" && chrome?.runtime?.sendMessage) {
      chrome.runtime.sendMessage({ type: "OTP_BADGE_FLASH" }).catch(() => {});
    }
  }

  function _dispatchPopupPulse() {
    if (typeof document !== "undefined") {
      document.dispatchEvent(new CustomEvent("VFX_OTP_PULSE", { bubbles: true }));
    }
    if (typeof chrome !== "undefined" && chrome?.runtime?.sendMessage) {
      chrome.runtime.sendMessage({ type: "OTP_POPUP_PULSE" }).catch(() => {});
    }
  }

  function _vibrate() {
    try {
      if (hasActivePageGesture() && global.navigator?.vibrate) global.navigator.vibrate([200, 100, 200]);
    } catch (_) {}
  }

  function _stopAudioElement() {
    if (!alarm) return;
    alarm.pause();
    alarm.currentTime = 0;
    alarm = null;
  }

  // ── Stop / mute / volume (unchanged API) ─────────────────────
  function stopAlarm() {
    if (_otpAlarmRetryTimer) clearTimeout(_otpAlarmRetryTimer);
    _otpAlarmRetryTimer = null;
    pendingOtpAlarm     = false;

    _stopAudioElement();
  }

  function muteAlarm() {
    muted = true;
    if (alarm) alarm.volume = 0;
  }

  function setVolume(volume) {
    muted = false;
    if (alarm) alarm.volume = Sounds.clampVolume(volume);
  }

  function isPlaying() {
    return Boolean(alarm);
  }

  function getAudioState() {
    return audioState;
  }

  // ── Runtime compatibility ─────────────────────────────────────
  // Keep the old message non-fatal, but do not touch AudioContext here because
  // Chrome requires the gesture to happen in this page's document.
  if (typeof chrome !== "undefined" && chrome?.runtime?.onMessage) {
    chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
      if (message?.type === "UNLOCK_AUDIO_CONTEXT") {
        sendResponse?.({ ok: true, ignored: true, reason: "audio_unlock_requires_page_gesture" });
      }
    });
  }

  const NotificationHandler = Object.freeze({
    isPlaying,
    muteAlarm,
    playAlarm,
    setVolume,
    stopAlarm,
    unlockAudioContext,
    unlockAudioEngine,
    forcePlayOtpAlarm,
    playOtpAlert,
    getAudioState
  });

  global.VisaFlowX = Object.assign(global.VisaFlowX || {}, {
    NotificationHandler,
    unlockAudioContext,
    unlockAudioEngine,
    forcePlayOtpAlarm,
    playOtpAlert
  });
})(typeof globalThis !== "undefined" ? globalThis : this);
