'use strict';

function channelForConv(key) {
  return null;
}

function convKeyForMsg(msg) {
  if (msg.to && msg.to === state.id) return msg.sid;
  if (msg.to) return msg.to;
  if (msg.sid && msg.sid !== state.id && msg.type !== 'TELEMETRY') return msg.sid;
  return 'group';
}

function chatKeysForHome() {
  const keys = new Set(['group']);
  Object.keys(state.contacts || {}).forEach((k) => {
    if (k && k !== state.id && !k.startsWith('ch:')) keys.add(k);
  });
  return Array.from(keys).sort((a, b) => {
    if (a === 'group') return -1;
    if (b === 'group') return 1;
    const ta = typeof convTitle === 'function' ? convTitle(a) : a;
    const tb = typeof convTitle === 'function' ? convTitle(b) : b;
    return ta.localeCompare(tb, 'zh-Hant');
  });
}

function convTitleTeam(key) {
  return null;
}

function convAvatarTeam(key) {
  return null;
}

let lastWalkSpeed = 1.2;
let qualityTimer = null;
const rtcQuality = {};

function recordWalkSpeed(pos) {
  if (pos.coords.speed != null && pos.coords.speed > 0.3) {
    lastWalkSpeed = pos.coords.speed;
    state.lastWalkSpeed = lastWalkSpeed;
    store.set('p2p_walk_speed', String(lastWalkSpeed));
  }
}

function rallyEtaMinutes(lat, lon) {
  if (!state.self || lat == null || lon == null) return null;
  const d = distanceM(state.self.lat, state.self.lon, lat, lon);
  const spd = Math.max(lastWalkSpeed, 0.5);
  return Math.max(1, Math.round(d / spd / 60));
}

function formatEta(mins) {
  if (mins == null) return '';
  if (mins < 60) return `約 ${mins} 分鐘`;
  return `約 ${Math.floor(mins / 60)} 時 ${mins % 60} 分`;
}

async function navigateToRally(r) {
  if (!state.self) { alert('尚無定位'); return; }
  state.rallyNavTarget = { lat: r.lat, lon: r.lon, label: r.label, sid: r.sid };
  store.setJSON('p2p_rally_nav', state.rallyNavTarget);
  showPage('map');
  await planRoute({ lat: state.self.lat, lon: state.self.lon }, { lat: r.lat, lon: r.lon });
  showMapHint('已規劃前往集合點「' + (r.label || '') + '」的路線');
  redrawRallyNav();
}

function redrawRallyNav() {
  if (!map || !map.getSource('route')) return;
  if (!state.rallyNavTarget) return;
}

function urlBase64ToUint8Array(base64String) {
  const padding = '='.repeat((4 - (base64String.length % 4)) % 4);
  const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/');
  const raw = atob(base64);
  const out = new Uint8Array(raw.length);
  for (let i = 0; i < raw.length; i++) out[i] = raw.charCodeAt(i);
  return out;
}

async function subscribeWebPush() {
  if (typeof isOfflineMode === 'function' && isOfflineMode()) {
    alert('無網路時不訂閱推播；恢復網路後可訂閱');
    return false;
  }
  const isIos = /iPad|iPhone|iPod/.test(navigator.userAgent) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1);
  const standalone = window.matchMedia('(display-mode: standalone)').matches || window.navigator.standalone;
  if (isIos && !standalone) {
    alert('iOS 推播需先「加入主畫面」安裝 PWA（Safari 分享→加入主畫面），再從主畫面圖示開啟訂閱。');
    return false;
  }
  if (!('serviceWorker' in navigator) || !('PushManager' in window)) {
    alert('此瀏覽器不支援 Web Push（iOS 需 16.4+ 且已安裝 PWA）');
    return false;
  }
  const perm = await Notification.requestPermission();
  if (perm !== 'granted') return false;
  const reg = await navigator.serviceWorker.ready;
  let resp;
  try { resp = await window.apiFetch('/api/push/vapid'); } catch { return false; }
  if (!resp.ok) return false;
  const { publicKey } = await resp.json();
  if (!publicKey) return false;
  let sub = await reg.pushManager.getSubscription();
  if (!sub) {
    sub = await reg.pushManager.subscribe({
      userVisibleOnly: true,
      applicationServerKey: urlBase64ToUint8Array(publicKey),
    });
  }
  await window.apiFetch('/api/push/subscribe', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      subscription: sub.toJSON(),
      room: state.relayRoom,
      code: myCode(),
      name: state.name,
    }),
  });
  state.pushSubscribed = true;
  store.set('p2p_push', '1');
  const el = $('pushStatus');
  if (el) { el.textContent = '已訂閱背景推播'; el.classList.add('ok'); }
  return true;
}

async function pollRtcQuality() {
  const rows = [];
  for (const conn of rtc.conns) {
    const pc = conn.pc;
    if (!pc) continue;
    let rtt = null;
    try {
      const stats = await pc.getStats();
      stats.forEach(report => {
        if (report.type === 'candidate-pair' && report.state === 'succeeded' && report.currentRoundTripTime != null) {
          rtt = Math.round(report.currentRoundTripTime * 1000);
        }
      });
    } catch {}
    rtcQuality[conn.id] = {
      name: conn.name,
      pc: pc.connectionState,
      ice: pc.iceConnectionState,
      dc: conn.dc ? conn.dc.readyState : '—',
      rtt,
    };
    rows.push(rtcQuality[conn.id]);
  }
  return rows;
}

function renderQualityPanel() {
  const box = $('qualityBody');
  if (!box) return;
  const relay = ws && ws.readyState === WebSocket.OPEN;
  const paths = typeof getActivePaths === 'function' ? getActivePaths() : [];
  const ms = state.health.relayMs;
  const meshNote = `Mesh TTL=${state.meshTtl}：語音片段與文字可經中繼隊友轉發延伸距離（每跳 -1 TTL）`;
  let html = `<div class="quality-grid">
    <div><b>中繼</b><br>${relay ? '已連線' : '斷線'} · ${ms != null ? ms + 'ms' : '—'}</div>
    <div><b>路徑</b><br>${paths.length ? paths.join(' + ') : '離線'}</div>
    <div><b>PTT 模式</b><br>${window.pttVoiceMode || '—'}</div>
    <div><b>語音延伸</b><br>${meshNote}</div>
  </div>`;
  const rtcRows = Object.values(rtcQuality);
  if (rtcRows.length) {
    html += '<div class="card-title" style="margin-top:10px">WebRTC 夥伴</div>';
    rtcRows.forEach(r => {
      html += `<div class="peer-item small">${escapeHtml(r.name)} · PC:${r.pc} ICE:${r.ice} DC:${r.dc}${r.rtt != null ? ' · ' + r.rtt + 'ms' : ''}</div>`;
    });
  } else {
    html += '<p class="muted small">尚無 WebRTC 連線。離線可掃 QR；有網路可輸入6碼。</p>';
  }
  html += `<p class="muted small">💡 延伸通話：全員連同一中繼房間，中間隊友在線即轉發語音片段（VOICE_STREAM）與文字，最遠約 TTL 跳數。WebRTC 即時語音僅限一對一；中繼斷時自動改 WebRTC。</p>`;
  box.innerHTML = html;
}

function openQualityModal() {
  const m = $('qualityModal');
  if (!m) return;
  renderQualityPanel();
  pollRtcQuality().then(() => renderQualityPanel());
  m.classList.remove('hidden');
}

function bindTeamUI() {
  const hb = $('healthBadge');
  if (hb) hb.onclick = openQualityModal;
  const qc = $('qualityClose');
  if (qc) qc.onclick = () => $('qualityModal').classList.add('hidden');

  const ps = $('pushSubscribeBtn');
  if (ps) {
    ps.onclick = subscribeWebPush;
    if (state.pushSubscribed) {
      const el = $('pushStatus');
      if (el) { el.textContent = '已訂閱背景推播'; el.classList.add('ok'); }
    }
  }

  lastWalkSpeed = parseFloat(store.get('p2p_walk_speed', '1.2')) || 1.2;
  state.rallyNavTarget = store.getJSON('p2p_rally_nav', null);
  state.pushSubscribed = store.get('p2p_push', '0') === '1';

  if (qualityTimer) clearInterval(qualityTimer);
  qualityTimer = setInterval(() => {
    if ($('qualityModal') && !$('qualityModal').classList.contains('hidden')) {
      pollRtcQuality().then(() => renderQualityPanel());
    }
  }, 3000);
}

function initTeamFeatures() {
  bindTeamUI();
  bindTeamOfflineUI();
  if (state.pushSubscribed && Notification.permission === 'granted') {
    subscribeWebPush().catch(() => {});
  }
}

window.ROLE_CHANNEL_KEYS = [];
window.channelForConv = channelForConv;
window.convKeyForMsg = convKeyForMsg;
window.chatKeysForHome = chatKeysForHome;
window.convTitleTeam = convTitleTeam;
window.convAvatarTeam = convAvatarTeam;
window.recordWalkSpeed = recordWalkSpeed;
window.rallyEtaMinutes = rallyEtaMinutes;
window.formatEta = formatEta;
window.navigateToRally = navigateToRally;
window.initTeamFeatures = initTeamFeatures;
window.subscribeWebPush = subscribeWebPush;

/* ---- 離線多人：隊伍 QR（TEAM_INVITE）---- */

function teamSeedPeers() {
  const seeds = [{ code: myCode(), name: state.name, sid: state.id }];
  rtc.conns.forEach((c) => {
    if (!c.sid || c.sid === state.id) return;
    const code = codeOf(c.sid);
    if (!seeds.some((s) => s.code === code)) {
      seeds.push({ code, name: c.name || '夥伴', sid: c.sid });
    }
  });
  return seeds.slice(0, 4);
}

function buildTeamInvitePayload() {
  if (!state.teamId) state.teamId = uuid();
  store.set('p2p_team_id', state.teamId);
  return {
    kind: 'TEAM_INVITE',
    teamId: state.teamId,
    leader: myCode(),
    leaderName: state.name,
    seeds: teamSeedPeers(),
    ts: Date.now(),
  };
}

async function makeTeamInviteQr() {
  const payload = buildTeamInvitePayload();
  const raw = JSON.stringify(payload);
  let encoded = raw;
  if (typeof encodeSignal === 'function') {
    try { encoded = await encodeSignal({ t: 'TEAM', d: raw }); } catch {}
  }
  if (typeof showQR === 'function') {
    showQR('隊伍 QR', encoded);
  } else if (typeof showQrLoading === 'function') {
    showQrLoading('隊伍 QR', encoded);
  }
  const ta = $('qrCodeText');
  if (ta) ta.value = encoded;
  setDialStatus('隊伍 QR 已產生（新成員掃一次）', 'ok');
  forwardTeamInvite(payload);
}

function forwardTeamInvite(payload) {
  if (ws && ws.readyState === WebSocket.OPEN) {
    ws.send(JSON.stringify(payload));
  }
  rtc.conns.forEach((c) => {
    if (c.dc && c.dc.readyState === 'open') c.dc.send(JSON.stringify(payload));
  });
}

async function joinTeamFromInvite(raw) {
  let data = raw;
  if (typeof decodeSignal === 'function') {
    try {
      const dec = await decodeSignal(raw);
      if (dec && dec.t === 'TEAM') data = dec.d;
    } catch {}
  }
  if (typeof data === 'string') {
    try { data = JSON.parse(data); } catch { alert('無效的隊伍 QR'); return; }
  }
  if (!data || data.kind !== 'TEAM_INVITE') { alert('不是隊伍 QR'); return; }
  state.teamId = data.teamId;
  state.teamPeers = data.seeds || [];
  store.set('p2p_team_id', state.teamId);
  store.setJSON('p2p_team_peers', state.teamPeers);
  setDialStatus('加入隊伍 ' + (data.teamId || '').slice(0, 8) + '…', 'busy');
  const seeds = (data.seeds || []).filter((s) => s.code && s.code !== myCode());
  let linked = 0;
  for (const seed of seeds.slice(0, 3)) {
    if (rtc.conns.some((c) => c.sid === seed.sid || codeOf(c.sid || '') === seed.code)) {
      linked++;
      continue;
    }
    if (ws && ws.readyState === WebSocket.OPEN && typeof dial === 'function') {
      await dial(seed.code);
      linked++;
    } else if (typeof makeOfferToCode === 'function') {
      await makeOfferToCode(seed.code, seed.name);
    }
  }
  if (!ws || ws.readyState !== WebSocket.OPEN) {
    const codes = seeds.map((s) => s.code).join('、');
    setDialStatus('離線隊伍：請與種子 ' + codes + ' 互掃 QR 連線', 'ok');
  } else {
    setDialStatus('已加入隊伍，已嘗試連線 ' + linked + ' 個種子', 'ok');
  }
  updateTeamBanner();
}

function updateTeamBanner() {
  const el = $('teamStatusBanner');
  if (!el) return;
  if (!state.teamId) { el.classList.add('hidden'); return; }
  const n = (state.teamPeers || []).length;
  const linked = rtc.conns.filter((c) => c.dc && c.dc.readyState === 'open').length;
  el.classList.remove('hidden');
  el.textContent = `隊伍 ${state.teamId.slice(0, 8)} · 種子 ${n} · 已連 ${linked}（PTT 半雙工）`;
}

function onTeamInvite(msg) {
  if (msg.teamId) {
    state.teamId = msg.teamId;
    state.teamPeers = msg.seeds || [];
    store.set('p2p_team_id', state.teamId);
    store.setJSON('p2p_team_peers', state.teamPeers);
    updateTeamBanner();
  }
}

function onTeamJoin(msg) {
  if (!msg.from || msg.from === myCode()) return;
  setDialStatus('新隊員 ' + (msg.name || msg.from) + ' 加入', 'ok');
  if (ws && ws.readyState === WebSocket.OPEN && typeof dial === 'function') dial(msg.from);
}

function bindTeamOfflineUI() {
  const mk = $('makeTeamQrBtn');
  if (mk) mk.onclick = () => makeTeamInviteQr();
  const scan = $('scanTeamQrBtn');
  if (scan) scan.onclick = () => {
    if (typeof openScannerAuto === 'function') openScannerAuto('team');
    else alert('請用「掃別人 QR」掃隊伍 QR');
  };
  state.teamId = store.get('p2p_team_id', '') || state.teamId;
  state.teamPeers = store.getJSON('p2p_team_peers', state.teamPeers || []);
  updateTeamBanner();
}

function teamMeshVoiceIntervalMs() {
  // 非 WebRTC 時以較短片段逼近即時（Web↔APK VOICE_STREAM）
  return state.teamId ? 200 : 350;
}

window.buildTeamInvitePayload = buildTeamInvitePayload;
window.joinTeamFromInvite = joinTeamFromInvite;
window.onTeamInvite = onTeamInvite;
window.onTeamJoin = onTeamJoin;
window.bindTeamOfflineUI = bindTeamOfflineUI;
window.teamMeshVoiceIntervalMs = teamMeshVoiceIntervalMs;
window.updateTeamBanner = updateTeamBanner;

window.bindTeamOfflineUI = bindTeamOfflineUI;
window.teamMeshVoiceIntervalMs = teamMeshVoiceIntervalMs;
window.updateTeamBanner = updateTeamBanner;
