'use strict';

let _internetReachable = null;
let _probeAt = 0;

async function probeOne(url, ms) {
  const ctrl = new AbortController();
  const tid = setTimeout(() => ctrl.abort(), ms);
  try {
    await fetch(url, { mode: 'no-cors', cache: 'no-store', signal: ctrl.signal });
    clearTimeout(tid);
    return true;
  } catch {
    clearTimeout(tid);
    return false;
  }
}

async function probeInternetReachable() {
  if (navigator.onLine === false) {
    _internetReachable = false;
    _probeAt = Date.now();
    return false;
  }
  const urls = [
    'https://connectivitycheck.gstatic.com/generate_204',
    'https://www.gstatic.com/generate_204',
    'https://pich2pich.web.app/favicon.ico',
  ];
  for (const u of urls) {
    if (await probeOne(u, 3500)) {
      _internetReachable = true;
      _probeAt = Date.now();
      return true;
    }
  }
  /* 探測失敗 ≠ 沒網路（可能被擋 gstatic）；仍應嘗試連中繼 */
  _internetReachable = null;
  _probeAt = Date.now();
  return null;
}

function hasRelayUp() {
  return typeof ws !== 'undefined' && ws && ws.readyState === WebSocket.OPEN;
}

function hasP2pUp() {
  return typeof hasMeshLink === 'function' && hasMeshLink();
}

/** 瀏覽器回報完全沒網路（飛航/關 WiFi） */
function noBrowserNetwork() {
  return navigator.onLine === false;
}

/** 使用者手動開啟無網優先（對齊 APK OfflineMode） */
function prefersOfflineFirst() {
  if (typeof state !== 'undefined' && state.offlineFirst) return true;
  try { return store.get('p2p_offline_first', '0') === '1'; } catch (_) { return false; }
}

/** 建議走熱點貼碼：確定無外網或瀏覽器離線，且中繼/P2P 都未連上 */
function needsOfflinePairing() {
  if (prefersOfflineFirst() && !hasRelayUp() && !hasP2pUp()) return true;
  if (noBrowserNetwork()) return true;
  if (hasRelayUp() || hasP2pUp()) return false;
  return _internetReachable === false;
}

function isOfflineMode() {
  return needsOfflinePairing();
}

function shouldConnectRelay() {
  if (prefersOfflineFirst()) return false;
  return navigator.onLine !== false;
}

function onRelayOpen() {
  _internetReachable = true;
  _probeAt = Date.now();
  if (typeof applyOfflineModeUI === 'function') applyOfflineModeUI();
}

function onRelayFailed() {
  /* 中繼失敗 ≠ 無外網；勿因此切換離線模式 */
}

function resetNetworkProbe() {
  _internetReachable = null;
  _probeAt = 0;
}

async function forceRelayReconnect() {
  resetNetworkProbe();
  await probeInternetReachable();
  if (typeof connectRelay === 'function') connectRelay(true);
  if (typeof applyOfflineModeUI === 'function') applyOfflineModeUI();
}

function applyOfflineModeDefaults() {
  if (!noBrowserNetwork()) return;
  if (state.mapSrc === 'online') {
    state.mapSrc = store.get('p2p_pmtiles_cached', '0') === '1' ? 'offline' : 'blank';
    store.set('p2p_mapsrc', state.mapSrc);
    const off = $('mapOffline');
    const blank = $('mapBlank');
    if (state.mapSrc === 'offline' && off) off.checked = true;
    if (state.mapSrc === 'blank' && blank) blank.checked = true;
  }
}

function syncModeHints() {
  document.querySelectorAll('.mode-hint').forEach((el) => {
    const t = needsOfflinePairing() ? el.dataset.offline : el.dataset.online;
    if (t) el.textContent = t;
  });
}

function updateOfflineModeBanner() {
  const el = $('offlineModeBanner');
  if (!el) return;
  const relayUp = hasRelayUp();
  const show = needsOfflinePairing() && !relayUp;
  if (show) {
    el.classList.remove('hidden');
    document.body.classList.add('has-offline-banner');
    const t = el.querySelector('.offline-mode-text');
    if (t) {
      t.textContent = noBrowserNetwork()
        ? '無行動數據：請連同一熱點，再按「貼上配對碼」'
        : '無法連中繼：可貼碼/掃 QR 區網配對，或按「重新連中繼」';
    }
  } else {
    el.classList.add('hidden');
    document.body.classList.remove('has-offline-banner');
  }
}

function applyOfflineModeUI() {
  state.offlineMode = needsOfflinePairing();
  document.body.classList.toggle('no-browser-net', noBrowserNetwork());
  document.body.classList.toggle('needs-offline-pair', needsOfflinePairing());
  syncModeHints();
  updateOfflineModeBanner();
  if (typeof applyConnModeUI === 'function') applyConnModeUI();
  if (typeof updateRelayStatusLine === 'function') updateRelayStatusLine();
  if (typeof updateDegradeBanner === 'function') updateDegradeBanner();
  if (typeof renderRegionMapList === 'function') renderRegionMapList();
}

function syncNetworkMode() {
  applyOfflineModeDefaults();
  applyOfflineModeUI();
  if (noBrowserNetwork()) {
    if (typeof disconnectRelay === 'function') disconnectRelay();
  } else if (!hasRelayUp()) {
    if (typeof connectRelay === 'function') connectRelay(false);
  }
  if (map && typeof currentStyle === 'function') {
    try { map.setStyle(currentStyle()); } catch {}
  }
}

async function initOfflineMode() {
  state.offlineMode = needsOfflinePairing();
  await probeInternetReachable();
  applyOfflineModeDefaults();
  applyOfflineModeUI();
  if (!window._p2pNetBound) {
    window._p2pNetBound = true;
    window.addEventListener('online', () => {
      resetNetworkProbe();
      probeInternetReachable().then(() => syncNetworkMode());
    });
    window.addEventListener('offline', syncNetworkMode);
  }
  if (noBrowserNetwork() && typeof disconnectRelay === 'function') disconnectRelay();
}

window.isOfflineMode = isOfflineMode;
window.needsOfflinePairing = needsOfflinePairing;
window.shouldConnectRelay = shouldConnectRelay;
window.initOfflineMode = initOfflineMode;
window.applyOfflineModeUI = applyOfflineModeUI;
window.updateOfflineModeBanner = updateOfflineModeBanner;
window.syncNetworkMode = syncNetworkMode;
window.onRelayOpen = onRelayOpen;
window.onRelayFailed = onRelayFailed;
window.probeInternetReachable = probeInternetReachable;
window.forceRelayReconnect = forceRelayReconnect;
