// NetLogger — トラフィック消費量の実測ユーティリティ。?debug=net で有効化。
// WebRTC inbound/outbound bytes（subscribed/published trackの getRTCStatsReport）と
// HTTP リソースバイト（PerformanceObserver）を 5 秒毎に累積、画面右上に表示 + /api/debug/net-log に POST。

function useNetLogger({ enabled, pageLabel, roomRef, cfgUser }) {
  const [stats, setStats] = React.useState({
    webrtcIn: 0,
    webrtcOut: 0,
    httpBytes: 0,
    sampleSec: 0,
    subscribedTracks: 0,
    publishedTracks: 0,
    visibility: typeof document !== 'undefined' ? document.visibilityState : 'unknown',
    intervalIn: 0,
    intervalOut: 0,
    intervalHttp: 0,
    activeIdentities: [],
  });
  const cumRef = React.useRef({ webrtcIn: 0, webrtcOut: 0, httpBytes: 0, startTs: Date.now() });
  // 直近サンプルの track-sid -> bytes、差分で算出
  const lastBytesRef = React.useRef({ in: {}, out: {} });
  const httpEntriesRef = React.useRef(0);

  React.useEffect(() => {
    if (!enabled) return;

    const sample = async () => {
      const room = roomRef.current;
      let intervalIn = 0;
      let intervalOut = 0;
      let subscribedTracks = 0;
      let publishedTracks = 0;
      const activeIdentities = [];

      if (room) {
        // INBOUND: 各 remote の subscribed audio track
        for (const rp of room.remoteParticipants.values()) {
          for (const pub of rp.trackPublications.values()) {
            if (!pub.isSubscribed || !pub.track) continue;
            subscribedTracks++;
            try {
              const report = await pub.track.getRTCStatsReport?.();
              if (!report) continue;
              report.forEach((s) => {
                if (s.type === 'inbound-rtp') {
                  const sid = pub.trackSid;
                  const prev = lastBytesRef.current.in[sid] || 0;
                  const cur = s.bytesReceived || 0;
                  if (cur > prev) intervalIn += (cur - prev);
                  lastBytesRef.current.in[sid] = cur;
                }
              });
            } catch (e) {}
          }
          if (rp.isSpeaking) activeIdentities.push(rp.identity);
        }
        // OUTBOUND: localParticipant の publish 中 track
        for (const pub of (room.localParticipant?.trackPublications?.values() || [])) {
          if (!pub.track) continue;
          publishedTracks++;
          try {
            const report = await pub.track.getRTCStatsReport?.();
            if (!report) continue;
            report.forEach((s) => {
              if (s.type === 'outbound-rtp') {
                const sid = pub.trackSid;
                const prev = lastBytesRef.current.out[sid] || 0;
                const cur = s.bytesSent || 0;
                if (cur > prev) intervalOut += (cur - prev);
                lastBytesRef.current.out[sid] = cur;
              }
            });
          } catch (e) {}
        }
      }

      // HTTP リソースバイト（Performance API、増分のみ）
      let intervalHttp = 0;
      try {
        const entries = performance.getEntriesByType('resource');
        for (let i = httpEntriesRef.current; i < entries.length; i++) {
          intervalHttp += entries[i].transferSize || 0;
        }
        httpEntriesRef.current = entries.length;
      } catch (e) {}

      cumRef.current.webrtcIn += intervalIn;
      cumRef.current.webrtcOut += intervalOut;
      cumRef.current.httpBytes += intervalHttp;
      const sampleSec = Math.floor((Date.now() - cumRef.current.startTs) / 1000);

      const next = {
        webrtcIn: cumRef.current.webrtcIn,
        webrtcOut: cumRef.current.webrtcOut,
        httpBytes: cumRef.current.httpBytes,
        sampleSec,
        subscribedTracks,
        publishedTracks,
        visibility: document.visibilityState,
        intervalIn,
        intervalOut,
        intervalHttp,
        activeIdentities,
      };
      setStats(next);

      // サーバに永続化（sendBeacon: tab close 時も発射、ブロッキングしない）
      try {
        const blob = new Blob([JSON.stringify({
          ts: Date.now(),
          page: pageLabel,
          identity: cfgUser,
          ...next,
        })], { type: 'application/json' });
        navigator.sendBeacon('/api/debug/net-log', blob);
      } catch (e) {}
    };

    const id = setInterval(sample, 5000);
    sample();
    return () => clearInterval(id);
  }, [enabled, pageLabel, cfgUser, roomRef]);

  return stats;
}

function fmtBytes(n) {
  if (n < 1024) return `${n} B`;
  if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`;
  if (n < 1024 * 1024 * 1024) return `${(n / 1024 / 1024).toFixed(2)} MB`;
  return `${(n / 1024 / 1024 / 1024).toFixed(3)} GB`;
}

function NetLoggerHUD({ stats, visible }) {
  if (!visible) return null;
  const totalBytes = stats.webrtcIn + stats.webrtcOut + stats.httpBytes;
  const sec = Math.max(1, stats.sampleSec);
  const avgBps = totalBytes / sec; // bytes/sec
  return (
    <div style={{
      position: 'fixed', top: 8, right: 8, zIndex: 99999,
      background: 'rgba(0,0,0,0.85)', color: '#0f0',
      fontFamily: 'JetBrains Mono, monospace', fontSize: 11,
      padding: '8px 10px', borderRadius: 6,
      lineHeight: 1.5, minWidth: 220,
      border: '1px solid #0f0',
    }}>
      <div style={{ color: '#fff', fontWeight: 700, marginBottom: 4 }}>NET LOGGER</div>
      <div>sec: {sec}s vis: {stats.visibility}</div>
      <div>↓ rx: {fmtBytes(stats.webrtcIn)} (5s:{fmtBytes(stats.intervalIn)})</div>
      <div>↑ tx: {fmtBytes(stats.webrtcOut)} (5s:{fmtBytes(stats.intervalOut)})</div>
      <div>http: {fmtBytes(stats.httpBytes)} (5s:{fmtBytes(stats.intervalHttp)})</div>
      <div style={{ color: '#fff', borderTop: '1px solid #0f0', marginTop: 4, paddingTop: 4 }}>
        TOTAL: {fmtBytes(totalBytes)}<br/>
        avg: {fmtBytes(avgBps)}/s
      </div>
      <div>sub:{stats.subscribedTracks} pub:{stats.publishedTracks}</div>
      {stats.activeIdentities.length > 0 && (
        <div>speak: {stats.activeIdentities.join(',')}</div>
      )}
    </div>
  );
}

window.useNetLogger = useNetLogger;
window.NetLoggerHUD = NetLoggerHUD;
