'use strict';
/** v62 consolidated stability (v47-v51) v47 + mesh bridge toggle (mirror APK RelayPrefs.mesh_bridge) */
/* $ / store / state 由 app.js 提供；本檔須在 app.js 之後載入 */

function loadV48State() {
  state.roomPin = store.get('p2p_room_pin', '');
  state.stayBehind = store.get('p2p_stay_behind', '0') === '1';
  state.meshBridge = store.get('p2p_mesh_bridge', '0') === '1';
  state.meshHopLog = store.getJSON('p2p_mesh_hops', []);
  state.taskBoard = store.getJSON('p2p_tasks', []);
  state.connStats = store.getJSON('p2p_conn_stats', { ok: 0, fail: 0 });
}

function bindRoomPinUI() {
  const inp = $('relayRoomPin');
  if (!inp) return;
  inp.value = state.roomPin || '';
  inp.oninput = (e) => {
    state.roomPin = e.target.value.trim();
    store.set('p2p_room_pin', state.roomPin);
  };
}

function bindMeshBridgeUI() {
  const sw = $('meshBridgeSwitch');
  if (!sw) return;
  sw.checked = !!state.meshBridge;
  sw.onchange = (e) => {
    state.meshBridge = e.target.checked;
    store.set('p2p_mesh_bridge', state.meshBridge ? '1' : '0');
    const hint = $('meshBridgeHint');
    if (hint) {
      hint.textContent = state.meshBridge
        ? '近距 P2P 連上時仍保持中繼在 Mesh 內。'
        : '近距 P2P 連上時會暫停中繼（與 APK 預設相同）。';
    }
    if (state.meshBridge && typeof connectRelay === 'function' && !ws) connectRelay(true);
  };
  sw.dispatchEvent(new Event('change'));
}

function relayUrlWithRoom(base, room) {
  const pin = (state.roomPin || '').trim();
  let url = base + (base.includes('?') ? '&' : '?') + 'room=' + encodeURIComponent(room || state.relayRoom || 'team1');
  if (pin) url += '&pin=' + encodeURIComponent(pin);
  return url;
}

function recordConnStat(ok) {
  state.connStats = state.connStats || { ok: 0, fail: 0 };
  if (ok) state.connStats.ok++; else state.connStats.fail++;
  store.setJSON('p2p_conn_stats', state.connStats);
}

function showOnboardingV62() {
  if (store.get('p2p_onboard', '0') === '1') return;
  const el = $('onboardModal');
  if (!el) return;
  el.classList.remove('hidden');
}

function closeOnboardingV62() {
  store.set('p2p_onboard', '1');
  $('onboardModal')?.classList.add('hidden');
}

function telemetryExtras() {
  const out = {};
  if (typeof state._batteryPct === 'number') out.battery = state._batteryPct;
  return out;
}

function refreshBatteryTelemetry() {
  if (!navigator.getBattery) return;
  navigator.getBattery().then((bat) => {
    state._batteryPct = Math.round(bat.level * 100);
    bat.onlevelchange = () => { state._batteryPct = Math.round(bat.level * 100); };
  }).catch(() => {});
}

function noteMeshHop(msg, fromConnId) {
  if (!msg || !msg.id || fromConnId === state.id) return;
  const entry = { id: msg.id, type: msg.type, ttl: msg.ttl, ts: Date.now() };
  state.meshHopLog = (state.meshHopLog || []).slice(-30);
  state.meshHopLog.push(entry);
  store.setJSON('p2p_mesh_hops', state.meshHopLog);
  const el = $('meshHopStatus');
  if (el) el.textContent = `最近轉發：${msg.type} TTL=${msg.ttl}`;
}

function detectPortableRouterMode() {
  const h = location.hostname;
  if (/^\d+\.\d+\.\d+\.\d+$/.test(h) && location.protocol === 'https:') {
    if (typeof setConnMode === 'function') setConnMode('hotspot');
    const el = $('connModeHint');
    if (el) el.textContent = '偵測到區網 IP，已切換熱點+QR 模式。';
  }
}

async function runTurnNatHint() {
  const el = $('turnNatStatus');
  if (!el) return;
  el.textContent = '測試中…';
  try {
    const pc = new RTCPeerConnection({ iceServers: iceServers() });
    pc.createDataChannel('t');
    const offer = await pc.createOffer();
    await pc.setLocalDescription(offer);
    await new Promise((r) => setTimeout(r, 2500));
    const cands = (await new Promise((res) => {
      const found = [];
      pc.onicecandidate = (e) => { if (e.candidate) found.push(e.candidate.candidate); };
      setTimeout(() => res(found), 500);
    }));
    pc.close();
    const hasRelay = cands.some((c) => / typ relay /.test(c));
    const hasSrflx = cands.some((c) => / typ srflx /.test(c));
    if (hasRelay) el.textContent = '✓ 偵測到 TURN relay，跨網語音較穩';
    else if (hasSrflx) el.textContent = '⚠️ 僅 STUN，對稱 NAT 可能需自建 TURN';
    else el.textContent = '⚠️ 僅 host，請用中繼或熱點+QR';
  } catch (e) {
    el.textContent = '測試失敗：' + e.message;
  }
}

function bindStayBehindUI() {
  const sw = $('stayBehindSwitch');
  if (!sw) return;
  sw.checked = !!state.stayBehind;
  sw.onchange = (e) => {
    state.stayBehind = e.target.checked;
    store.set('p2p_stay_behind', state.stayBehind ? '1' : '0');
    const ptt = $('pttBtn');
    if (ptt && state.stayBehind) ptt.disabled = true;
  };
  if (sw.checked) { const ptt = $('pttBtn'); if (ptt) ptt.disabled = true; }
}

function renderTaskBoard() {
  const el = $('taskBoardList');
  if (!el) return;
  const tasks = state.taskBoard || [];
  if (!tasks.length) { el.innerHTML = '<div class="muted small">尚無任務</div>'; return; }
  el.innerHTML = tasks.map((t, i) =>
    `<label class="radio-row"><input type="checkbox" data-idx="${i}" ${t.done ? 'checked' : ''}/> ${escapeHtml(t.text)}</label>`
  ).join('');
  el.querySelectorAll('input').forEach((cb) => {
    cb.onchange = () => {
      const i = +cb.dataset.idx;
      state.taskBoard[i].done = cb.checked;
      store.setJSON('p2p_tasks', state.taskBoard);
    };
  });
}

function addTaskFromQuick(text) {
  state.taskBoard = state.taskBoard || [];
  state.taskBoard.push({ text, done: false, ts: Date.now() });
  store.setJSON('p2p_tasks', state.taskBoard);
  renderTaskBoard();
  if (typeof meshSend === 'function') meshSend({ type: 'TEXT', text: '📋 任務：' + text });
}

function checkAppVersionV62(showBanner) {
  const el = $('versionCheckStatus');
  const badge = $('appVersionBadge');
  const build = window.PICH_BUILD || 0;
  fetch('./sw.js?v=' + Date.now(), { cache: 'no-store' }).then((r) => r.text()).then((t) => {
    const m = t.match(/pich2pich-v(\d+)/);
    const remote = m ? +m[1] : 0;
    const local = +(store.get('p2p_sw_ver', '0') || 0);
    const label = `網頁版 v${remote || build || '?'}（建置 ${build || '?'})`;
    if (badge) badge.textContent = 'v' + (remote || build || '?');
    if (el) {
      if (remote && build && remote > build) {
        el.textContent = `${label} — 有新版本，請點「清除快取並更新」`;
        el.className = 'small warn';
      } else if (local && remote > local) {
        el.textContent = `${label} — 偵測到更新 v${remote}`;
        el.className = 'small warn';
      } else {
        el.textContent = label + ' ✓ 已是最新';
        el.className = 'small ok';
      }
    }
    if (remote && (remote > local || (build && remote > build)) && (showBanner || local > 0)) {
      const banner = $('updateBanner');
      const text = $('updateBannerText');
      if (text) text.textContent = `有新版本 v${remote}，請點「立即更新」或到設定→清除快取並更新。`;
      banner?.classList.remove('hidden');
    }
    if (remote) store.set('p2p_sw_ver', String(remote));
  }).catch(() => {
    if (el) el.textContent = `網頁版 v${build || '?'}`;
    if (badge) badge.textContent = 'v' + (build || '?');
  });
}

function initStabilityV62() {
  loadV48State();
  bindRoomPinUI();
  bindMeshBridgeUI();
  bindStayBehindUI();
  renderTaskBoard();
  refreshBatteryTelemetry();
  detectPortableRouterMode();
  showOnboardingV62();
  checkAppVersionV62();
  const turnBtn = $('turnNatTestBtn');
  if (turnBtn) turnBtn.onclick = runTurnNatHint;
  $('taskAddBtn')?.addEventListener('click', () => {
    const t = ($('taskInput')?.value || '').trim();
    if (!t) return;
    addTaskFromQuick(t);
    if ($('taskInput')) $('taskInput').value = '';
  });
  bindVoicePathGuide();
  bindOfflineMapQuickStart();
  bindOfflineMapBundle();
  const e2eeClose = $('e2eeQrCloseBtn');
  if (e2eeClose) e2eeClose.onclick = () => $('e2eeQrModal')?.classList.add('hidden');
  bindStabilityHints();
  bindMedCardQr();
  bindGeofenceMapPick();
  bindUpdateBanner();
  if (typeof initApkParity === 'function') initApkParity();
  const forceBtn = $('forceUpdateBtn');
  const apply2 = $('applyUpdateBtn2');
  if (forceBtn && !forceBtn._v62) {
    forceBtn._v62 = true;
    forceBtn.onclick = () => forceClearCacheAndUpdate();
  }
  if (apply2 && !apply2._v62) {
    apply2._v62 = true;
    apply2.onclick = () => applyPwaUpdate();
  }
  // iOS 主畫面：啟動時若建置過舊，提示更新
  const standalone = window.matchMedia('(display-mode: standalone)').matches
    || window.navigator.standalone === true;
  if (standalone) checkAppVersionV62(true);

}

window.telemetryExtras = telemetryExtras;
window.noteMeshHop = noteMeshHop;
window.recordConnStat = recordConnStat;
window.relayUrlWithRoom = relayUrlWithRoom;

function bindVoicePathGuide() {
  const el = $('voicePathGuide');
  if (!el) return;
  const update = () => {
    const relay = typeof ws !== 'undefined' && ws && ws.readyState === WebSocket.OPEN;
    const p2p = typeof rtc !== 'undefined' && rtc.conns.some((c) => c.dc && c.dc.readyState === 'open');
    const android = /Android/i.test(navigator.userAgent || '');
    if (p2p && !relay) {
      el.innerHTML = '⚡ <b>WebRTC 即時語音</b>：按住 PTT 走標準 Opus（與 Chrome／iPhone 互通）';
      el.className = 'voice-path-guide ok';
    } else if (p2p && relay) {
      el.innerHTML = '🔀 P2P+中繼：語音優先 WebRTC；文字／位置可走 Mesh';
      el.className = 'voice-path-guide ok';
    } else if (relay) {
      el.innerHTML = android
        ? '📡 <b>中繼語音片段</b>（約 0.7s）：與 APK 跨平台互通；近距請用熱點+QR 或 APK Wi‑Fi Direct'
        : '📡 <b>中繼語音片段</b>（約 0.7s）：跨網穩定；無外網請用熱點+QR 兩輪配對';
      el.className = 'voice-path-guide warn';
    } else {
      el.innerHTML = '❌ 未連線：有網→中繼+6碼；無網→掃 QR／貼配對碼（Chrome 無 Wi‑Fi Direct）';
      el.className = 'voice-path-guide bad';
    }
  };
  update();
  setInterval(update, 4000);
}

function bindOfflineMapQuickStart() {
  const status = $('mapDownloadStatus');
  const checkBtn = $('mapDownloadCheckBtn');
  if (checkBtn && !checkBtn._v49) {
    checkBtn._v49 = true;
    checkBtn.addEventListener('click', () => {
      if (status) status.textContent = '提示：APK 用「下載台灣離線圖」；網頁用 PMTiles。兩者皆支援進山離線。';
    });
  }
  const dlBtn = $('mapDownloadBtn');
  if (dlBtn && !dlBtn._v49Hint) {
    dlBtn._v49Hint = true;
    const orig = dlBtn.onclick;
    dlBtn.addEventListener('click', () => {
      if (status && !status.textContent) {
        status.textContent = '下載中…（與 APK MapLibre 圖磚快取同概念）';
      }
    });
  }
  if (status && state.mapSrc === 'offline') {
    status.textContent = status.textContent || '✓ 離線 PMTiles 模式（APK 對應：MapLibre 圖磚下載）';
  }
}

async function bindOfflineMapBundle() {
  const btn = $('mapBundleFetchBtn');
  const status = $('mapBundleStatus');
  if (!btn || btn._v50) return;
  btn._v50 = true;
  btn.onclick = async () => {
    if (!status) return;
    status.textContent = '檢查 map/region.pmtiles…';
    try {
      const r = await fetch('map/region.pmtiles', { method: 'HEAD', cache: 'no-store' });
      if (r.ok) {
        state.pmtilesPath = 'map/region.pmtiles';
        store.set('p2p_pmtiles', state.pmtilesPath);
        state.mapSrc = 'offline';
        store.set('p2p_mapsrc', 'offline');
        if (typeof applyMapSource === 'function') applyMapSource();
        status.textContent = '✓ 已載入伺服器預建 PMTiles';
        status.className = 'map-download-status ok';
      } else {
        status.textContent = '伺服器尚無預建檔，請用電腦執行 tools/make_pmtiles.py';
        status.className = 'map-download-status warn';
      }
    } catch (e) {
      status.textContent = '無法連線：' + e.message;
      status.className = 'map-download-status bad';
    }
  };
}

function bindStabilityHints() {
  const pinHint = $('relayPinHint');
  if (pinHint && !pinHint._v62) {
    pinHint._v62 = true;
    pinHint.textContent = '遠距中繼建議在伺服器設 RELAY_ROOM_PINS；跨網 WebRTC 不穩可自建 coturn。';
  }
  const batHint = $('batteryHint');
  if (batHint && !batHint._v62) {
    batHint._v62 = true;
    batHint.textContent = '長時間開定位＋中繼較耗電，戶外請帶行動電源。';
  }
  const pwaHint = $('pwaUpdateHint');
  if (pwaHint && !pwaHint._v62) {
    pwaHint._v62 = true;
    pwaHint.textContent = '更新後請完全關閉分頁再開，或清除網站資料，以載入最新 v62。';
  }
  if (navigator.getBattery) {
    navigator.getBattery().then(b => {
      const el = $('batteryLevelHint');
      if (!el) return;
      const pct = Math.round(b.level * 100);
      el.textContent = `本機電量約 ${pct}%${b.charging ? '（充電中）' : ''}`;
    }).catch(() => {});
  }
}

function bindMedCardQr() {
  const btn = $('medCardQrBtn');
  const modal = $('medCardQrModal');
  const close = $('medCardQrCloseBtn');
  const canvas = $('medCardQrCanvas');
  if (!btn || btn._v62) return;
  btn._v62 = true;
  btn.onclick = () => {
    const payload = medCardQrPayload();
    if (!payload || payload === 'p2p-med:|||') {
      alert('請先填寫並儲存急救卡');
      return;
    }
    if (modal) modal.classList.remove('hidden');
    const Qr = (typeof qrLib === 'function' ? qrLib() : null) || window.qrcode;
    if (!canvas || !Qr) return;
    try {
      const qr = Qr(0, 'M');
      qr.addData(payload);
      qr.make();
      if (typeof renderQrCanvas === 'function') {
        const off = renderQrCanvas(qr, Math.max(4, Math.floor(200 / qr.getModuleCount())), 2);
        canvas.width = 220;
        canvas.height = 220;
        const ctx = canvas.getContext('2d');
        ctx.fillStyle = '#fff';
        ctx.fillRect(0, 0, 220, 220);
        ctx.drawImage(off, 0, 0, 220, 220);
      }
    } catch (e) {
      console.warn('medCard QR', e);
    }
  };
  if (close) close.onclick = () => modal?.classList.add('hidden');
}

function medCardQrPayload() {
  const blood = ($('medBlood')?.value || '').trim();
  const allergies = ($('medAllergies')?.value || '').trim();
  const notes = ($('medNotes')?.value || '').trim();
  const phone = ($('medPhone')?.value || '').trim();
  return 'p2p-med:' + [blood, allergies, notes, phone].join('|');
}

let geofenceCenterMarker = null;

function setGeofenceCenterMarker(lat, lon) {
  if (!map || lat == null || lon == null) return;
  const ll = [lon, lat];
  if (!geofenceCenterMarker) {
    const el = document.createElement('div');
    el.style.cssText = 'width:14px;height:14px;border-radius:50%;background:#f57c00;border:2px solid #fff;box-shadow:0 0 4px rgba(0,0,0,.4)';
    geofenceCenterMarker = new maplibregl.Marker({ element: el }).setLngLat(ll).addTo(map);
  } else {
    geofenceCenterMarker.setLngLat(ll);
  }
}

function bindGeofenceMapPick() {
  const btn = $('geofenceMapPickBtn');
  if (!btn || btn._v62) return;
  btn._v62 = true;
  btn.onclick = () => {
    state.geofencePickMode = !state.geofencePickMode;
    btn.textContent = state.geofencePickMode ? '點地圖設中心（進行中）' : '地圖點選中心';
    if (state.geofencePickMode) {
      showPage('map');
      ensureMap();
      wireGeofenceMapClick();
      showMapHint('點地圖上任意位置設為圍欄中心');
    }
  };
}

function wireGeofenceMapClick() {
  if (!map || map._geofencePickBound) return;
  map._geofencePickBound = true;
  const btn = $('geofenceMapPickBtn');
  map.on('click', (e) => {
    if (!state.geofencePickMode) return;
    const g = state.geofence;
    g.lat = e.lngLat.lat;
    g.lon = e.lngLat.lng;
    g.enabled = true;
    const sw = $('geofenceSwitch');
    if (sw) sw.checked = true;
    state.geofencePickMode = false;
    if (btn) btn.textContent = '地圖點選中心';
    persistGeofence();
    setGeofenceCenterMarker(g.lat, g.lon);
    checkGeofence();
    showMapHint('已設圍欄中心');
  });
}

function refreshGeofenceCenterMarker() {
  const g = state.geofence;
  if (g && g.lat != null && g.lon != null) setGeofenceCenterMarker(g.lat, g.lon);
}


function bindUpdateBanner() {
  const banner = $('updateBanner');
  const close = $('updateBannerClose');
  const apply = $('updateBannerApply');
  const text = $('updateBannerText');
  if (close && !close._v62) {
    close._v62 = true;
    close.onclick = () => banner?.classList.add('hidden');
  }
  if (apply && !apply._v62) {
    apply._v62 = true;
    apply.onclick = () => applyPwaUpdate();
  }
  if (!('serviceWorker' in navigator) || !banner) return;

  const showUpdate = (ver) => {
    if (text) text.textContent = `有新版本 v${ver || ''}，點「立即更新」載入最新功能。`;
    banner.classList.remove('hidden');
  };

  navigator.serviceWorker.register('./sw.js', { updateViaCache: 'none' }).then((reg) => {
    reg.update().catch(() => {});
    reg.addEventListener('updatefound', () => {
      const nw = reg.installing;
      if (!nw) return;
      nw.addEventListener('statechange', () => {
        if (nw.state === 'installed' && navigator.serviceWorker.controller) showUpdate('');
      });
    });
    if (reg.waiting) showUpdate('');
    navigator.serviceWorker.addEventListener('controllerchange', () => {
      if (window._pichSwReloading) return;
      window._pichSwReloading = true;
      window.location.reload();
    });
  }).catch(() => {});

  // iOS 主畫面：回到 App 時檢查 sw 版本
  document.addEventListener('visibilitychange', () => {
    if (document.visibilityState === 'visible') checkAppVersionV62(true);
  });
}

async function forceClearCacheAndUpdate() {
  try {
    if ('serviceWorker' in navigator) {
      const regs = await navigator.serviceWorker.getRegistrations();
      await Promise.all(regs.map((r) => r.unregister()));
    }
    if ('caches' in window) {
      const keys = await caches.keys();
      await Promise.all(keys.map((k) => caches.delete(k)));
    }
  } catch (_) {}
  store.set('p2p_sw_ver', '0');
  window.location.href = './index.html?nocache=' + Date.now();
}
window.forceClearCacheAndUpdate = forceClearCacheAndUpdate;

async function applyPwaUpdate() {
  if (!('serviceWorker' in navigator)) {
    window.location.reload();
    return;
  }
  const reg = await navigator.serviceWorker.getRegistration();
  if (reg?.waiting) {
    reg.waiting.postMessage({ type: 'SKIP_WAITING' });
    return;
  }
  try {
    await reg?.update();
  } catch (_) {}
  window.location.reload();
}
window.applyPwaUpdate = applyPwaUpdate;

window.initStabilityV62 = initStabilityV62;
window.initStabilityV51 = initStabilityV62;
window.initStabilityV50 = initStabilityV62;
window.initStabilityV49 = initStabilityV62;
window.initStabilityV48 = initStabilityV62;
window.initStabilityV47 = initStabilityV62;
window.addTaskFromQuick = addTaskFromQuick;
window.wireGeofenceMapClick = wireGeofenceMapClick;
window.wireGeofenceMapPick = wireGeofenceMapClick;
