'use strict';
// ============================================================================
//  直播延迟捕获器 - 后台 Service Worker (v0.3.4)
//
//  架构变更（v0.3）：
//   - content script 只负责"发现"URL（fetch hook / XHR hook）
//   - **本文件负责实际的媒体流抓取和转发**
//   - 利用扩展的 host_permissions (<all_urls>) 直接跨域 fetch，绕过页面 CORS
//   - 抓到的字节经 WebSocket 推给本地服务端 ws://127.0.0.1:8091/ws/capture
// ============================================================================

// ★ 全局错误捕获 — 防止未捕获异常导致 Service Worker "无效"
self.addEventListener('error', (e) => {
  console.error('[BG] 全局 error:', e.message, 'at', e.filename, ':', e.lineno);
  e.preventDefault();
});
self.addEventListener('unhandledrejection', (e) => {
  console.error('[BG] 未捕获 Promise 拒绝:', e.reason);
  e.preventDefault();
});

const WS_URL = 'ws://127.0.0.1:8091/ws/capture';

let ws = null;
let wsReady = false;
let reconnectTimer = null;
const ports = new Set();
const discovered = {};

/** 当前活跃的捕获任务 { captureId: { abortCtrl, url, tabId } } */
const activeCaptures = {};

/** 断线恢复用：记录当前正在捕获的 URL 和 tabId */
let resumeCaptureUrl = null;
let resumeTabId = null;
let resumeQuality = null;

// ---------------------------------------------------------------------------
// WebSocket 连接本地服务端
// ---------------------------------------------------------------------------
function connectWS() {
  try {
    ws = new WebSocket(WS_URL);
  } catch (e) {
    console.warn('[BG] WebSocket 创建失败:', e.message);
    scheduleReconnect();
    return;
  }
  ws.onopen = () => {
    wsReady = true;
    console.log('[BG] WS connected');
    notifyPopup();
    // ★ 断线恢复：若之前有活跃捕获，重新发送 init 恢复会话
    if (resumeCaptureUrl) {
      console.log('[BG] 重连后恢复捕获:', resumeCaptureUrl.slice(0, 60));
      wsSend(JSON.stringify({
        type: 'init',
        captureId: 'cap_' + urlHash(resumeCaptureUrl),
        format: detectFormat(resumeCaptureUrl),
        title: (discovered[resumeTabId] && discovered[resumeTabId].title) || '',
        url: resumeCaptureUrl,
        quality: resumeQuality || 'orig'
      }), false);
    }
  };
  ws.onclose = () => {
    wsReady = false;
    scheduleReconnect();
  };
  // ★ 不在 onerror 里主动关闭，让浏览器自然重连
  ws.onerror = (e) => {
    const errMsg = e && e.message ? e.message : '(no message)';
    console.warn('[BG] WS error:', errMsg);
  };
  ws.onmessage = (ev) => {
    try {
      const msg = JSON.parse(ev.data);
      if (msg.type === 'ready' || msg.type === 'error') {
        chrome.runtime.sendMessage({ type: 'svc', payload: msg }).catch(() => {});
        notifyPopup();
      }
    } catch (e) {}
  };
}

function scheduleReconnect() {
  if (reconnectTimer) return;
  reconnectTimer = setTimeout(() => { reconnectTimer = null; connectWS(); }, 2000);
}

function wsSend(text, binary, binaryData) {
  if (!wsReady || !ws) return false;
  try {
    if (binary) ws.send(binaryData);
    else ws.send(text);
    return true;
  } catch (e) { return false; }
}

// ---------------------------------------------------------------------------
// content script 长连接（只接收 found 消息）
// ---------------------------------------------------------------------------
try {
  chrome.runtime.onConnect.addListener((port) => {
    if (port.name !== 'capture-content') return;
    ports.add(port);
    port.onMessage.addListener((msg) => handleContent(port, msg));
    port.onDisconnect.addListener(() => ports.delete(port));
  });
} catch (e) {
  console.error('[BG] onConnect 注册失败:', e.message);
}

function handleContent(port, msg) {
  if (msg.type === 'found') {
    if (!msg.url || isOurOwnUrl(msg.url)) return;
    const existing = discovered[msg.tabId];
    if (existing) {
      const existingCore = stripUrlHash(existing.url);
      const newCore = stripUrlHash(msg.url);
      if (existingCore === newCore && existing.format === 'flv') return;
    }
    discovered[msg.tabId] = { url: msg.url, format: msg.format, title: msg.title };
    notifyPopup();
  }
  // init/chunk/stop 不再由 content 发出——全部由 background 自己做
}

function notifyPopup() {
  chrome.runtime.sendMessage({
    type: 'discovered',
    payload: { discovered, wsReady }
  }).catch(() => {});
}

// ---------------------------------------------------------------------------
// webRequest 发现（主要手段）
// ---------------------------------------------------------------------------
const MEDIA_RE = /\.(flv|m3u8|mpd)(\?|$)/i;
function isOurOwnUrl(url) { return /localhost:8090|\/hls\/cap_/i.test(url); }
function stripUrlHash(url) {
  try {
    const u = new URL(url);
    // 忽略签名、时间戳、abr 等会变化的参数，仅保留协议+host+pathname+核心标识
    const keep = ['roomid', 'vendor', 'uid', 'userId', 'live', 'cid'];
    const kept = new URLSearchParams();
    for (const k of keep) {
      if (u.searchParams.has(k)) {
        const v = u.searchParams.get(k);
        if (v) kept.append(k, v);
      }
    }
    return u.origin + u.pathname + (kept.toString() ? '?' + kept.toString() : '');
  } catch (e) { return url; }
}

// ★ 用 try-catch 包裹 webRequest 监听器注册，防止 API 不可用时导致 SW 失效
try {
  if (chrome.webRequest && chrome.webRequest.onResponseStarted) {
    chrome.webRequest.onResponseStarted.addListener((details) => {
      if (!MEDIA_RE.test(details.url)) return;
      if (isOurOwnUrl(details.url)) return;
      if (details.type !== 'media' && details.type !== 'xmlhttprequest' && details.type !== 'other') return;
      // tabId 可能为 -1（非标签页请求），需要保护
      if (!details.tabId || details.tabId < 0) return;
      chrome.tabs.get(details.tabId, (tab) => {
        if (chrome.runtime.lastError) return; // 标签页可能已关闭
        const title = (tab && tab.title) ? tab.title : '';
        const fmt = /\.flv/.test(details.url) ? 'flv' : (/\.mpd/.test(details.url) ? 'dash' : 'hls');
        const existing = discovered[details.tabId];
        if (existing) {
          const existingCore = stripUrlHash(existing.url);
          const newCore = stripUrlHash(details.url);
          if (existingCore === newCore && existing.format === 'flv') return;
        }
        discovered[details.tabId] = {
          url: details.url,
          format: fmt,
          title
        };
        notifyPopup();
      });
    }, { urls: ['<all_urls>'] }, ['responseHeaders']);
    console.log('[BG] webRequest 监听器已注册');
  } else {
    console.warn('[BG] webRequest API 不可用');
  }
} catch (e) {
  console.error('[BG] webRequest 注册失败:', e.message);
}

// ---------------------------------------------------------------------------
// ★ 核心：由 background 直接执行媒体流抓取
// ---------------------------------------------------------------------------
function detectFormat(url) {
  if (/\.flv/i.test(url)) return 'flv';
  if (/\.mpd/i.test(url)) return 'dash';
  return 'hls';
}

function urlHash(s) {
  let h = 0;
  for (let i = 0; i < s.length; i++) h = (h * 31 + s.charCodeAt(i)) | 0;
  return Math.abs(h).toString(36);
}

/**
 * 带超时的 fetch 包装器
 */
function fetchWithTimeout(url, options, timeoutMs = 12000) {
  const controller = new AbortController();
  const timer = setTimeout(() => controller.abort(), timeoutMs);
  // 合并 signal（用户可能已传自己的）
  const origSignal = options.signal;
  if (origSignal) {
    try {
      origSignal.addEventListener('abort', () => controller.abort());
    } catch (e) {}
  }
  options.signal = controller.signal;
  return fetch(url, options).then(resp => {
    clearTimeout(timer);
    return resp;
  }).catch(err => {
    clearTimeout(timer);
    if (err.name === 'AbortError') throw new Error('请求超时 (' + timeoutMs / 1000 + 's)，CDN 未响应');
    throw err;
  });
}

/**
 * 启动捕获：background 用自己的权限直接 fetch 媒体 URL，泵送到 WS
 */
async function startCapture(url, tabId, quality) {
  const captureId = 'cap_' + urlHash(url);
  const format = detectFormat(url);

  if (activeCaptures[captureId]) {
    stopCapture(captureId);
  }

  console.log('[BG] 开始抓取:', format, url.slice(0, 100));

  const abortCtrl = new AbortController();

  // 注册为活跃捕获（用于外部停止 / 断线恢复）
  activeCaptures[captureId] = { abortCtrl, url, tabId };
  resumeCaptureUrl = url;
  resumeTabId = tabId;
  resumeQuality = quality || 'orig';

  // ★ HLS 格式：服务端直接从 URL 拉流，扩展不推数据
  //   HLS 的 .ts 分片通常在公开 CDN 上（签名在 URL 参数），不需要 cookie
  //   ffmpeg 自带 HLS 解析器，直接 -i "url.m3u8" 即可
  if (format === 'hls') {
    sendStatus(tabId, '📡 HLS 源 — 服务端直接拉流…');
    wsSend(JSON.stringify({
      type: 'init',
      captureId,
      format: 'hls',
      title: (discovered[tabId] && discovered[tabId].title) || '',
      url,
      quality: quality || 'orig'
    }), false);

    // HLS 模式：保持 WS 心跳，等待服务端反馈
    const heartbeat = setInterval(() => {
      if (!activeCaptures[captureId]) { clearInterval(heartbeat); return; }
      wsSend(JSON.stringify({ type: 'meta', captureId, ts: Date.now() }), false);
    }, 5000);

    // 等待停止
    return;
  }

  // ★ FLV 格式：扩展 fetch → WS 管道推流（需要登录态 cookie）
  let refererOrigin;
  try { refererOrigin = new URL(url).origin; } catch(e) { refererOrigin = ''; }

  let firstSeg = true;
  let totalBytes = 0;

  // ★ 持续重连循环：小红书等直播 CDN 的长连接常在 1~2 分钟后断开，
  //   必须自动重连；重连时发 seg_start 让服务端跳过新 FLV header，
  //   从而无缝续接同一个 ffmpeg 进程，避免播放卡顿。
  while (activeCaptures[captureId]) {
    try {
      // 首次建 ffmpeg；重连时服务端会复用/重建（由 init 逻辑判断）
      wsSend(JSON.stringify({
        type: 'init',
        captureId,
        format: 'flv',
        title: (discovered[tabId] && discovered[tabId].title) || '',
        url,
        quality: quality || 'orig'
      }), false);

      // 重连片段：通知服务端跳过新 FLV header（首个片段保留 header）
      if (!firstSeg) {
        wsSend(JSON.stringify({ type: 'seg_start', captureId }), false);
        sendStatus(tabId, '🔄 已重连，继续接收…');
      } else {
        sendStatus(tabId, '⏳ 正在连接 CDN…');
      }

      const resp = await fetchWithTimeout(url, {
        method: 'GET',
        credentials: 'include',
        cache: 'no-store',
        headers: {
          'Accept': '*/*',
          'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
          'Referer': refererOrigin || 'https://www.xiaohongshu.com/',
          'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
          'Sec-Fetch-Dest': 'video',
          'Sec-Fetch-Mode': 'no-cors',
          'Sec-Fetch-Site': 'same-site',
        },
        signal: abortCtrl.signal,
      }, 15000);

      console.log('[BG] fetch 完成, status:', resp.status);
      if (!resp.body) throw new Error('响应无 body（可能被浏览器拦截）');
      sendStatus(tabId, '📡 HTTP ' + resp.status + ' — 接收中…');

      const reader = resp.body.getReader();
      while (activeCaptures[captureId]) {
        const { done, value } = await reader.read();
        if (done) { sendStatus(tabId, '⏹ 分段结束，准备重连…'); break; }
        if (value && value.byteLength > 0) {
          totalBytes += value.byteLength;
          if (firstSeg && totalBytes === value.byteLength) {
            const head = Array.from(value.slice(0, 32));
            console.log('[BG] 首批字节 hex:', head.map(b => b.toString(16).padStart(2, '0')).join(' '));
            console.log('[BG] 首批 ASCII:', String.fromCharCode(...head.filter(b => b >= 32 && b < 127)));
          }
          // ★ 必须发 value（精确 Uint8Array 视图），不能发 value.buffer
          wsSend(null, true, value);
          if (totalBytes % 500000 < value.byteLength) {
            sendStatus(tabId, '✅ 已推送 ' + Math.round(totalBytes / 1024) + ' KB…');
          }
        }
      }

      firstSeg = false;
      // 正常分段结束 → 短暂停顿后重连续接
      if (activeCaptures[captureId]) {
        sendStatus(tabId, '↻ 1 秒后重连…');
        await new Promise(r => setTimeout(r, 1000));
      }
    } catch (e) {
      if (!activeCaptures[captureId]) break;
      const msg = e.message || String(e);
      console.warn('[BG] 抓取/泵送异常:', msg);
      sendStatus(tabId, '⚠️ 中断，2 秒后重连: ' + msg.slice(0, 40));
      await new Promise(r => setTimeout(r, 2000));
    }
  }

  // 清理
  delete activeCaptures[captureId];
  if (resumeCaptureUrl === url) { resumeCaptureUrl = null; resumeTabId = null; resumeQuality = null; }
  wsSend(JSON.stringify({ type: 'stop', captureId }), false);
  sendStatus(tabId, '⏹ 已停止 (' + Math.round(totalBytes / 1024) + ' KB)');
}

/**
 * 停止指定捕获
 */
function stopCapture(captureId) {
  const cap = activeCaptures[captureId];
  if (cap && cap.abortCtrl) {
    try { cap.abortCtrl.abort(); } catch (e) {}
  }
  delete activeCaptures[captureId];
  if (cap && resumeCaptureUrl === cap.url) { resumeCaptureUrl = null; resumeTabId = null; }
  wsSend(JSON.stringify({ type: 'stop', captureId }), false);
}

/**
 * 停止某个 tab 下所有捕获
 */
function stopAllForTab(tabId) {
  for (const cid of Object.keys(activeCaptures)) {
    if (activeCaptures[cid].tabId === tabId) {
      stopCapture(cid);
    }
  }
}

/**
 * 向 popup 发送状态更新
 */
function sendStatus(tabId, statusText) {
  chrome.runtime.sendMessage({
    type: 'capture_status',
    tabId,
    status: statusText
  }).catch(() => {});
}

// ---------------------------------------------------------------------------
// popup / content 指令处理
// ---------------------------------------------------------------------------
try {
  chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
    if (msg.type === 'getTabId') {
      sendResponse({ tabId: sender.tab ? sender.tab.id : null });
      return true;
    }
    if (msg.type === 'getDiscovered') {
      sendResponse({ discovered, wsReady });
      return true;
    }

    // ★ enable：popup 点了"开始捕获" → background 自己来拉流
    if (msg.type === 'enable') {
      const url = msg.url || (discovered[msg.tabId] && discovered[msg.tabId].url);
      if (!url) {
        sendResponse({ ok: false, error: '没有可捕获的 URL' });
        return true;
      }
      // 异步执行，不阻塞 sendResponse
      startCapture(url, msg.tabId, msg.quality || 'orig').then(() => {}).catch((err) => {
        console.error('[BG] startCapture error:', err);
        sendStatus(msg.tabId, '❌ 错误: ' + (err.message || err));
      });
      sendResponse({ ok: true });
      return true;
    }

    if (msg.type === 'disable') {
      stopAllForTab(msg.tabId);
      sendResponse({ ok: true });
      return true;
    }

    if (msg.type === 'openPlayer') {
      chrome.tabs.create({ url: 'http://localhost:8090/' });
      sendResponse({ ok: true });
      return true;
    }

    return false;
  });
} catch (e) {
  console.error('[BG] onMessage 注册失败:', e.message);
}

// ★ MV3 最佳实践：注册安装/更新事件
try {
  chrome.runtime.onInstalled.addListener((details) => {
    console.log('[BG] 扩展 installed/updated, reason:', details.reason);
  });
} catch (e) {
  console.warn('[BG] onInstalled 不可用:', e.message);
}

console.log('[BG] 直播延迟捕获器 v0.3.4 加载成功');

// 启动 WS 连接（放在最后，确保所有监听器都已注册）
try {
  connectWS();
} catch (e) {
  console.error('[BG] 初始 WS 连接失败:', e.message);
}
