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

  // ── Storage key ──────────────────────────────────────────────
  const COOLDOWN_KEY = "vfx.cooldown";

  // ── State values added by this module ────────────────────────
  const STATES = Object.freeze({
    SERVER_COOLDOWN: "SERVER_COOLDOWN",
    WAITING_RETRY:   "WAITING_RETRY",
    AUDIO_BLOCKED:   "AUDIO_BLOCKED",
    AUDIO_READY:     "AUDIO_READY"
  });

  const DEFAULT_COOLDOWN_MS = 5 * 60 * 1000;
  const MAX_COOLDOWN_MS = 24 * 60 * 60 * 1000;
  const COOLDOWN_HINT = /log\s*in\s*after|try\s*again|too\s*many|please\s*wait|wait\s*before\s*retry|rate\s*limit|temporarily\s*block|retry\s*after/i;
  let mutationWatcher = null;
  let mutationTimer = null;

  function log(message) {
    console.log("[VisaFlowX][CooldownManager]", message);
  }

  // ─────────────────────────────────────────────────────────────
  // parseCooldownMessage()
  //   Accepts any text string (toast, alert, DOM text, API body).
  //   Returns { minutes, seconds, totalMs, source } or null.
  // ─────────────────────────────────────────────────────────────
  function clampCooldownMs(totalMs) {
    const value = Math.round(Number(totalMs) || 0);
    if (value <= 0) return 0;
    return Math.min(value, MAX_COOLDOWN_MS);
  }

  function parsedFromTotal(totalMs, source) {
    const safeTotalMs = clampCooldownMs(totalMs);
    if (!safeTotalMs) return null;
    const totalSeconds = Math.ceil(safeTotalMs / 1000);
    return {
      minutes: Math.floor(totalSeconds / 60),
      seconds: totalSeconds % 60,
      totalMs: safeTotalMs,
      source: String(source || "").replace(/\s+/g, " ").trim().slice(0, 240)
    };
  }

  function formatDuration(totalMs) {
    const totalSeconds = Math.max(0, Math.ceil((Number(totalMs) || 0) / 1000));
    const hours = Math.floor(totalSeconds / 3600);
    const minutes = Math.floor((totalSeconds % 3600) / 60);
    const seconds = totalSeconds % 60;
    const parts = [];
    if (hours) parts.push(`${hours}h`);
    if (minutes || hours) parts.push(`${minutes}m`);
    parts.push(`${seconds}s`);
    return parts.join(" ");
  }

  function normalizeParsedCooldown(parsed, now = Date.now()) {
    if (!parsed) return null;
    const totalMs = parsed.totalMs
      ? clampCooldownMs(parsed.totalMs)
      : clampCooldownMs((Number(parsed.minutes || 0) * 60 + Number(parsed.seconds || 0)) * 1000);
    const normalized = parsedFromTotal(totalMs, parsed.source || "");
    if (!normalized) return null;
    return {
      ...normalized,
      endsAt: Number(parsed.endsAt || 0) > now ? Number(parsed.endsAt) : now + normalized.totalMs,
      detectedAt: Number(parsed.detectedAt || 0) || now
    };
  }

  function parseCooldownMessage(text) {
    const raw = String(text || "").replace(/\s+/g, " ").trim();
    const lower = raw.toLowerCase();

    // No cooldown hint → bail early
    if (!COOLDOWN_HINT.test(lower)) {
      return null;
    }

    // ── Pattern 1: IVAC "log in after N minute(s) and M second(s)" ──
    const ivacMatch = lower.match(
      /log\s+in\s+after\s+([\d.]+)\s*minute(?:\(s\)|s)?(?:\s+and)?\s+([\d.]+)\s*second(?:\(s\)|s)?/i
    );
    if (ivacMatch) {
      return parsedFromTotal(((Number(ivacMatch[1]) || 0) * 60 + (Number(ivacMatch[2]) || 0)) * 1000, raw);
    }

    // ── Pattern 2: collect explicit duration units anywhere in the message ──
    const durationPattern = /([\d.]+)\s*(hours?|hrs?|h|minutes?|mins?|minute\(s\)|m(?!s)|seconds?|secs?|second\(s\)|s)\b/gi;
    let totalMs = 0;
    let match;
    while ((match = durationPattern.exec(lower))) {
      const amount = Number(match[1]) || 0;
      const unit = match[2];
      if (/^(h|hr|hour)/i.test(unit)) totalMs += amount * 60 * 60 * 1000;
      else if (/^(m|min|minute)/i.test(unit)) totalMs += amount * 60 * 1000;
      else totalMs += amount * 1000;
    }
    if (totalMs > 0) {
      return parsedFromTotal(totalMs, raw);
    }

    // ── Pattern 3: timer text, e.g. "00:07:32" or "07:32" ──
    const timer = lower.match(/\b(?:(\d{1,2}):)?(\d{1,2}):(\d{2})\b/);
    if (timer) {
      const hours = Number(timer[1] || 0);
      const minutes = Number(timer[2] || 0);
      const seconds = Number(timer[3] || 0);
      return parsedFromTotal(((hours * 3600) + (minutes * 60) + seconds) * 1000, raw);
    }

    // ── Pattern 4: bare integer after retry/wait wording means seconds. ──
    const bareMatch = lower.match(/(?:try\s+again\s+after|retry\s+after|please\s+wait|wait)\s+(\d{1,6})\b/i);
    if (bareMatch) {
      const seconds = Number(bareMatch[1]) || 0;
      return parsedFromTotal(seconds * 1000, raw);
    }

    // ── Pattern 5: blocks with no explicit time — default 5-min ──
    if (/too\s+many|temporarily\s+blocked|rate\s+limit|please\s+wait\s+before\s+retrying|wait\s+before\s+retry/i.test(lower)) {
      return parsedFromTotal(DEFAULT_COOLDOWN_MS, raw);
    }

    return null;
  }

  function parseApiResponseForCooldown(payload) {
    if (!payload) return null;
    if (typeof payload === "string") return parseCooldownMessage(payload);
    if (typeof Response !== "undefined" && payload instanceof Response) return null;
    if (typeof payload === "object") {
      const candidates = [
        payload.message,
        payload.error,
        payload.detail,
        payload.statusText,
        payload.responseText,
        payload.data?.message,
        payload.data?.error
      ];
      for (const candidate of candidates) {
        const parsed = parseCooldownMessage(candidate);
        if (parsed) return parsed;
      }
      try {
        return parseCooldownMessage(JSON.stringify(payload));
      } catch (_) {
        return null;
      }
    }
    return null;
  }

  // ─────────────────────────────────────────────────────────────
  // detectServerRetryCooldown()
  //   Scans the live DOM for cooldown messages in:
  //     - toasts / alerts / modals / inline messages
  //     - page body text
  //   Returns parsed cooldown object or null.
  // ─────────────────────────────────────────────────────────────
  function detectServerRetryCooldown() {
    if (typeof document === "undefined") return null;

    // High-priority selectors first
    const selectors = [
      "[role='alert']",
      ".toast", ".alert", ".notification", ".message", ".error",
      ".modal-body", ".modal-content",
      ".flash", ".banner",
      "p.error", "p.warning", "span.error", "div.error",
      "body"
    ];

    for (const selector of selectors) {
      const elements = Array.from(document.querySelectorAll(selector));
      for (const el of elements) {
        const text = el.textContent || el.innerText || "";
        if (!text.trim()) continue;
        const result = parseCooldownMessage(text);
        if (result) return result;
      }
    }
    return null;
  }

  function installServerCooldownWatcher(onDetected) {
    if (typeof document === "undefined" || typeof MutationObserver === "undefined" || mutationWatcher) {
      return false;
    }
    const target = document.documentElement || document.body;
    if (!target) return false;
    const scheduleScan = () => {
      if (mutationTimer) clearTimeout(mutationTimer);
      mutationTimer = setTimeout(() => {
        mutationTimer = null;
        const parsed = detectServerRetryCooldown();
        if (parsed && typeof onDetected === "function") onDetected(parsed);
      }, 100);
    };
    mutationWatcher = new MutationObserver(scheduleScan);
    mutationWatcher.observe(target, {
      childList: true,
      subtree: true,
      characterData: true,
      attributes: true
    });
    scheduleScan();
    return true;
  }

  function stopServerCooldownWatcher() {
    if (mutationWatcher) mutationWatcher.disconnect();
    mutationWatcher = null;
    if (mutationTimer) clearTimeout(mutationTimer);
    mutationTimer = null;
  }

  // ─────────────────────────────────────────────────────────────
  // Storage helpers (chrome.storage.local ↔ memory fallback)
  // ─────────────────────────────────────────────────────────────
  function hasChromeStorage() {
    return Boolean(
      (typeof chrome !== "undefined") && chrome?.storage?.local
    );
  }

  async function saveCooldownState(state) {
    const normalized = normalizeParsedCooldown(state) || state;
    const data = {
      active:    Boolean(state.active),
      state:     STATES.SERVER_COOLDOWN,
      endsAt:    Number(normalized.endsAt || state.endsAt || 0),
      minutes:   Number(normalized.minutes || state.minutes || 0),
      seconds:   Number(normalized.seconds || state.seconds || 0),
      totalMs:   Number(normalized.totalMs || state.totalMs || 0),
      source:    String(normalized.source || state.source || ""),
      detectedAt: Number(normalized.detectedAt || state.detectedAt || Date.now()),
      savedAt:   Date.now()
    };
    if (hasChromeStorage()) {
      await chrome.storage.local.set({ [COOLDOWN_KEY]: data });
    } else {
      (global.__VFXMemory = global.__VFXMemory || {})[COOLDOWN_KEY] = data;
    }
    return data;
  }

  async function loadCooldownState() {
    let raw = null;
    if (hasChromeStorage()) {
      const result = await chrome.storage.local.get(COOLDOWN_KEY);
      raw = result[COOLDOWN_KEY] || null;
    } else {
      raw = (global.__VFXMemory || {})[COOLDOWN_KEY] || null;
    }
    if (!raw || !raw.active) return null;
    // Discard if already expired
    if (Number(raw.endsAt || 0) <= Date.now()) {
      await clearCooldownState();
      return null;
    }
    return raw;
  }

  async function clearCooldownState() {
    if (hasChromeStorage()) {
      await chrome.storage.local.remove(COOLDOWN_KEY).catch(() => {});
    } else {
      delete (global.__VFXMemory || {})[COOLDOWN_KEY];
    }
  }

  // ─────────────────────────────────────────────────────────────
  // CooldownManager class
  // ─────────────────────────────────────────────────────────────
  class CooldownManager {
    constructor() {
      this._countdownTimer    = null;
      this._resumeCallback    = null;
      this._active            = false;
      this._endsAt            = 0;
      this._onTickCallback    = null;
    }

    // ── Public API ─────────────────────────────────────────────

    /**
     * Start a server-enforced cooldown.
     * @param {object} parsed  { minutes, seconds, totalMs, source }
     * @param {object} options { onTick(remaining), onResume() }
     */
    async start(parsed, options = {}) {
      const normalized = normalizeParsedCooldown(parsed);
      if (!normalized || !normalized.totalMs) return;

      this._clearTimer();
      this._active          = true;
      this._endsAt          = normalized.endsAt;
      this._onTickCallback  = options.onTick   || null;
      this._resumeCallback  = options.onResume || null;

      // Persist so popup close/reopen keeps the timer alive
      await saveCooldownState({
        active:  true,
        endsAt:  this._endsAt,
        minutes: normalized.minutes,
        seconds: normalized.seconds,
        totalMs: normalized.totalMs,
        source:  normalized.source || ""
      });

      log(`Server cooldown detected. Waiting ${formatDuration(normalized.totalMs)} (${normalized.totalMs}ms)`);
      log(`Login paused for ${Math.round(normalized.totalMs / 1000)} seconds`);
      log("Cooldown countdown active");

      this._startCountdown();
    }

    /** Re-attach to a persisted cooldown after popup reopen / SW restart. */
    async restore(options = {}) {
      const saved = await loadCooldownState();
      if (!saved) return false;

      this._active          = true;
      this._endsAt          = Number(saved.endsAt);
      this._onTickCallback  = options.onTick   || null;
      this._resumeCallback  = options.onResume || null;

      const remaining = this._endsAt - Date.now();
      log(`Cooldown restored. ${Math.round(remaining / 1000)}s remaining.`);
      this._startCountdown();
      return true;
    }

    isActive() {
      return this._active && this._endsAt > Date.now();
    }

    remainingMs() {
      return Math.max(0, this._endsAt - Date.now());
    }

    formatRemaining() {
      return formatDuration(this.remainingMs());
    }

    async cancel() {
      this._clearTimer();
      this._active = false;
      this._endsAt = 0;
      await clearCooldownState();
      log("Cooldown cancelled");
    }

    // ── Private helpers ────────────────────────────────────────

    _startCountdown() {
      this._clearTimer();
      const tick = () => {
        const remaining = this._endsAt - Date.now();

        if (this._onTickCallback) {
          try {
            this._onTickCallback(remaining, this.formatRemaining());
          } catch (_) {}
        }

        if (remaining <= 0) {
          this._onCooldownFinished();
          return;
        }

        this._countdownTimer = setTimeout(tick, 1000);
      };
      tick();
    }

    _clearTimer() {
      if (this._countdownTimer) clearTimeout(this._countdownTimer);
      this._countdownTimer = null;
    }

    async _onCooldownFinished() {
      this._clearTimer();
      this._active = false;
      await clearCooldownState();
      log("Cooldown finished");
      log("Resuming automation");
      if (this._resumeCallback) {
        try {
          await this._resumeCallback();
        } catch (err) {
          log(`Resume callback error: ${err.message}`);
        }
      }
    }
  }

  // Export a singleton + the helpers
  const cooldownManager = new CooldownManager();

  const CooldownManagerModule = Object.freeze({
    STATES,
    cooldownManager,
    parseCooldownMessage,
    detectServerRetryCooldown,
    saveCooldownState,
    loadCooldownState,
    clearCooldownState,
    parseApiResponseForCooldown,
    formatDuration,
    normalizeParsedCooldown,
    installServerCooldownWatcher,
    stopServerCooldownWatcher
  });

  global.VisaFlowX = Object.assign(global.VisaFlowX || {}, {
    CooldownManager: CooldownManagerModule,
    detectServerRetryCooldown: detectServerRetryCooldown
  });

  if (typeof module !== "undefined" && module.exports) {
    module.exports = CooldownManagerModule;
  }
})(typeof globalThis !== "undefined" ? globalThis : this);
