/* global chrome, importScripts */
"use strict";

importScripts(
  "../utils/site-context.js",
  "../utils/storage.js",
  "../utils/scheduler.js",
  "../utils/notifications.js",
  "../utils/parser.js",
  "../utils/cooldownManager.js",
  "../utils/timers.js",
  "../fiva/config/runtimeConfig.js",
  "../fiva/utils/fivaStateMachine.js",
  "../fiva/runtime/fivaRuntimeStore.js",
  "../fiva/utils/cleanOCRText.js",
  "../fiva/utils/normalizeOptionText.js",
  "../fiva/utils/strictDobValidator.js",
  "../fiva/utils/normalizeParsedData.js",
  "../fiva/utils/normalizeProfile.js",
  "../fiva/utils/fieldIntentResolver.js",
  "../fiva/utils/smartVisaProfileEngine.js",
  "challenge-state.js"
);

const { ChallengeState, CooldownManager, Notifications, Scheduler, SiteContext, Storage } = self.VisaFlowX;

const IVAC_SIGNIN_URL = SiteContext.URLS.IVAC_SIGNIN;
const FIVA_REGISTRATION_URL = SiteContext.URLS.FIVA_REGISTRATION;
const FIVA_SESSION_KEY = "fiva.autofillSession";
const FIVA_RUNTIME_STORE_KEY = "fiva.runtimeStore";
const FIVA_LEGACY_PROFILE_KEY = "fiva.profile";
const FIVA_LEGACY_ACTIVITY_KEY = "fiva.activity";
const FIVA_LEGACY_RUNTIME_KEY = "fiva.runtime";
const FIVA_CONTENT_FILES = [
  "fiva/config/runtimeConfig.js",
  "fiva/utils/fivaStateMachine.js",
  "fiva/utils/cleanOCRText.js",
  "fiva/utils/normalizeOptionText.js",
  "fiva/utils/strictDobValidator.js",
  "fiva/utils/normalizeParsedData.js",
  "fiva/utils/normalizeProfile.js",
  "fiva/utils/fieldIntentResolver.js",
  "fiva/utils/smartVisaProfileEngine.js",
  "fiva/utils/detectFieldType.js",
  "fiva/utils/selectSmartOption.js",
  "fiva/utils/humanDropdownSelect.js",
  "fiva/utils/humanSelectOption.js",
  "fiva/form-engine/validators.js",
  "fiva/utils/hardPaste.js",
  "fiva/utils/hardPasteEngine.js",
  "fiva/form-engine/dropdownEngine.js",
  "fiva/form-engine/content.js"
];

const CONTENT_FILES = [
  "utils/storage.js",
  "utils/sounds.js",
  "utils/timers.js",
  "utils/parser.js",
  "utils/cooldownManager.js",
  "utils/dom-utils.js",
  "content/notification-handler.js",
  "content/detector.js",
  "otp/otpTestProvider.js",
  "otp/otpInputEngine.js",
  "content/retry-engine.js",
  "content/otp-monitor.js",
  "content/captcha-solver.js",
  "content/automation.js"
];

const LOG_PREFIX = "[VisaFlowX]";
const FIVA_STATES = self.FIVA?.FivaStateMachine?.STATES || {};
const COOLDOWN_ALARM = "vfx-cooldown-resume";
const RELOAD_GUARD_MS = 800;
const RESUME_GUARD_MS = 500;
const STALE_VERIFICATION_MS = 120000;
const VERIFICATION_RECONCILE_MS = 500;
const VERIFICATION_FRAME_POLL_MS = 150;
const TOP_FRAME_ID = 0;

const STATES = Object.freeze({
  IDLE: "IDLE",
  RUNNING: "RUNNING",
  WAITING_VERIFICATION: "WAITING_VERIFICATION",
  WAITING_COOLDOWN: "WAITING_COOLDOWN",
  WAITING_RETRY: "WAITING_RETRY",
  SERVER_COOLDOWN: "SERVER_COOLDOWN",
  RETRYING: "RETRYING",
  RELOADING: "RELOADING",
  STOPPED: "STOPPED",
  OTP_DETECTED: "OTP_DETECTED",
  OTP_REQUIRED: "OTP_REQUIRED",
  SCHEDULED: "SCHEDULED",
  ERROR: "ERROR",
  COMPLETED: "COMPLETED",
  AUDIO_BLOCKED: "AUDIO_BLOCKED",
  AUDIO_READY: "AUDIO_READY"
});

const ACTIVE_AUTOMATION_STATES = new Set([
  STATES.RUNNING,
  STATES.WAITING_VERIFICATION,
  STATES.WAITING_COOLDOWN,
  STATES.WAITING_RETRY,
  STATES.SERVER_COOLDOWN,
  STATES.RETRYING,
  STATES.RELOADING,
  STATES.ERROR
]);

const EXPLICIT_START_SOURCES = new Set(["manual", "popup", "hotkey"]);

const AutomationController = Object.freeze({
  isActive(status = {}) {
    return ACTIVE_AUTOMATION_STATES.has(status.state || "");
  },
  isOtpLocked(status = {}) {
    return status.state === STATES.OTP_DETECTED || status.state === STATES.OTP_REQUIRED || status.otpState === "Detected";
  },
  isStopped(status = {}) {
    return status.state === STATES.STOPPED;
  },
  isServerCooldownActive(status = {}) {
    return status.state === STATES.SERVER_COOLDOWN
      && Number(status.retryCountdownEndsAt || 0) > Date.now();
  },
  isExplicitStart(source = "manual") {
    return EXPLICIT_START_SOURCES.has(source);
  },
  canStart(status = {}, source = "manual") {
    if (this.isServerCooldownActive(status) && source !== "cooldown_end") return false;
    if (this.isOtpLocked(status) && !this.isExplicitStart(source)) return false;
    if (this.isStopped(status) && source === "schedule") return false;
    if (this.isActive(status) && source === "schedule") return false;
    return true;
  },
  canAutoResume(status = {}) {
    if (this.isServerCooldownActive(status)) return false;
    return this.isActive(status) && !this.isOtpLocked(status);
  },
  canReload(status = {}) {
    return this.isActive(status)
      && !this.isOtpLocked(status)
      && status.state !== STATES.WAITING_VERIFICATION
      && status.state !== STATES.WAITING_COOLDOWN
      && status.state !== STATES.SERVER_COOLDOWN
      && status.state !== STATES.STOPPED;
  },
  blocksContentPatch(currentStatus = {}, incomingState = "") {
    if (this.isServerCooldownActive(currentStatus)) {
      return incomingState && ![STATES.SERVER_COOLDOWN, STATES.OTP_DETECTED, STATES.OTP_REQUIRED].includes(incomingState);
    }
    if (this.isOtpLocked(currentStatus)) {
      return incomingState && incomingState !== STATES.OTP_DETECTED && incomingState !== STATES.OTP_REQUIRED && incomingState !== STATES.RUNNING;
    }
    if (this.isStopped(currentStatus)) {
      return incomingState === STATES.IDLE || this.isActive({ state: incomingState });
    }
    if (currentStatus.state === STATES.SCHEDULED && incomingState === STATES.IDLE) {
      return true;
    }
    return false;
  }
});

let automationResumeBusy = false;
let automationStartBusy = false;
let reloadTimer = null;
let reloadTimerTabId = null;
const lastResumeAtByTabId = new Map();
const fivaReadyTabs = new Set();
const challengeState = ChallengeState.createManager({
  cooldownMs: () => 45000 + Math.random() * 30000,
  duplicateWindowMs: 2500,
  maxRetryCount: 5
});
const verificationStaleTimers = new Map();
const verificationFramePollTimers = new Map();
const verificationReconcileTimers = new Map();
const FIVA_SENSITIVE_FIELDS = new Set([
  "dateOfBirth",
  "dob",
  "expectedArrivalDate",
  "arrivalDate",
  "passportIssueDate",
  "passportExpiryDate",
  "nationality",
  "email",
  "reEnterEmail",
  "fullName",
  "givenNames",
  "givenName",
  "surname",
  "passportNumber",
  "gender"
]);

function log(message, details) {
  if (details === undefined) {
    console.log(LOG_PREFIX, message);
    return;
  }
  console.log(LOG_PREFIX, message, details);
}

function logVerification(event, details = {}) {
  console.log("[VisaFlowX][Verification]", event, details);
}

function isIvacUrl(url = "") {
  return SiteContext.isIvacUrl(url);
}

function isFivaUrl(url = "") {
  return SiteContext.isFivaUrl(url);
}

function isAutomationActive(status = {}) {
  return AutomationController.isActive(status);
}

function normalizeStatusPatch(currentStatus = {}, patch = {}, options = {}) {
  const next = { ...patch };
  const incomingState = next.state || "";

  if (!options.force && options.origin === "content" && AutomationController.blocksContentPatch(currentStatus, incomingState)) {
    log(AutomationController.isOtpLocked(currentStatus) ? "Reload blocked due to OTP" : `Content state ignored after ${currentStatus.state}`);
    return {};
  }

  if (!options.force && AutomationController.isOtpLocked(currentStatus) && incomingState && incomingState !== STATES.OTP_DETECTED && incomingState !== STATES.OTP_REQUIRED && incomingState !== STATES.RUNNING) {
    log("Reload blocked due to OTP");
    return {};
  }

  if (!isAutomationActive(currentStatus)) return next;

  if (next.state === STATES.IDLE && /content script ready/i.test(next.lastAction || "")) {
    delete next.state;
  }

  if (next.state === STATES.WAITING_COOLDOWN && next.retryCountdownEndsAt && next.retryCountdownEndsAt <= Date.now()) {
    next.state = STATES.RELOADING;
    next.lastAction = "Cooldown finished. Resuming...";
  }

  if (next.state === STATES.COMPLETED) {
    next.state = STATES.RUNNING;
    next.otpState = next.otpState || "Waiting";
    next.lastAction = "Signed in. Waiting for OTP page.";
  }

  if (next.state === STATES.ERROR) {
    next.state = STATES.RETRYING;
    next.lastAction = next.lastAction === "Error" ? "Retrying after temporary automation error" : (next.lastAction || "Retrying");
  }

  return next;
}

function clearReloadTimer() {
  if (reloadTimer) clearTimeout(reloadTimer);
  reloadTimer = null;
  reloadTimerTabId = null;
}

function clearVerificationStaleTimer(tabId) {
  const timer = verificationStaleTimers.get(Number(tabId));
  if (timer) clearTimeout(timer);
  verificationStaleTimers.delete(Number(tabId));
}

function clearVerificationFramePoll(tabId) {
  const timer = verificationFramePollTimers.get(Number(tabId));
  if (timer) clearInterval(timer);
  verificationFramePollTimers.delete(Number(tabId));
}

function clearVerificationReconcileTimer(tabId) {
  const timer = verificationReconcileTimers.get(Number(tabId));
  if (timer) clearTimeout(timer);
  verificationReconcileTimers.delete(Number(tabId));
}

function isVerificationFrame(frame = {}) {
  return Number(frame.frameId) !== TOP_FRAME_ID && SiteContext.isCloudflareUrl(frame.url || "");
}

async function readVerificationFrameState(tabId) {
  const frames = await chrome.webNavigation.getAllFrames({ tabId }).catch(() => []);
  const verificationFrames = (frames || []).filter(isVerificationFrame);
  let fallbackState = null;
  for (const frame of verificationFrames) {
    const response = await chrome.tabs.sendMessage(
      tabId,
      { type: "GET_CHALLENGE_STATE" },
      { frameId: frame.frameId }
    ).catch(() => null);
    if (response?.ok && response.challenge) {
      const frameState = {
        ...response.challenge,
        frameId: frame.frameId,
        url: response.url || frame.url || "",
        source: response.challenge.source || "cf_frame_poll"
      };
      if (frameState.completed === true) return frameState;
      if (!fallbackState || frameState.active === true) fallbackState = frameState;
    }
  }
  return fallbackState;
}

async function pollVerificationFrameState(tabId) {
  const state = await Storage.ensureDefaults().catch(() => null);
  if (
    !state
    || state.status.activeTabId !== tabId
    || state.status.state !== STATES.WAITING_VERIFICATION
    || !challengeState.isActive(tabId)
    || AutomationController.isOtpLocked(state.status)
  ) {
    clearVerificationFramePoll(tabId);
    return;
  }

  const frameState = await readVerificationFrameState(tabId);
  if (!frameState) return;

  if (frameState.completed === true) {
    logVerification("frame_completed", {
      tabId,
      frameId: frameState.frameId,
      selector: frameState.selector || "",
      url: frameState.url || ""
    });
    await handleChallengeState({
      active: false,
      completed: true,
      selector: frameState.selector || "cf-frame-completed",
      source: "frame_poll",
      url: frameState.url || state.status.page || "",
      tabId
    }, { tab: { id: tabId, url: state.status.page || "" } });
  }
}

async function reconcileVerificationState(tabId, reason = "verification_reconcile") {
  const state = await Storage.ensureDefaults().catch(() => null);
  if (
    !state
    || state.status.activeTabId !== tabId
    || state.status.state !== STATES.WAITING_VERIFICATION
    || !challengeState.isActive(tabId)
    || AutomationController.isOtpLocked(state.status)
  ) {
    clearVerificationReconcileTimer(tabId);
    return { ok: true, skipped: true };
  }

  const tab = await chrome.tabs.get(tabId).catch(() => null);
  if (!tab?.id || !isIvacUrl(tab.url || tab.pendingUrl || state.status.page || "")) {
    return clearChallengeForTab(tabId, "Verification cleared after leaving IVAC page");
  }

  const frameState = await readVerificationFrameState(tabId);
  if (frameState?.completed === true) {
    logVerification("watchdog_resolved", {
      tabId,
      frameId: frameState.frameId,
      selector: frameState.selector || "",
      reason
    });
    return handleChallengeState({
      active: false,
      completed: true,
      selector: frameState.selector || "cf-frame-completed",
      source: reason,
      url: frameState.url || tab.url || "",
      tabId
    }, { tab });
  }

  const topState = await readContentVerificationState(tabId);
  const verification = topState?.verification || {};
  if (verification.completed === true) {
    logVerification("watchdog_resolved", {
      tabId,
      selector: verification.selector || "",
      reason: "top_frame_completed"
    });
    return handleChallengeState({
      active: false,
      completed: true,
      selector: verification.selector || "cf-turnstile-response",
      source: "top_frame_completed",
      url: tab.url || "",
      tabId
    }, { tab });
  }

  const snapshot = challengeState.snapshot(tabId);
  const waitingMs = Date.now() - Number(snapshot?.firstSeenAt || Date.now());
  const topStateAvailable = topState?.ok === true;
  const noVisibleChallenge = topStateAvailable
    && verification.present !== true
    && verification.pending !== true
    && frameState?.active !== true;
  if (waitingMs >= VERIFICATION_RECONCILE_MS && noVisibleChallenge) {
    logVerification("watchdog_resolved", {
      tabId,
      selector: verification.selector || "verification_no_longer_visible",
      reason: "challenge_disappeared",
      waitingMs
    });
    return handleChallengeState({
      active: false,
      completed: true,
      selector: verification.selector || "verification_no_longer_visible",
      source: "challenge_disappeared",
      url: tab.url || state.status.page || "",
      tabId
    }, { tab });
  }

  logVerification("watchdog_still_waiting", {
    tabId,
    frameState: frameState
      ? { active: frameState.active === true, completed: frameState.completed === true, selector: frameState.selector || "" }
      : null,
    topState: verification
      ? { present: verification.present === true, completed: verification.completed === true, pending: verification.pending === true, selector: verification.selector || "" }
      : null
  });
  scheduleVerificationReconcile(tabId);
  return { ok: true, active: true };
}

function scheduleVerificationReconcile(tabId) {
  if (!tabId || verificationReconcileTimers.has(Number(tabId))) return;
  const timer = setTimeout(() => {
    verificationReconcileTimers.delete(Number(tabId));
    reconcileVerificationState(tabId, "watchdog_fast").catch((error) => {
      logVerification("watchdog_error", { tabId, error: error.message });
    });
  }, VERIFICATION_RECONCILE_MS);
  verificationReconcileTimers.set(Number(tabId), timer);
}

function startVerificationFramePoll(tabId) {
  if (!tabId || verificationFramePollTimers.has(Number(tabId))) return;
  logVerification("frame_poll_started", { tabId });
  const timer = setInterval(() => {
    pollVerificationFrameState(tabId).catch((error) => {
      logVerification("frame_poll_error", { tabId, error: error.message });
    });
  }, VERIFICATION_FRAME_POLL_MS);
  verificationFramePollTimers.set(Number(tabId), timer);
  pollVerificationFrameState(tabId).catch(() => {});
}

function scheduleVerificationStaleCheck(tabId) {
  if (!tabId) return;
  clearVerificationStaleTimer(tabId);
  const snapshot = challengeState.snapshot(tabId);
  const firstSeenAt = Number(snapshot?.firstSeenAt || Date.now());
  const delayMs = Math.max(1000, (firstSeenAt + STALE_VERIFICATION_MS) - Date.now() + 250);
  const timer = setTimeout(() => {
    verificationStaleTimers.delete(Number(tabId));
    recoverStaleVerification(tabId, "Verification wait expired").catch((error) => {
      log("Stale verification recovery failed", error.message);
    });
  }, delayMs);
  verificationStaleTimers.set(Number(tabId), timer);
}

async function readContentVerificationState(tabId) {
  try {
    return await chrome.tabs.sendMessage(tabId, { type: "GET_VERIFICATION_STATE" }, { frameId: TOP_FRAME_ID });
  } catch (_) {
    return null;
  }
}

async function clearChallengeForTab(tabId, reason = "Verification state cleared") {
  clearVerificationStaleTimer(tabId);
  clearVerificationFramePoll(tabId);
  clearVerificationReconcileTimer(tabId);
  challengeState.clear(tabId);

  const state = await Storage.ensureDefaults().catch(() => null);
  if (
    !state
    || state.status.activeTabId !== tabId
    || state.status.state !== STATES.WAITING_VERIFICATION
    || AutomationController.isOtpLocked(state.status)
  ) {
    return { ok: true, cleared: true };
  }

  const status = await updateStatus({
    state: STATES.RETRYING,
    retryCountdownEndsAt: null,
    verificationState: "Idle",
    lastAction: reason,
    lastError: "",
    challengeState: null
  }, { force: true });

  return { ok: true, cleared: true, status };
}

async function recoverStaleVerification(tabId, reason = "Verification wait expired") {
  const state = await Storage.ensureDefaults();
  if (
    state.status.activeTabId !== tabId
    || state.status.state !== STATES.WAITING_VERIFICATION
    || AutomationController.isOtpLocked(state.status)
  ) {
    return { ok: true, skipped: true };
  }

  const snapshot = challengeState.snapshot(tabId);
  if (!snapshot?.active) return { ok: true, skipped: true };

  const waitingMs = Date.now() - Number(snapshot.firstSeenAt || Date.now());
  if (waitingMs < STALE_VERIFICATION_MS) {
    scheduleVerificationStaleCheck(tabId);
    return { ok: true, skipped: true, waitingMs };
  }

  const tab = await chrome.tabs.get(tabId).catch(() => null);
  if (!tab?.id || !isIvacUrl(tab.url || tab.pendingUrl || "")) {
    return clearChallengeForTab(tabId, "Verification cleared after leaving IVAC page");
  }

  const frameState = await readVerificationFrameState(tabId);
  if (frameState?.completed === true) {
    return handleChallengeState({
      active: false,
      completed: true,
      selector: frameState.selector || "cf-frame-completed",
      source: "stale_frame_check",
      url: frameState.url || tab.url || "",
      tabId
    }, { tab });
  }

  const contentState = await readContentVerificationState(tabId);
  const verification = contentState?.verification || {};
  if (verification.present === true) {
    scheduleVerificationStaleCheck(tabId);
    return { ok: true, stillVisible: true };
  }
  if (verification.completed === true) {
    return handleChallengeState({
      active: false,
      completed: true,
      selector: verification.selector || "cf-turnstile-response",
      source: "stale_check",
      url: tab.url || ""
    }, { tab });
  }

  return handleChallengeState({
    active: false,
    completed: true,
    selector: "verification_stale_not_visible",
    source: "stale_check",
    url: tab.url || state.status.page || "",
    tabId
  }, { tab });
}

async function cancelAutomationTimers({ clearSchedule = false } = {}) {
  clearReloadTimer();
  await chrome.alarms.clear(COOLDOWN_ALARM).catch(() => {});
  if (clearSchedule) {
    await chrome.alarms.clear(Scheduler.alarmName()).catch(() => {});
    await Storage.saveSchedule({ enabled: false, date: "", time: "", nextRunAt: null }).catch(() => {});
    log("Scheduler cancelled");
  }
}

async function getActiveTab() {
  const tabs = await chrome.tabs.query({ active: true, currentWindow: true });
  return tabs[0] || null;
}

async function getActiveTabContext() {
  return SiteContext.fromTab(await getActiveTab());
}

async function findIvacTab() {
  const tabs = await chrome.tabs.query({ url: "https://appointment.ivacbd.com/*" });
  return tabs[0] || null;
}

async function prepareIvacTab(tab, { forceSignin = true } = {}) {
  if (!tab?.id) return tab;
  const currentUrl = tab.url || tab.pendingUrl || "";
  if (forceSignin && currentUrl !== IVAC_SIGNIN_URL) {
    return chrome.tabs.update(tab.id, { url: IVAC_SIGNIN_URL });
  }
  return tab;
}

function normalizeServerCooldown(cooldown = {}) {
  const parsed = CooldownManager?.normalizeParsedCooldown?.(cooldown);
  if (parsed) return parsed;
  const totalMs = Number(cooldown.totalMs || 0);
  if (totalMs <= 0) return null;
  const totalSeconds = Math.ceil(totalMs / 1000);
  return {
    minutes: Math.floor(totalSeconds / 60),
    seconds: totalSeconds % 60,
    totalMs,
    endsAt: Date.now() + totalMs,
    source: String(cooldown.source || "").slice(0, 240)
  };
}

function formatCooldownMs(totalMs) {
  if (CooldownManager?.formatDuration) return CooldownManager.formatDuration(totalMs);
  const totalSeconds = Math.max(0, Math.ceil((Number(totalMs) || 0) / 1000));
  const minutes = Math.floor(totalSeconds / 60);
  const seconds = totalSeconds % 60;
  return minutes > 0 ? `${minutes}m ${seconds}s` : `${seconds}s`;
}

async function armCooldownAlarm(endsAt) {
  const when = Number(endsAt || 0);
  await chrome.alarms.clear(COOLDOWN_ALARM).catch(() => {});
  if (when > Date.now()) {
    await chrome.alarms.create(COOLDOWN_ALARM, { when });
  }
}

async function pauseContentForServerCooldown(tabId) {
  if (!tabId) return;
  await sendToContent(tabId, {
    type: "PAUSE_FOR_SERVER_COOLDOWN",
    reason: "server_cooldown"
  }, { reinject: false }).catch(() => {});
}

async function activateServerCooldown(cooldown = {}, sender = {}) {
  const parsed = normalizeServerCooldown(cooldown);
  if (!parsed) return { ok: false, error: "No valid server cooldown found." };

  const current = await Storage.ensureDefaults();
  if (AutomationController.isOtpLocked(current.status)) {
    log("Server cooldown ignored because OTP is already required");
    return { ok: true, ignored: true, status: current.status };
  }

  const tabId = sender.tab?.id || cooldown.tabId || current.status.activeTabId || null;
  const tab = tabId ? await chrome.tabs.get(tabId).catch(() => null) : null;
  const remainingMs = Math.max(0, parsed.endsAt - Date.now());
  const label = formatCooldownMs(remainingMs || parsed.totalMs);

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

  await cancelAutomationTimers({ clearSchedule: true });
  if (CooldownManager?.saveCooldownState) {
    await CooldownManager.saveCooldownState({
      ...parsed,
      totalMs: remainingMs || parsed.totalMs,
      active: true
    }).catch(() => {});
  }

  const status = await updateStatus({
    state: STATES.SERVER_COOLDOWN,
    activeTabId: tabId,
    page: tab?.url || sender.tab?.url || current.status.page || "",
    retryCountdownEndsAt: parsed.endsAt,
    verificationState: "Paused",
    otpState: "Idle",
    schedulerState: "Not scheduled",
    lastAction: `Server cooldown detected. Retrying in ${label}`,
    lastError: ""
  }, { force: true });

  await pauseContentForServerCooldown(tabId);
  await armCooldownAlarm(parsed.endsAt);
  await notify("retry", "Server cooldown detected", `Retrying in ${label}.`).catch(() => {});

  return { ok: true, status, cooldown: parsed };
}

async function finishServerCooldownAndResume(source = "cooldown_end") {
  const current = await Storage.ensureDefaults();
  const status = current.status || {};
  const endsAt = Number(status.retryCountdownEndsAt || 0);

  if (![STATES.SERVER_COOLDOWN, STATES.WAITING_COOLDOWN].includes(status.state)) {
    if (CooldownManager?.clearCooldownState) await CooldownManager.clearCooldownState().catch(() => {});
    await chrome.alarms.clear(COOLDOWN_ALARM).catch(() => {});
    return { ok: true, ignored: true, status };
  }

  if (endsAt > Date.now() + 500) {
    await armCooldownAlarm(endsAt);
    const label = formatCooldownMs(endsAt - Date.now());
    await updateStatus({
      state: STATES.SERVER_COOLDOWN,
      retryCountdownEndsAt: endsAt,
      lastAction: `Server cooldown detected. Retrying in ${label}`,
      lastError: ""
    }, { force: true });
    return { ok: true, waiting: true, status };
  }

  if (CooldownManager?.clearCooldownState) await CooldownManager.clearCooldownState().catch(() => {});
  await chrome.alarms.clear(COOLDOWN_ALARM).catch(() => {});

  if (AutomationController.isOtpLocked(status) || AutomationController.isStopped(status)) {
    log(AutomationController.isOtpLocked(status) ? "Cooldown finished - resume blocked by OTP" : "Cooldown finished - automation is stopped");
    return { ok: true, blocked: true, status };
  }

  log("Cooldown finished");
  log("Resuming automation");
  await updateStatus({
    state: STATES.WAITING_RETRY,
    retryCountdownEndsAt: null,
    lastAction: "Cooldown finished. Resuming automation",
    lastError: ""
  }, { force: true });

  return startAutomation({ source });
}

async function restoreCooldownAlarm() {
  const current = await Storage.ensureDefaults();
  if (AutomationController.isOtpLocked(current.status) || AutomationController.isStopped(current.status)) {
    await chrome.alarms.clear(COOLDOWN_ALARM).catch(() => {});
    return;
  }

  const saved = CooldownManager?.loadCooldownState
    ? await CooldownManager.loadCooldownState().catch(() => null)
    : null;
  const endsAt = Number(saved?.endsAt || (current.status.state === STATES.SERVER_COOLDOWN ? current.status.retryCountdownEndsAt : 0) || 0);
  if (!endsAt) return;

  if (endsAt <= Date.now()) {
    await finishServerCooldownAndResume("cooldown_end").catch((error) => log(`Cooldown restore resume failed: ${error.message}`));
    return;
  }

  const label = formatCooldownMs(endsAt - Date.now());
  await updateStatus({
    state: STATES.SERVER_COOLDOWN,
    retryCountdownEndsAt: endsAt,
    lastAction: `Server cooldown detected. Retrying in ${label}`,
    lastError: ""
  }, { force: true });
  await armCooldownAlarm(endsAt);
}

async function resumeAutomationNow(source = "retry", reason = "Continuing automation") {
  if (automationResumeBusy) return;

  const state = await Storage.ensureDefaults();
  if (!AutomationController.canAutoResume(state.status)) {
    log(AutomationController.isOtpLocked(state.status) ? "Reload blocked due to OTP" : `Resume blocked due to ${state.status.state}`);
    return;
  }

  automationResumeBusy = true;
  try {
    const waitingVerification = state.status.state === STATES.WAITING_VERIFICATION;
    await updateStatus({
      state: waitingVerification ? STATES.WAITING_VERIFICATION : STATES.RELOADING,
      retryCountdownEndsAt: null,
      lastAction: waitingVerification ? "Waiting for verification completion" : reason,
      lastError: ""
    });
    await startAutomation({ source });
  } catch (error) {
    await updateStatus({
      state: STATES.RETRYING,
      retryCountdownEndsAt: null,
      lastAction: "Retrying after temporary automation error",
      lastError: error.message
    });
  } finally {
    automationResumeBusy = false;
  }
}

async function updateStatus(patch, options = {}) {
  const current = await Storage.ensureDefaults();
  const normalized = normalizeStatusPatch(current.status, patch, options);
  if (!Object.keys(normalized).length) return current.status;

  const status = await Storage.saveStatus({
    ...current.status,
    ...normalized,
    updatedAt: new Date().toISOString()
  });

  if ([STATES.WAITING_COOLDOWN, STATES.SERVER_COOLDOWN].includes(status.state) && status.retryCountdownEndsAt) {
    const when = Number(status.retryCountdownEndsAt);
    if (when > Date.now()) {
      await chrome.alarms.create(COOLDOWN_ALARM, { when });
    } else {
      await chrome.alarms.clear(COOLDOWN_ALARM).catch(() => {});
    }
  } else {
    await chrome.alarms.clear(COOLDOWN_ALARM).catch(() => {});
  }

  await broadcast({ type: "STATUS_UPDATE", status, fromBackground: true });
  return status;
}

async function broadcast(message) {
  try {
    await chrome.runtime.sendMessage(message);
  } catch (_) {
    // Popup may be closed; storage remains the source of truth.
  }
}

async function notify(kind, title, message) {
  return Notifications.send(Storage, kind, title, message);
}

async function waitForTabReady(tabId, timeoutMs = 12000) {
  const started = Date.now();
  while (Date.now() - started < timeoutMs) {
    const tab = await chrome.tabs.get(tabId).catch(() => null);
    if (tab?.status === "complete" && isIvacUrl(tab.url || "")) return tab;
    await new Promise((resolve) => setTimeout(resolve, 150));
  }
  throw new Error("Waiting for page load");
}

async function waitForFivaTabReady(tabId, timeoutMs = 12000) {
  const started = Date.now();
  while (Date.now() - started < timeoutMs) {
    const tab = await chrome.tabs.get(tabId).catch(() => null);
    if (tab?.status === "complete" && isFivaUrl(tab.url || "")) return tab;
    await new Promise((resolve) => setTimeout(resolve, 150));
  }
  throw new Error("Waiting for Indian Visa form load");
}

async function sendToContent(tabId, message, { reinject = true } = {}) {
  try {
    return await chrome.tabs.sendMessage(tabId, message, { frameId: TOP_FRAME_ID });
  } catch (error) {
    if (!reinject) throw new Error("IVAC page not ready");
    await waitForTabReady(tabId).catch(() => null);
    await ensureContent(tabId);
    try {
      return await chrome.tabs.sendMessage(tabId, message, { frameId: TOP_FRAME_ID });
    } catch (_) {
      throw new Error("IVAC page not ready");
    }
  }
}

async function ensureContent(tabId) {
  const tab = await chrome.tabs.get(tabId);
  if (!tab?.id || !isIvacUrl(tab.url || "")) {
    throw new Error("Open https://appointment.ivacbd.com/signin before starting VisaFlowX.");
  }

  try {
    const response = await chrome.tabs.sendMessage(tabId, { type: "PING" }, { frameId: TOP_FRAME_ID });
    if (response?.ready) return { injected: false, url: tab.url };
  } catch (_) {
    // Expected when content scripts are not attached yet.
  }

  await chrome.scripting.executeScript({
    target: { tabId },
    files: CONTENT_FILES
  });

  const response = await chrome.tabs.sendMessage(tabId, { type: "PING" }, { frameId: TOP_FRAME_ID });
  if (!response?.ready) throw new Error("Content script injection failed.");
  return { injected: true, url: tab.url };
}

function fivaCapabilitiesReady(response = {}) {
  const capabilities = response.capabilities || {};
  return response.ready === true
    && capabilities.runtimeConfig === true
    && capabilities.stateMachine === true
    && capabilities.normalizeOptionText === true
    && capabilities.strictDobValidator === true
    && capabilities.normalizeParsedData === true
    && capabilities.profileNormalizer === true
    && capabilities.detectFieldType === true
    && capabilities.selectSmartOption === true
    && capabilities.humanDropdownSelect === true
    && capabilities.humanSelectOption === true
    && capabilities.validators === true
    && capabilities.hardPaste === true
    && capabilities.dropdown === true;
}

function isFivaMessageChannelError(error = {}) {
  return /message channel closed|receiving end does not exist|could not establish connection|extension context invalidated/i
    .test(String(error.message || error || ""));
}

async function sendFivaContentMessage(tabId, message, timeoutMs = 90000) {
  let timeoutId = null;
  try {
    return await Promise.race([
      chrome.tabs.sendMessage(tabId, message),
      new Promise((_, reject) => {
        timeoutId = setTimeout(() => reject(new Error("FIVA content response timed out.")), timeoutMs);
      })
    ]);
  } catch (error) {
    if (isFivaMessageChannelError(error)) {
      throw new Error("FIVA content script stopped before replying. Reload the Indian Visa page and try again.");
    }
    throw error;
  } finally {
    if (timeoutId) clearTimeout(timeoutId);
  }
}

async function ensureFivaContent(tabId) {
  const tab = await chrome.tabs.get(tabId);
  if (!tab?.id || !isFivaUrl(tab.url || tab.pendingUrl || "")) {
    throw new Error("Open the Indian Visa registration form before using FIVA autofill.");
  }

  try {
    const response = await sendFivaContentMessage(tabId, { type: "FIVA_PING_V2" }, 3000);
    if (fivaCapabilitiesReady(response)) return { injected: false, url: tab.url };
  } catch (_) {
    // Expected before the FIVA form content script is attached.
  }

  await chrome.scripting.executeScript({
    target: { tabId },
    files: FIVA_CONTENT_FILES
  });

  const response = await sendFivaContentMessage(tabId, { type: "FIVA_PING_V2" }, 5000);
  if (!fivaCapabilitiesReady(response)) throw new Error("FIVA content script injection failed.");
  return { injected: true, url: tab.url };
}

async function getOrOpenFivaTab() {
  const active = await getActiveTab();
  if (active?.id && isFivaUrl(active.url || active.pendingUrl || "")) {
    return active;
  }

  const tabs = await chrome.tabs.query({ url: "https://indianvisa-bangladesh.nic.in/visa/*" });
  if (tabs[0]?.id) return tabs[0];

  return chrome.tabs.create({ url: FIVA_REGISTRATION_URL, active: false });
}

async function getOrOpenIvacTab(options = {}) {
  const { preferStatusTab = true, forceSignin = true } = options;
  if (preferStatusTab) {
    const state = await Storage.ensureDefaults();
    const storedTabId = state.status.activeTabId;
    if (storedTabId) {
      const stored = await chrome.tabs.get(storedTabId).catch(() => null);
      if (stored?.id && isIvacUrl(stored.url || stored.pendingUrl || "")) {
        return prepareIvacTab(stored, { forceSignin });
      }
    }
  }

  const active = await getActiveTab();
  if (active?.id && isIvacUrl(active.url || active.pendingUrl || "")) {
    return prepareIvacTab(active, { forceSignin });
  }

  const existing = await findIvacTab();
  if (existing?.id) {
    return prepareIvacTab(existing, { forceSignin });
  }

  return chrome.tabs.create({ url: IVAC_SIGNIN_URL, active: false });
}

async function executeGuardedReload(tabId, targetUrl, mode, reason) {
  const state = await Storage.ensureDefaults();
  if (challengeState.isActive(tabId)) {
    log("Reload blocked due to active verification");
    return { ok: true, blocked: true, reason: "verification_active" };
  }
  if (state.status.activeTabId && state.status.activeTabId !== tabId) {
    log("Reload blocked due to tab mismatch");
    return { ok: true, blocked: true, reason: "tab_mismatch" };
  }
  if (!AutomationController.canReload(state.status)) {
    log(AutomationController.isOtpLocked(state.status) ? "Reload blocked due to OTP" : `Reload blocked due to ${state.status.state}`);
    return {
      ok: true,
      blocked: true,
      terminal: AutomationController.isOtpLocked(state.status) || AutomationController.isStopped(state.status),
      reason: state.status.state
    };
  }

  await updateStatus({
    state: STATES.RELOADING,
    activeTabId: tabId,
    retryCountdownEndsAt: null,
    lastReloadAt: Date.now(),
    lastAction: reason || "Reloading IVAC page",
    lastError: ""
  });

  if (targetUrl) {
    await chrome.tabs.update(tabId, { url: targetUrl });
  } else {
    await chrome.tabs.reload(tabId, { bypassCache: mode === "hard" });
  }

  return { ok: true, reloading: true };
}

async function scheduleGuardedReload({ tabId, targetUrl = "", mode = "soft", reason = "Reloading IVAC page" } = {}) {
  if (!tabId) return { ok: true, blocked: true, reason: "missing_tab" };

  const state = await Storage.ensureDefaults();
  if (challengeState.isActive(tabId)) {
    log("Reload blocked due to active verification");
    return { ok: true, blocked: true, reason: "verification_active" };
  }
  if (!AutomationController.canReload(state.status)) {
    log(AutomationController.isOtpLocked(state.status) ? "Reload blocked due to OTP" : `Reload blocked due to ${state.status.state}`);
    return {
      ok: true,
      blocked: true,
      terminal: AutomationController.isOtpLocked(state.status) || AutomationController.isStopped(state.status),
      reason: state.status.state
    };
  }

  const lastReloadAt = Number(state.status.lastReloadAt || 0);
  const delayMs = Math.max(0, (lastReloadAt + RELOAD_GUARD_MS) - Date.now());
  clearReloadTimer();

  await updateStatus({
    state: STATES.RELOADING,
    activeTabId: tabId,
    retryCountdownEndsAt: null,
    lastAction: delayMs > 0 ? "Reload scheduled" : (reason || "Reloading IVAC page"),
    lastError: ""
  });
  log("Reload scheduled", delayMs > 0 ? `${delayMs}ms` : "now");

  if (delayMs > 0) {
    reloadTimerTabId = tabId;
    reloadTimer = setTimeout(() => {
      reloadTimer = null;
      reloadTimerTabId = null;
      executeGuardedReload(tabId, targetUrl, mode, reason).catch((error) => {
        updateStatus({
          state: STATES.RETRYING,
          lastAction: "Reload failed. Retrying automatically.",
          lastError: error.message
        }).catch(() => {});
      });
    }, delayMs);
    return { ok: true, scheduled: true, delayMs };
  }

  return executeGuardedReload(tabId, targetUrl, mode, reason);
}

async function handleReloadRequest(message, sender) {
  const tabId = sender.tab?.id || message.tabId;
  return scheduleGuardedReload({
    tabId,
    targetUrl: message.url || "",
    mode: message.mode || "soft",
    reason: message.reason || "Retry requested a controlled reload"
  });
}

async function handleChallengeState(message, sender) {
  const tabId = sender.tab?.id || message.tabId;
  if (!tabId) return { ok: false, error: "Missing tab id" };
  logVerification("background message received", {
    type: "CHALLENGE_STATE",
    tabId,
    active: message.active === true,
    completed: message.completed === true,
    source: message.source || "content",
    selector: message.selector || ""
  });

  const state = await Storage.ensureDefaults();
  const tab = await chrome.tabs.get(tabId).catch(() => sender.tab || null);
  const pageUrl = tab?.url || tab?.pendingUrl || state.status.page || message.url || "";
  const tracked = Boolean(
    state.status.activeTabId === tabId
    && AutomationController.isActive(state.status)
    && !AutomationController.isOtpLocked(state.status)
    && isIvacUrl(pageUrl)
  );

  if (!tracked) {
    await clearChallengeForTab(tabId, "Verification cleared outside tracked IVAC tab");
    return { ok: true, ignored: true, reason: "untracked_tab" };
  }

  const details = {
    source: message.source || "content",
    selector: message.selector || "",
    url: message.url || pageUrl
  };

  if (message.active === true && message.completed !== true) {
    const transition = challengeState.markDetected(tabId, details);
    scheduleVerificationStaleCheck(tabId);
    startVerificationFramePoll(tabId);
    scheduleVerificationReconcile(tabId);
    if (transition.firstSeen) {
      logVerification("challenge-state updated", {
        action: "markDetected",
        tabId,
        active: transition.snapshot?.active === true,
        retryCount: transition.retryCount
      });
      logVerification("detected", {
        tabId,
        source: details.source,
        selector: details.selector
      });
    }
    const status = await updateStatus({
      state: STATES.WAITING_VERIFICATION,
      activeTabId: tabId,
      page: pageUrl,
      retryCountdownEndsAt: null,
      verificationState: "Waiting for verification completion",
      lastAction: transition.firstSeen ? "Verification detected" : "Waiting for verification completion",
      lastError: "",
      challengeState: transition.snapshot
    }, { force: state.status.state === STATES.WAITING_VERIFICATION });

    if (transition.firstSeen && !transition.limited) {
      await notify("verification", "Verification detected", "Waiting for verification completion");
    }

    return { ok: true, active: true, transition, status };
  }

  const resolvedByCompletion = message.completed === true;
  const resolvedByDisappearance = message.active === false && (
    state.status.state === STATES.WAITING_VERIFICATION
    || challengeState.isActive(tabId)
  );

  if (!resolvedByCompletion && !resolvedByDisappearance) {
    return { ok: true, active: false, completed: false };
  }

  clearVerificationStaleTimer(tabId);
  clearVerificationFramePoll(tabId);
  clearVerificationReconcileTimer(tabId);
  logVerification("markResolved called", {
    tabId,
    completed: resolvedByCompletion,
    disappeared: resolvedByDisappearance,
    selector: details.selector || ""
  });
  const transition = challengeState.markResolved(tabId, {
    ...details,
    selector: details.selector || (resolvedByDisappearance ? "verification_no_longer_visible" : "")
  });
  logVerification("challenge-state updated", {
    action: "markResolved",
    tabId,
    active: transition.snapshot?.active === true,
    wasActive: transition.wasActive
  });
  if (!transition.wasActive && state.status.state !== STATES.WAITING_VERIFICATION) {
    return { ok: true, active: false, transition };
  }

  const status = await updateStatus({
    state: STATES.RUNNING,
    activeTabId: tabId,
    page: pageUrl,
    retryCountdownEndsAt: null,
    verificationState: "Verification completed",
    lastAction: "Verification completed",
    lastError: "",
    challengeState: transition.snapshot
  }, { force: state.status.state === STATES.WAITING_VERIFICATION });
  logVerification("resolved", {
    tabId,
    source: details.source,
    selector: details.selector || "",
    byCompletion: resolvedByCompletion,
    byDisappearance: resolvedByDisappearance,
    wasActive: transition.wasActive
  });
  logVerification("verificationActive=false", {
    tabId,
    active: transition.snapshot?.active === true
  });

  if (transition.wasActive || state.status.state === STATES.WAITING_VERIFICATION) {
    await notify("verification-resumed", "Automation resumed", "Automation resumed");
    logVerification("runtime message sent", {
      type: "VERIFICATION_COMPLETED",
      tabId
    });
    await sendToContent(tabId, { type: "VERIFICATION_COMPLETED" }, { reinject: false }).catch(() => {});
  }

  return { ok: true, active: false, transition, status };
}

function normalizeFivaAnalysis(profile) {
  const analysis = profile?.profile ? profile : { profile: profile || {} };
  const strictNormalizer = self.FIVA?.NormalizeParsedData;
  const normalizer = strictNormalizer?.toQueueProfile && strictNormalizer?.normalizeParsedData
    ? strictNormalizer
    : self.FIVA?.ProfileNormalizer;
  if (!normalizer?.toQueueProfile) return analysis;
  const hasRawData = Object.entries(analysis.profile || {}).some(([key, value]) => (
    !String(key).startsWith("__") && String(value || "").replace(/\s+/g, " ").trim()
  ));
  const source = {
    ...(analysis.source || {}),
    siteUrl: hasRawData ? FIVA_REGISTRATION_URL : "",
    allowDefaultCountry: hasRawData,
    defaultCountry: "BANGLADESH",
    currentUploadOnly: true
  };
  const currentProfile = self.FIVA?.StrictDobValidator?.normalizeProfileDates
    ? self.FIVA.StrictDobValidator.normalizeProfileDates(pruneLowConfidenceFivaFields(analysis.profile || {}, analysis.fields || []), analysis.fields || [])
    : pruneLowConfidenceFivaFields(analysis.profile || {}, analysis.fields || []);
  const smartProfile = self.FIVA?.SmartVisaProfileEngine?.normalizeProfile
    ? self.FIVA.SmartVisaProfileEngine.normalizeProfile(currentProfile, source).profile
    : currentProfile;
  const normalizedProfile = normalizer.normalizeParsedData
    ? normalizer.normalizeParsedData(smartProfile, source)
    : normalizer.normalizeProfile(smartProfile, source);
  const review = Array.from(new Set([
    ...(Array.isArray(analysis.review) ? analysis.review : []),
    ...lowConfidenceFivaFields(analysis.fields || [])
  ]));
  return {
    ...analysis,
    profile: normalizer.toQueueProfile(smartProfile, source),
    normalizedProfile,
    source,
    review
  };
}

function confidenceKeyAliases(key = "") {
  if (key === "dob") return ["dateOfBirth", "dob"];
  if (key === "arrivalDate") return ["expectedArrivalDate", "arrivalDate"];
  if (key === "givenName") return ["givenNames", "givenName"];
  return [key];
}

function pruneLowConfidenceFivaFields(profile = {}, fields = []) {
  if (!Array.isArray(fields) || !fields.length) return profile;
  const records = new Map(fields.filter((record) => record?.key).map((record) => [record.key, record]));
  const next = { ...profile };
  Object.keys(next).forEach((key) => {
    if (String(key).startsWith("__")) return;
    if (!FIVA_SENSITIVE_FIELDS.has(key)) return;
    const record = confidenceKeyAliases(key).map((alias) => records.get(alias)).find(Boolean);
    if (!record) return;
    const confidence = Number(record.confidence || 0);
    if (record.requiresReview === true || (record.verified !== true && confidence > 0 && confidence < 90)) {
      delete next[key];
    }
  });
  return next;
}

function lowConfidenceFivaFields(fields = []) {
  return (Array.isArray(fields) ? fields : [])
    .filter((record) => record?.key && FIVA_SENSITIVE_FIELDS.has(record.key))
    .filter((record) => {
      const confidence = Number(record.confidence || 0);
      return record.requiresReview === true || (record.verified !== true && confidence > 0 && confidence < 90);
    })
    .map((record) => record.key);
}

function fivaStore() {
  return self.FIVA.FivaRuntimeStore;
}

function defaultFivaRuntimeStore() {
  return fivaStore().reset();
}

async function writeFivaRuntimeStore(store, { broadcastUpdate = true } = {}) {
  const value = fivaStore().nextStore(store);
  await chrome.storage.local.set({ [FIVA_RUNTIME_STORE_KEY]: value });
  if (broadcastUpdate) await broadcastFivaRuntimeStore(value);
  return value;
}

async function broadcastFivaRuntimeStore(store) {
  const runtime = store.runtimeState;
  const activity = store.lastActivity || [];
  const snapshot = { store, runtime, activity };
  await broadcast({ type: "FIVA_RUNTIME_UPDATED", ...snapshot });
  await broadcast({ type: "FIVA_RUNTIME_UPDATE", ...snapshot });
}

async function getFivaRuntimeStore() {
  const defaults = {
    [FIVA_RUNTIME_STORE_KEY]: null,
    [FIVA_LEGACY_PROFILE_KEY]: null,
    [FIVA_LEGACY_ACTIVITY_KEY]: [],
    [FIVA_LEGACY_RUNTIME_KEY]: null
  };
  const data = await chrome.storage.local.get(defaults);
  if (data[FIVA_RUNTIME_STORE_KEY]) {
    return fivaStore().nextStore(data[FIVA_RUNTIME_STORE_KEY]);
  }

  const migrated = fivaStore().nextStore(defaultFivaRuntimeStore(), {
    parsedProfile: data[FIVA_LEGACY_PROFILE_KEY] || null,
    runtimeState: data[FIVA_LEGACY_RUNTIME_KEY] || null,
    lastActivity: data[FIVA_LEGACY_ACTIVITY_KEY] || []
  });
  if (fivaStore().hasProfileData(migrated.parsedProfile) || migrated.lastActivity.length) {
    await chrome.storage.local.set({ [FIVA_RUNTIME_STORE_KEY]: migrated });
    await chrome.storage.local.remove([
      FIVA_LEGACY_PROFILE_KEY,
      FIVA_LEGACY_ACTIVITY_KEY,
      FIVA_LEGACY_RUNTIME_KEY
    ]);
  }
  return migrated;
}

function removeStaleFivaUploadWarnings(activity = []) {
  return (Array.isArray(activity) ? activity : []).filter((item) => (
    !/upload.*document.*first|document parsing failed|could not analyze document|no valid application data found/i
      .test(String(item?.message || ""))
  )).slice(0, fivaStore().MAX_ACTIVITY || 8);
}

async function saveFivaProfile(profile) {
  const analysis = normalizeFivaAnalysis(profile);
  const value = {
    profile: analysis.profile || {},
    normalizedProfile: analysis.normalizedProfile || {},
    fields: analysis.fields || [],
    missing: analysis.missing || [],
    review: analysis.review || [],
    confidence: Number(analysis.confidence || 0),
    status: analysis.status || "",
    source: analysis.source || {},
    updatedAt: new Date().toISOString()
  };
  const current = await getFivaRuntimeStore();
  const next = fivaStore().nextStore(fivaStore().withProfile(current, value), {
    lastActivity: removeStaleFivaUploadWarnings(current.lastActivity)
  });
  await writeFivaRuntimeStore(next);
  return value;
}

async function getFivaProfile() {
  return (await getFivaRuntimeStore()).parsedProfile || null;
}

async function clearFivaProfile() {
  await chrome.storage.local.remove([
    FIVA_RUNTIME_STORE_KEY,
    FIVA_SESSION_KEY,
    FIVA_LEGACY_PROFILE_KEY,
    FIVA_LEGACY_ACTIVITY_KEY,
    FIVA_LEGACY_RUNTIME_KEY
  ]);
  await broadcastFivaRuntimeStore(defaultFivaRuntimeStore());
  return { ok: true };
}

async function resetFivaSession() {
  await chrome.storage.local.remove(FIVA_SESSION_KEY);
}

function defaultFivaRuntime() {
  return defaultFivaRuntimeStore().runtimeState;
}

async function getFivaRuntime() {
  return (await getFivaRuntimeStore()).runtimeState;
}

async function saveFivaRuntime(patch = {}, { broadcastUpdate = true } = {}) {
  const current = await getFivaRuntimeStore();
  const next = fivaStore().withRuntime(current, patch);
  const store = await writeFivaRuntimeStore(next, { broadcastUpdate });
  return store.runtimeState;
}

function cleanFivaRuntimePatch(patch = {}) {
  return Object.fromEntries(Object.entries(patch).filter(([, value]) => value !== undefined && value !== null));
}

async function appendFivaActivity(message, level = "info", runtimePatch = {}) {
  const text = String(message || "").replace(/\s+/g, " ").trim();
  if (!text) return [];
  const cleanPatch = cleanFivaRuntimePatch(runtimePatch);
  const current = await getFivaRuntimeStore();
  const next = fivaStore().addActivity(current, text, level, cleanPatch);
  const store = await writeFivaRuntimeStore(next, { broadcastUpdate: false });
  await broadcastFivaRuntimeStore(store);
  chrome.runtime.sendMessage({ type: "FIVA_ACTIVITY_UPDATE", store, runtime: store.runtimeState, activity: store.lastActivity }).catch(() => {});
  return store.lastActivity;
}

async function getFivaActivity() {
  return (await getFivaRuntimeStore()).lastActivity || [];
}

async function stopFivaAutofill(reason = "manual") {
  await appendFivaActivity("FIVA stopped", "warn", { state: FIVA_STATES.STOPPED, status: "STOPPED" });
  await chrome.storage.local.remove(FIVA_SESSION_KEY);
  const tabs = await chrome.tabs.query({ url: "https://indianvisa-bangladesh.nic.in/*" }).catch(() => []);
  await Promise.all((tabs || []).map((tab) => (
    tab.id ? chrome.tabs.sendMessage(tab.id, { type: "FIVA_STOP_FORM", reason }).catch(() => null) : null
  )));
  return { ok: true, stopped: true };
}

async function fillFivaForm(profile, { confirmedUnsupported = false, reviewFields = [] } = {}) {
  const storedProfile = profile ? null : await getFivaProfile();
  const sourceProfile = profile || storedProfile;
  const analysis = normalizeFivaAnalysis(sourceProfile?.profile ? sourceProfile : { profile: sourceProfile || {} });
  const payloadProfile = analysis.profile || {};
  if (!payloadProfile || !Object.keys(payloadProfile).length) {
    await appendFivaActivity("No valid application data found.", "error", { state: FIVA_STATES.FAILED, status: "FAILED" });
    throw new Error("FIVA document is not ready.");
  }

  try {
    const activeTabContext = await getActiveTabContext().catch(() => SiteContext.classifyUrl(""));
    if (needsSupportedTabConfirmation(activeTabContext, SiteContext.TYPES.FIVA) && !confirmedUnsupported) {
      log("Passive mode enabled");
      return unsupportedConfirmationResponse(SiteContext.TYPES.FIVA, activeTabContext);
    }

    const currentStore = await getFivaRuntimeStore();
    await writeFivaRuntimeStore(fivaStore().nextStore(currentStore, {
      parsedProfile: analysis,
      autofillStatus: "FILLING FORM",
      runtimeState: { state: FIVA_STATES.RUNNING, status: "FILLING FORM", failedFields: [] },
      queue: ["country", "mission", "nationality", "dateOfBirth", "email", "reEnterEmail", "expectedArrivalDate", "visaType", "purpose"],
      startedAt: Date.now()
    }));
    await appendFivaActivity("Building autofill queue", "info", { state: FIVA_STATES.RUNNING, status: "FILLING FORM" });
    if (analysis.review?.length) {
      await appendFivaActivity(`Field requires manual confirmation: ${analysis.review.join(", ")}`, "warn", {
        state: FIVA_STATES.RUNNING,
        status: "FILLING FORM",
        failedFields: analysis.review
      });
    }
    const tab = await getOrOpenFivaTab();
    if (tab.status === "loading") await waitForFivaTabReady(tab.id, 10000).catch(() => tab);
    await ensureFivaContent(tab.id);
    await appendFivaActivity("Content script connected", "info", { state: FIVA_STATES.RUNNING, status: "FILLING FORM" });
    await resetFivaSession();
    await appendFivaActivity("Autofill started", "info", { state: FIVA_STATES.RUNNING, status: "FILLING FORM" });

    console.log("[FIVA]", "parser output received", sourceProfile?.profile || sourceProfile || {});
    console.log("[FIVA]", "profile normalized", analysis.normalizedProfile || payloadProfile);
    const response = await sendFivaContentMessage(tab.id, {
      type: "FIVA_FILL_FORM_V2",
      profile: payloadProfile,
      fields: analysis.fields || [],
      source: analysis.source || {},
      reviewFields
    });

    if (!response?.ok) throw new Error(response?.error || "FIVA form autofill failed.");
    if (response.blocked) console.log("[FIVA]", "Autofill waiting for required fields");
    else if (response.stopped) console.log("[FIVA]", "Autofill stopped");
    else if (response.continued) console.log("[FIVA]", "Continue clicked");
    else if (response.manual) console.log("[FIVA]", "Autofill waiting for manual input");
    else if (response.complete) console.log("[FIVA]", "Autofill completed successfully");
    else console.log("[FIVA]", response.message || "Autofill step finished");

    const failedFields = response.failedFields || response.missing || [];
    if (response.stopped) {
      await appendFivaActivity("FIVA stopped", "warn", { state: FIVA_STATES.STOPPED, status: "STOPPED", failedFields });
    } else if (response.blocked) {
      const state = response.filled?.length ? FIVA_STATES.PARTIAL_SUCCESS : FIVA_STATES.FAILED;
      await appendFivaActivity(response.message || "Autofill partially complete", state === FIVA_STATES.FAILED ? "error" : "warn", {
        state,
        status: state === FIVA_STATES.FAILED ? "FAILED" : "PARTIAL SUCCESS",
        failedFields
      });
    } else if (response.continued || response.complete) {
      await appendFivaActivity(response.message || "Autofill complete", "info", {
        state: FIVA_STATES.SUCCESS,
        status: "AUTOFILL COMPLETE",
        failedFields: []
      });
    }
    return { ok: true, tabId: tab.id, ...response };
  } catch (error) {
    await appendFivaActivity(error.message || "Autofill failed", "error", { state: FIVA_STATES.FAILED, status: "FAILED" });
    throw error;
  }
}

function needsSupportedTabConfirmation(activeTabContext, targetType) {
  return SiteContext.needsUnsupportedConfirmation(activeTabContext, targetType);
}

function unsupportedConfirmationResponse(target, activeTabContext) {
  const targetLabel = target === SiteContext.TYPES.FIVA ? "Indian Visa registration" : "IVAC signin";
  return {
    ok: true,
    needsConfirmation: true,
    target,
    activeTabContext,
    message: `Open ${targetLabel} from this tab?`
  };
}

async function startAutomation({ source = "manual", confirmedUnsupported = false } = {}) {
  const activeTabContext = await getActiveTabContext().catch(() => SiteContext.classifyUrl(""));
  if (AutomationController.isExplicitStart(source) && needsSupportedTabConfirmation(activeTabContext, SiteContext.TYPES.IVAC)) {
    if (source === "hotkey") {
      await updateStatus({
        lastAction: "Passive mode enabled on unsupported tab. Open IVAC or use the popup to confirm start.",
        lastError: ""
      }).catch(() => {});
      log("Passive mode enabled");
      await notify("passive", "VisaFlowX passive mode", "Open IVAC or use the popup to confirm starting automation.").catch(() => {});
      return { ok: true, blocked: true, passive: true, activeTabContext };
    }
    if (!confirmedUnsupported) {
      log("Passive mode enabled");
      return unsupportedConfirmationResponse(SiteContext.TYPES.IVAC, activeTabContext);
    }
  }

  const state = await Storage.ensureDefaults();
  if (!AutomationController.canStart(state.status, source)) {
    log(source === "schedule" ? "Scheduler blocked due to OTP or stopped state" : `Automation start blocked due to ${state.status.state}`);
    return { ok: true, blocked: true, status: state.status };
  }
  if (automationStartBusy) {
    log("Automation start ignored because controller is busy");
    return { ok: true, busy: true };
  }

  automationStartBusy = true;
  clearReloadTimer();
  const keepCurrentPage = [STATES.WAITING_VERIFICATION, STATES.RUNNING].includes(state.status.state) && !["manual", "popup", "schedule", "hotkey"].includes(source);
  let tab = null;

  try {
    tab = await getOrOpenIvacTab({ preferStatusTab: true, forceSignin: !keepCurrentPage });
    if (tab.status === "loading") {
      tab = await waitForTabReady(tab.id).catch(() => tab);
    }
    await updateStatus({
      state: STATES.RUNNING,
      activeTabId: tab.id,
      page: tab.url || IVAC_SIGNIN_URL,
      retryCountdownEndsAt: null,
      verificationState: "Checking",
      otpState: "Idle",
      lastAction: source === "schedule" ? "Scheduled run started" : "Automation starting",
      lastError: ""
    }, { force: AutomationController.isExplicitStart(source) });

    const injection = await ensureContent(tab.id);
    await sendToContent(tab.id, {
      type: "START_AUTOMATION",
      credentials: state.credentials,
      retry: state.retry,
      settings: state.settings,
      source
    });
    const latestAfterSend = await Storage.ensureDefaults();
    if (AutomationController.isStopped(latestAfterSend.status) || AutomationController.isOtpLocked(latestAfterSend.status)) {
      if (tab?.id) {
        await sendToContent(tab.id, { type: "STOP_AUTOMATION", reason: "state_locked" }, { reinject: false }).catch(() => {});
      }
      log(`Automation start blocked due to ${latestAfterSend.status.state}`);
      return { ok: true, blocked: true, status: latestAfterSend.status };
    }

    await updateStatus({
      state: STATES.RUNNING,
      activeTabId: tab.id,
      page: injection.url,
      otpState: "Idle",
      schedulerState: source === "schedule" ? "Started" : state.schedule.enabled ? "Scheduled" : "Not scheduled",
      lastAction: source === "schedule" ? "Scheduled automation started" : "Automation started",
      lastError: ""
    }, { force: AutomationController.isExplicitStart(source) });
    log("Automation started");
    if (source === "schedule") {
      await notify("schedule", "VisaFlowX scheduled run", "Scheduled IVAC workflow has started.");
    }
    return { ok: true, tabId: tab.id, injection };
  } catch (error) {
    const latest = await Storage.ensureDefaults().catch(() => state);
    if (
      AutomationController.isStopped(latest.status)
      || AutomationController.isOtpLocked(latest.status)
      || (!AutomationController.canAutoResume(latest.status) && !AutomationController.isExplicitStart(source))
    ) {
      log(`Start recovery blocked due to ${latest.status.state}`);
      return { ok: true, blocked: true, error: error.message };
    }
    await updateStatus({
      state: STATES.RETRYING,
      activeTabId: tab?.id || state.status.activeTabId || null,
      page: tab?.url || state.status.page || IVAC_SIGNIN_URL,
      retryCountdownEndsAt: null,
      lastAction: "IVAC page not ready. Retrying automatically.",
      lastError: error.message
    });
    return { ok: true, retrying: true, error: error.message };
  } finally {
    automationStartBusy = false;
  }
}

async function stopAutomation({ reason = "manual" } = {}) {
  const state = await Storage.ensureDefaults();
  const tabId = state.status.activeTabId;
  if (tabId) {
    challengeState.clear(tabId);
    clearVerificationStaleTimer(tabId);
    clearVerificationFramePoll(tabId);
    clearVerificationReconcileTimer(tabId);
  }
  await cancelAutomationTimers({ clearSchedule: true });
  const status = await updateStatus({
    state: STATES.STOPPED,
    activeTabId: null,
    retryCountdownEndsAt: null,
    verificationState: "Idle",
    otpState: "Idle",
    schedulerState: "Not scheduled",
    lastAction: `Automation stopped (${reason})`,
    lastError: ""
  }, { force: true });
  if (tabId) {
    await sendToContent(tabId, { type: "STOP_AUTOMATION", reason }, { reinject: false }).catch(() => {});
  }
  log("Automation stopped successfully");
  await notify("stopped", "VisaFlowX stopped", `Automation stopped (${reason}).`);
  return { ok: true, status };
}

async function saveSchedule(schedule) {
  const when = Scheduler.parseLocalRun(schedule.date, schedule.time);
  const current = await Storage.ensureDefaults();
  const value = await Storage.saveSchedule({
    enabled: true,
    date: schedule.date,
    time: schedule.time,
    nextRunAt: when
  });
  await chrome.alarms.clear(Scheduler.alarmName()).catch(() => {});
  await chrome.alarms.create(Scheduler.alarmName(), { when });
  log("Scheduler armed");
  await updateStatus({
    state: isAutomationActive(current.status) ? current.status.state : STATES.SCHEDULED,
    verificationState: isAutomationActive(current.status) ? current.status.verificationState : "Idle",
    otpState: isAutomationActive(current.status) ? current.status.otpState : "Idle",
    schedulerState: Scheduler.preview(value),
    lastAction: "Scheduled automation saved",
    lastError: ""
  }, { force: AutomationController.isOtpLocked(current.status) });
  return { ok: true, schedule: value };
}

async function clearSchedule() {
  await chrome.alarms.clear(Scheduler.alarmName());
  const current = await Storage.ensureDefaults();
  const schedule = await Storage.saveSchedule({ enabled: false, date: "", time: "", nextRunAt: null });
  log("Scheduler cancelled");
  await updateStatus({
    state: AutomationController.isOtpLocked(current.status)
      ? STATES.OTP_DETECTED
      : isAutomationActive(current.status)
        ? current.status.state
        : STATES.IDLE,
    schedulerState: "Not scheduled",
    lastAction: "Schedule cleared"
  }, { force: AutomationController.isOtpLocked(current.status) });
  return { ok: true, schedule };
}

async function handleStatusUpdate(message, sender) {
  if (message.fromBackground) return { ok: true, ignored: true };
  const incoming = message.status || {};
  if (incoming.state === STATES.OTP_DETECTED) {
    return handleOtpDetected(message, sender);
  }
  if (
    sender.tab?.id
    && challengeState.isActive(sender.tab.id)
    && incoming.state
    && ![STATES.WAITING_VERIFICATION, STATES.OTP_DETECTED].includes(incoming.state)
  ) {
    const current = await Storage.ensureDefaults();
    return { ok: true, blocked: true, reason: "verification_active", status: current.status };
  }

  const status = await updateStatus({
    ...incoming,
    activeTabId: sender.tab?.id || incoming.activeTabId || null,
    page: incoming.page || sender.tab?.url || ""
  }, { origin: "content" });

  if (message.notify === "verification") {
    await notify("verification", "Verification required", "Complete the IVAC verification manually. VisaFlowX will continue afterward.");
  } else if (message.notify === "retry") {
    await notify("retry", "VisaFlowX retrying", incoming.lastAction || "VisaFlowX is retrying automatically.");
  } else if (message.notify === "otp") {
    await notify("otp", "OTP detected", "Automation stopped. Enter the OTP manually.");
  }

  return { ok: true, status };
}

async function handleOtpDetected(message, sender) {
  const previous = await Storage.ensureDefaults();
  const senderTabId = sender.tab?.id || null;
  const senderIsTrackedTab = Boolean(senderTabId && previous.status.activeTabId === senderTabId);
  if (!AutomationController.isActive(previous.status) && !AutomationController.isOtpLocked(previous.status) && !senderIsTrackedTab) {
    log(`OTP signal ignored because automation is ${previous.status.state}`);
    return { ok: true, ignored: true, status: previous.status };
  }

  await cancelAutomationTimers({ clearSchedule: true });
  if (sender.tab?.id) {
    challengeState.clear(sender.tab.id);
    clearVerificationStaleTimer(sender.tab.id);
  }

  const status = await updateStatus({
    state: STATES.OTP_REQUIRED,
    activeTabId: sender.tab?.id || null,
    page: sender.tab?.url || message.url || "",
    retryCountdownEndsAt: null,
    verificationState: "Completed",
    otpState: "Detected",
    schedulerState: "Not scheduled",
    lastAction: "OTP page detected. Automation paused. Waiting for OTP.",
    lastError: ""
  }, { force: true });

  console.log("[VisaFlowX] OTP page detected");
  console.log("[VisaFlowX] Automation paused");
  console.log("[VisaFlowX] Waiting for OTP");
  console.log("[VisaFlowX] When user click OTP Solver in VisaFlowX extension UI it must be start work");

  log("OTP detected. Automation engine locked.");
  if (!AutomationController.isOtpLocked(previous.status)) {
    await notify("otp", "OTP detected", "VisaFlowX stopped automation for manual OTP entry.");
  }
  return { ok: true, status };
}

async function getVerificationAutomationPolicy(sender = {}) {
  const tabId = sender.tab?.id || null;
  if (!tabId) {
    return { allowed: false, mode: "blocked", reason: "missing_tab" };
  }

  const state = await Storage.ensureDefaults();
  const tab = await chrome.tabs.get(tabId).catch(() => sender.tab || null);
  const pageUrl = tab?.url || tab?.pendingUrl || state.status.page || "";
  const frameUrl = sender.url || sender.origin || pageUrl;
  const frameIsSupported = SiteContext.isIvacUrl(frameUrl) || SiteContext.isCloudflareUrl(frameUrl);
  const tracked = Boolean(
    state.status.activeTabId === tabId
    && AutomationController.isActive(state.status)
    && !AutomationController.isOtpLocked(state.status)
    && isIvacUrl(pageUrl)
    && frameIsSupported
  );

  if (!tracked) {
    return {
      allowed: false,
      mode: "blocked",
      reason: AutomationController.isOtpLocked(state.status) ? "otp_locked" : "untracked_or_unsupported_tab"
    };
  }

  return {
    allowed: true,
    mode: "safe_handoff",
    reason: "tracked_ivac_verification",
    actions: ["focus", "highlight", "observe", "resume_after_completion"]
  };
}

async function handleMessage(message, sender = {}) {
  switch (message?.type) {
    case "GET_STATE": {
      const activeTab = await getActiveTab().catch(() => null);
      return {
        ok: true,
        ...(await Storage.ensureDefaults()),
        activeTab,
        activeTabContext: SiteContext.fromTab(activeTab)
      };
    }
    case "SAVE_CREDENTIALS": {
      const credentials = await Storage.saveCredentials(message.credentials || {});
      await updateStatus({ lastAction: "Credentials saved", lastError: "" });
      return { ok: true, credentials: { contactNumber: credentials.contactNumber, hasPassword: Boolean(credentials.password) } };
    }
    case "CLEAR_CREDENTIALS": {
      await Storage.clearCredentials();
      await updateStatus({ lastAction: "Credentials cleared", lastError: "" });
      return { ok: true };
    }
    case "SAVE_RETRY":
      return { ok: true, retry: await Storage.saveRetry(message.retry || {}) };
    case "SAVE_SETTINGS":
      return { ok: true, settings: await Storage.saveSettings(message.settings || {}) };
    case "SAVE_SCHEDULE":
      return saveSchedule(message.schedule || {});
    case "CLEAR_SCHEDULE":
      return clearSchedule();
    case "START_AUTOMATION":
      return startAutomation({
        source: message.source || "manual",
        confirmedUnsupported: message.confirmedUnsupported === true
      });
    case "STOP_AUTOMATION":
      return stopAutomation({ reason: message.reason || "manual" });
    case "STATUS_UPDATE":
      return handleStatusUpdate(message, sender);
    case "OTP_DETECTED":
      return handleOtpDetected(message, sender);
    case "REQUEST_RELOAD":
      return handleReloadRequest(message, sender);
    case "SERVER_COOLDOWN_DETECTED":
      return activateServerCooldown(message.cooldown || message.parsed || message, sender);
    case "SERVER_COOLDOWN_FINISHED":
      return finishServerCooldownAndResume("cooldown_end");
    case "CHALLENGE_STATE":
      return handleChallengeState(message, sender);
    case "CAPTCHA_RESULT": {
      const tabId = sender.tab?.id;
      logVerification("background message received", {
        type: "CAPTCHA_RESULT",
        tabId,
        success: message.success === true,
        source: message.source || sender.url || ""
      });
      const state = await Storage.ensureDefaults();
      if (!AutomationController.isActive(state.status) || AutomationController.isOtpLocked(state.status)) {
        return { ok: true, ignored: true };
      }
      if (message.success === true && tabId) {
        await handleChallengeState({
          active: false,
          completed: true,
          selector: message.selector || "cf-turnstile-response",
          source: message.source || "cf_frame",
          url: sender.url || message.url || ""
        }, sender).catch((error) => {
          log(`Verification completion routing failed: ${error.message}`);
        });
      }
      if (tabId) {
        sendToContent(tabId, message, { reinject: false }).catch(() => {});
      }
      return { ok: true };
    }
    case "CAN_RUN_VERIFICATION_AUTOMATION": {
      const policy = await getVerificationAutomationPolicy(sender);
      log(policy.allowed ? "Safe verification handoff allowed" : `Verification handoff blocked: ${policy.reason}`);
      return { ok: true, ...policy };
    }
    case "FIVA_GET_PROFILE":
      return { ok: true, profile: await getFivaProfile() };
    case "FIVA_SAVE_PROFILE": {
      const profile = await saveFivaProfile(message.profile || {});
      const store = await getFivaRuntimeStore();
      return { ok: true, profile, store, runtime: store.runtimeState, activity: store.lastActivity };
    }
    case "FIVA_CLEAR_PROFILE":
      await clearFivaProfile();
      return { ok: true, store: defaultFivaRuntimeStore(), runtime: defaultFivaRuntime(), activity: [] };
    case "FIVA_GET_ACTIVITY":
      return { ok: true, activity: await getFivaActivity(), runtime: await getFivaRuntime(), store: await getFivaRuntimeStore() };
    case "FIVA_GET_RUNTIME": {
      const store = await getFivaRuntimeStore();
      return { ok: true, store, runtime: store.runtimeState, activity: store.lastActivity };
    }
    case "FIVA_ACTIVITY": {
      await appendFivaActivity(message.message || "", message.level || "info", {
        state: message.state,
        status: message.status,
        failedFields: message.failedFields
      });
      const store = await getFivaRuntimeStore();
      return { ok: true, store, runtime: store.runtimeState, activity: store.lastActivity };
    }
    case "FIVA_ACTIVITY_UPDATE":
      return { ok: true };
    case "FIVA_RUNTIME_UPDATE":
    case "FIVA_RUNTIME_UPDATED": {
      const runtime = await saveFivaRuntime(message.runtime || message.store?.runtimeState || {});
      const store = await getFivaRuntimeStore();
      return { ok: true, store, runtime, activity: store.lastActivity };
    }
    case "FIVA_FILL_ACTIVE_FORM":
      return fillFivaForm(message.profile || null, {
        confirmedUnsupported: message.confirmedUnsupported === true,
        reviewFields: message.reviewFields || []
      });
    case "FIVA_STOP_AUTOFILL":
      return stopFivaAutofill(message.reason || "popup");
    case "FIVA_CONTENT_READY":
      if (sender.tab?.id && !fivaReadyTabs.has(sender.tab.id)) {
        fivaReadyTabs.add(sender.tab.id);
        console.log("[FIVA]", "content ready");
      }
      return { ok: true };
    case "OPEN_SIDE_PANEL": {
      if (!chrome.sidePanel?.open) return { ok: false, error: "Side panel is not available in this Chrome version." };
      const activeTab = await getActiveTab().catch(() => null);
      const windowId = activeTab?.windowId;
      if (!windowId) throw new Error("No active window found.");
      await chrome.sidePanel.open({ windowId });
      return { ok: true };
    }
    case "TEST_NOTIFICATION":
      await notify("test", "VisaFlowX", "Notifications are working.");
      return { ok: true };
    case "TEST_ALARM":
    case "STOP_ALARM":
    case "MUTE_ALARM":
    case "SET_ALARM_VOLUME": {
      const state = await Storage.ensureDefaults();
      const tabId = state.status.activeTabId || (await getActiveTab())?.id;
      if (!tabId) throw new Error("Open the IVAC page to test the alarm.");
      await ensureContent(tabId);
      await sendToContent(tabId, message);
      return { ok: true };
    }
    case "HARDWARE_CLICK": {
      log("Hardware verification click ignored; safe handoff mode is active");
      return { ok: false, ignored: true, reason: "safe_handoff_no_hardware_click" };
    }
    // ── OTP alarm fallback channels ──────────────────────────────
    case "OTP_BADGE_FLASH": {
      // Flash the extension action badge as a visual OTP alert
      chrome.action.setBadgeText({ text: "OTP" }).catch(() => {});
      chrome.action.setBadgeBackgroundColor({ color: "#FF4500" }).catch(() => {});
      setTimeout(() => chrome.action.setBadgeText({ text: "" }).catch(() => {}), 4000);
      return { ok: true };
    }
    case "OTP_POPUP_PULSE":
      // Broadcast to popup so it can apply a pulse animation
      await broadcast({ type: "OTP_POPUP_PULSE" });
      return { ok: true };
    case "OTP_FALLBACK_NOTIFICATION": {
      const title = message.title   || "OTP Required — VisaFlowX";
      const body  = message.message || "OTP page detected. Complete your verification now!";
      await notify("otp", title, body);
      return { ok: true };
    }
    case "UNLOCK_AUDIO_CONTEXT":
      // Relay to the tracked IVAC content script so popup interaction can
      // unlock/retry the page-owned OTP alarm.
      {
        const state = await Storage.ensureDefaults();
        const tabId = state.status.activeTabId || sender.tab?.id || (await getActiveTab().catch(() => null))?.id;
        if (tabId) {
          await sendToContent(tabId, { type: "UNLOCK_AUDIO_CONTEXT" }, { reinject: false }).catch(() => {});
        }
        await broadcast({ type: "UNLOCK_AUDIO_CONTEXT" });
      }
      return { ok: true };
    default:
      throw new Error(`Unknown message: ${message?.type || "empty"}`);
  }
}

function isUiOnlyMessage(type = "") {
  return String(type || "").startsWith("FIVA_") || type === "OPEN_SIDE_PANEL" || type === "GET_STATE";
}

async function restoreScheduleAlarm() {
  const state = await Storage.ensureDefaults();
  if (state.schedule.enabled && (AutomationController.isOtpLocked(state.status) || AutomationController.isStopped(state.status))) {
    await chrome.alarms.clear(Scheduler.alarmName()).catch(() => {});
    await Storage.saveSchedule({ enabled: false, date: "", time: "", nextRunAt: null }).catch(() => {});
    await updateStatus({
      schedulerState: "Not scheduled",
      lastAction: "Scheduler cancelled because automation is stopped"
    }, { force: AutomationController.isOtpLocked(state.status) });
    log("Scheduler cancelled");
    return;
  }

  if (
    state.schedule.enabled
    && state.schedule.nextRunAt
    && state.schedule.nextRunAt > Date.now()
    && !AutomationController.isOtpLocked(state.status)
    && !AutomationController.isStopped(state.status)
  ) {
    await chrome.alarms.create(Scheduler.alarmName(), { when: state.schedule.nextRunAt });
    log("Scheduler armed");
    return;
  }

  if (state.schedule.enabled && (!state.schedule.nextRunAt || state.schedule.nextRunAt <= Date.now())) {
    await clearSchedule();
  } else {
    await chrome.alarms.clear(Scheduler.alarmName()).catch(() => {});
  }
}

async function restoreRuntimeAlarms() {
  await restoreScheduleAlarm();
  await restoreCooldownAlarm();
}

chrome.runtime.onInstalled.addListener(() => {
  restoreRuntimeAlarms().catch(console.error);
});

chrome.runtime.onStartup.addListener(() => {
  restoreRuntimeAlarms().catch(console.error);
});

restoreCooldownAlarm().catch(() => {});

chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  handleMessage.call(null, message, sender)
    .then((response) => sendResponse(response))
    .catch(async (error) => {
      if (message?.type === "START_AUTOMATION") {
        await updateStatus({
          state: STATES.RETRYING,
          lastAction: "Start failed. Retrying automatically.",
          lastError: error.message
        }).catch(() => {});
      } else if (message?.type === "STOP_AUTOMATION") {
        await updateStatus({
          state: STATES.STOPPED,
          retryCountdownEndsAt: null,
          lastAction: "Automation stopped after stop error",
          lastError: error.message
        }, { force: true }).catch(() => {});
      } else if (!isUiOnlyMessage(message?.type)) {
        await updateStatus({
          state: STATES.ERROR,
          lastAction: "Error",
          lastError: error.message
        }).catch(() => {});
      }
      if (!isUiOnlyMessage(message?.type)) {
        await notify("error", "VisaFlowX error", error.message).catch(() => {});
      }
      sendResponse({ ok: false, error: error.message });
    });
  return true;
});

chrome.commands.onCommand.addListener((command) => {
  if (command !== "toggle-automation") return;
  Storage.ensureDefaults()
    .then((state) => ([STATES.IDLE, STATES.STOPPED, STATES.SCHEDULED, STATES.OTP_DETECTED].includes(state.status.state)
      ? startAutomation({ source: "hotkey" })
      : stopAutomation({ reason: "hotkey" })))
    .catch((error) => {
      updateStatus({ state: STATES.ERROR, lastAction: "Hotkey failed", lastError: error.message });
    });
});

chrome.alarms.onAlarm.addListener((alarm) => {
  if (alarm.name === COOLDOWN_ALARM) {
    finishServerCooldownAndResume("cooldown_end").catch(console.error);
    return;
  }
  if (alarm.name !== Scheduler.alarmName()) return;
  Storage.ensureDefaults()
    .then(async (state) => {
      await Storage.saveSchedule({ enabled: false, date: "", time: "", nextRunAt: null });
      await chrome.alarms.clear(Scheduler.alarmName()).catch(() => {});
      log("Scheduler cancelled");

      if (!state.schedule.enabled) return;
      if (!AutomationController.canStart(state.status, "schedule")) {
        await updateStatus({
          state: AutomationController.isOtpLocked(state.status)
            ? STATES.OTP_DETECTED
            : AutomationController.isActive(state.status)
              ? state.status.state
              : STATES.STOPPED,
          schedulerState: "Not scheduled",
          lastAction: "Scheduled run cancelled because automation is not idle",
          lastError: ""
        }, { force: AutomationController.isOtpLocked(state.status) });
        return;
      }

      await startAutomation({ source: "schedule" });
    })
    .catch((error) => {
      updateStatus({ state: STATES.ERROR, schedulerState: "Failed", lastAction: "Scheduled run failed", lastError: error.message });
      notify("error", "Scheduled run failed", error.message);
    });
});

chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
  const nextUrl = changeInfo.url || tab.url || tab.pendingUrl || "";
  if (nextUrl && !isIvacUrl(nextUrl)) {
    clearChallengeForTab(tabId, "Verification cleared after leaving IVAC page").catch(() => {});
    return;
  }
  if (changeInfo.status !== "complete" || !isIvacUrl(tab.url || "")) return;
  Storage.ensureDefaults().then((state) => {
    if (state.status.activeTabId !== tabId) return;
    if (challengeState.isActive(tabId)) {
      log("Navigation resume blocked due to active verification");
      return;
    }

    if (!AutomationController.canAutoResume(state.status)) {
      log(AutomationController.isOtpLocked(state.status) ? "Navigation detected but blocked by OTP state." : `Navigation resume blocked due to ${state.status.state}`);
      return;
    }

    const now = Date.now();
    const lastResumeAt = lastResumeAtByTabId.get(tabId) || 0;
    if (now - lastResumeAt < RESUME_GUARD_MS || automationResumeBusy) return;
    lastResumeAtByTabId.set(tabId, now);

    startAutomation({ source: "resume" }).catch((error) => {
      updateStatus({ state: STATES.RETRYING, lastAction: "Resume failed. Retrying automatically.", lastError: error.message });
    });
  }).catch(() => {});
});

chrome.tabs.onRemoved.addListener((tabId) => {
  if (reloadTimerTabId === tabId) clearReloadTimer();
  lastResumeAtByTabId.delete(tabId);
  fivaReadyTabs.delete(tabId);
  challengeState.clear(tabId);
  clearVerificationStaleTimer(tabId);
  clearVerificationFramePoll(tabId);
  clearVerificationReconcileTimer(tabId);
  Storage.ensureDefaults().then((state) => {
    if (state.status.activeTabId === tabId && isAutomationActive(state.status)) {
      stopAutomation({ reason: "tracked_tab_closed" }).catch(() => {});
    }
  }).catch(() => {});
});
