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

  const { Parser, Storage } = global.VisaFlowX;
  // Access lazily so dynamic reinjection order remains non-fatal.
  const getCooldownManager = () => global.VisaFlowX?.CooldownManager?.cooldownManager || null;

  const TERMINAL_STATES = new Set(["IDLE", "STOPPED", "SCHEDULED", "OTP_DETECTED", "OTP_REQUIRED", "SERVER_COOLDOWN"]);
  const PAUSED_STATES   = new Set(["WAITING_VERIFICATION"]);

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

  class RetryEngine {
    constructor(controller) {
      this.controller = controller;
      this.pending    = false;
      this.attempt    = 0;
      this.endsAt     = null;
      this.timer      = null;
      this.sequence   = 0;
    }

    reset() {
      if (this.timer) clearTimeout(this.timer);
      this.timer    = null;
      this.sequence += 1;
      this.pending   = false;
      this.attempt   = 0;
      this.endsAt    = null;
    }

    hasPendingRetry() {
      return this.pending;
    }

    async getRuntimeStatus() {
      try {
        const state = await Storage.ensureDefaults();
        return state.status || {};
      } catch (_) {
        return {};
      }
    }

    async isBlockedByRuntimeState() {
      const status = await this.getRuntimeStatus();

      // Block if server cooldown is active in storage
      if (status.state === "SERVER_COOLDOWN") {
        log("Retry loop blocked: server cooldown active");
        this.controller.pauseForServerCooldown?.();
        return true;
      }

      if (PAUSED_STATES.has(status.state || "")) {
        log("Retry loop blocked due to active verification");
        return true;
      }
      if (!TERMINAL_STATES.has(status.state || "")) return false;

      if (status.state === "OTP_DETECTED") {
        this.controller.otpLocked = true;
        log("Reload blocked due to OTP");
      }

      if (
        status.state === "STOPPED" ||
        status.state === "IDLE"    ||
        status.state === "SCHEDULED"
      ) {
        log(`Retry loop blocked due to ${status.state}`);
      }

      this.controller.stop(`state_${String(status.state || "blocked").toLowerCase()}`, { silent: true });
      return true;
    }

    wait(delayMs, retryToken) {
      return new Promise((resolve) => {
        this.timer = setTimeout(() => {
          this.timer = null;
          resolve(retryToken === this.sequence);
        }, Math.max(0, Number(delayMs) || 0));
      });
    }

    async handlePageState(pageState, retrySettings) {
      if (this.hasPendingRetry()) return true;
      if (await this.isBlockedByRuntimeState()) return true;

      // ── SERVER COOLDOWN DETECTION ────────────────────────────────
      // Try CooldownManager first (richer parser with minutes/seconds)
      const CooldownModule = global.VisaFlowX?.CooldownManager;
      if (CooldownModule) {
        // Parse from page text
        const parsed = CooldownModule.parseCooldownMessage(pageState.text || "");
        if (parsed) {
          await this._handleServerCooldown(parsed, retrySettings);
          return true;
        }
        // Also scan the live DOM for injected messages
        const domParsed = CooldownModule.detectServerRetryCooldown();
        if (domParsed) {
          await this._handleServerCooldown(domParsed, retrySettings);
          return true;
        }
      } else {
        // Fallback to legacy parser
        const retryDelay = Parser.parseRetryDelay(pageState.text);
        if (retryDelay) {
          await this.schedule(retryDelay.ms, `Cooldown detected: ${retryDelay.source}`, "cooldown", retrySettings);
          return true;
        }
      }

      if (pageState.error) {
        await this.schedule(0, `Recovering from ${pageState.error}`, "recover", retrySettings);
        return true;
      }
      return false;
    }

    // ── New: handle a confirmed server cooldown ──────────────────
    async _handleServerCooldown(parsed, retrySettings) {
      const CooldownModule = global.VisaFlowX?.CooldownManager;
      const normalized = CooldownModule?.normalizeParsedCooldown?.(parsed) || parsed;
      const { minutes, seconds, totalMs } = normalized;
      const label = CooldownModule?.formatDuration
        ? CooldownModule.formatDuration(totalMs)
        : (minutes > 0 ? `${minutes}m ${seconds}s` : `${seconds}s`);
      const endsAt = Number(normalized.endsAt || (Date.now() + totalMs));

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

      // Immediately stop retries
      this.pending = false;
      if (this.timer) { clearTimeout(this.timer); this.timer = null; }

      // Persist + start local countdown as a secondary resume signal. The
      // service worker remains the source of truth for state and reloads.
      const cm = getCooldownManager();
      if (cm) {
        await cm.start(normalized, {
          onResume: () => this._resumeAfterCooldown(retrySettings)
        }).catch(() => {});
      }

      const response = await chrome.runtime.sendMessage({
        type: "SERVER_COOLDOWN_DETECTED",
        cooldown: {
          ...normalized,
          endsAt,
          source: normalized.source || parsed.source || ""
        },
        url: location.href
      }).catch((error) => ({ ok: false, error: error?.message || String(error || "cooldown_signal_failed") }));

      this.controller.pauseForServerCooldown?.();

      if (!response?.ok) {
        await this.controller.updateStatus({
          state:               "SERVER_COOLDOWN",
          retryCountdownEndsAt: endsAt,
          lastAction:          `Server cooldown detected. Retrying in ${label}`,
          lastError:           ""
        }, { notify: "" });
      }
    }

    async _resumeAfterCooldown(retrySettings) {
      const response = await chrome.runtime.sendMessage({
        type: "SERVER_COOLDOWN_FINISHED"
      }).catch((error) => ({ ok: false, error: error?.message || String(error || "cooldown_resume_failed") }));
      if (response?.ok) return;

      // Safety: check OTP not detected meanwhile
      const status = await this.getRuntimeStatus();
      if (status.state === "OTP_DETECTED" || status.state === "OTP_REQUIRED" || this.controller?.otpLocked) {
        log("Cooldown finished — resume blocked (OTP locked)");
        return;
      }
      log("Cooldown finished");
      log("Resuming automation");

      this.pending = false;
      await this.schedule(0, "Cooldown finished. Resuming...", "cooldown", retrySettings || {});
    }

    async schedule(delayMs, reason, mode, retrySettings) {
      if (this.pending) return;
      if (await this.isBlockedByRuntimeState()) return;

      this.attempt += 1;
      this.pending  = true;
      log("Retry loop started");

      const retryToken = this.sequence;
      const runToken   = this.controller.runToken;
      const endsAt     = delayMs > 0 ? Date.now() + delayMs : null;
      this.endsAt      = endsAt;

      const state = mode === "cooldown" ? "WAITING_RETRY" : "RETRYING";

      await this.controller.updateStatus({
        state:               state,
        retryCountdownEndsAt: endsAt,
        lastAction:          reason,
        lastError:           ""
      }, { notify: mode === "cooldown" ? "" : "retry" });

      if (delayMs > 0) {
        console.log(`[VisaFlowX] Cooldown detected. Waiting until ${new Date(endsAt).toLocaleTimeString()}`);
        if (delayMs > 15000) {
          this.controller.stop("cooldown", { silent: true });
          return;
        }
        const stillCurrent = await this.wait(delayMs, retryToken);
        if (!stillCurrent || !this.controller.isCurrentRun(runToken)) {
          this.pending = false;
          return;
        }
      }

      if (!this.controller.isCurrentRun(runToken) || await this.isBlockedByRuntimeState()) {
        this.pending = false;
        return;
      }

      try {
        await this.perform(mode, retrySettings, reason, runToken);
      } catch (error) {
        if (!this.controller.isCurrentRun(runToken)) return;
        this.pending = false;
        await this.controller.updateStatus({
          state:               "RETRYING",
          retryCountdownEndsAt: null,
          lastAction:          "Retry failed. Retrying again.",
          lastError:           error.message
        });
        await this.schedule(0, `Recovering from retry failure: ${error.message}`, "reload", retrySettings);
      }
    }

    async perform(mode, retrySettings, reason, runToken) {
      if (!this.controller.isCurrentRun(runToken) || await this.isBlockedByRuntimeState()) {
        this.pending = false;
        return;
      }

      await this.controller.updateStatus({
        state:               "RETRYING",
        retryCountdownEndsAt: null,
        lastAction:          mode === "cooldown" ? "Cooldown finished. Resuming..." : `Retry attempt ${this.attempt}`,
        lastError:           ""
      });

      let targetUrl  = "";
      let reloadMode = retrySettings.refreshMode === "hard" ? "hard" : "soft";

      if (mode === "recover" || mode === "cooldown") {
        targetUrl = "https://appointment.ivacbd.com/signin";
      } else if (reloadMode === "hard") {
        const url = new URL(location.href);
        url.searchParams.set("_vfx_retry", String(Date.now()));
        targetUrl = url.toString();
      }

      const response = await chrome.runtime.sendMessage({
        type:   "REQUEST_RELOAD",
        mode:   reloadMode,
        url:    targetUrl,
        reason: reason || `Retry attempt ${this.attempt}`
      });

      if (!response?.ok) {
        throw new Error(response?.error || "Reload request failed");
      }

      if (response.blocked) {
        this.pending = false;
        if (response.terminal) this.controller.stop("reload_blocked", { silent: true });
      }
    }
  }

  global.VisaFlowX = Object.assign(global.VisaFlowX || {}, { RetryEngine });
})(typeof globalThis !== "undefined" ? globalThis : this);
