// record.js — SW-side control for browser recording (nbrowser_record_*).
//
// 🎥 DOCTRINE — why it's built this way (full detail: the native-browser-recording skill + README "Recording"):
//   GOAL: demos must look AMAZING → full-res, HIGH FRAMERATE (20–60fps). The enemy is PAINT-THROTTLING, not the
//   codec: Chrome correctly stops painting occluded/background/static windows (battery) → capture freezes/drops fps.
//   (CDP screencast is NOT garbage — it's pup's browser_record_start engine, 20–30fps when the page paints.)
//   The NATIVE-browser methods here are 'wgc' + 'getdisplaymedia' (real high-fps engines). Background capture of
//   the user's REAL Chrome (the only browser with their logins) needs it to PAINT while occluded — which means
//   launching it with the occlusion/throttle-disable flags (--disable-features=CalculateNativeWinOcclusion
//   --disable-backgrounding-occluded-windows --disable-renderer-backgrounding --disable-background-timer-throttling).
//   Default Chrome lacks them (correctly — battery). So:
//     • No login needed  → record via PUP/Hydrogen (see the demo-recording skill); don't touch the user's Chrome.
//     • Login needed      → native Chrome: ASK+WARN, FULLY kill the whole chrome.exe process tree, relaunch with the
//       user's profile + the flags, record full-fps in the BACKGROUND, then OFFER TO REVERT later (battery saver).
//   ⛔ NEVER foreground the user's real Chrome to fix framerate (use the flags); NEVER leave the flags on forever.
//
// TWO methods (caller picks via args.method; README has the full pros/cons):
//   • 'getdisplaymedia' (desktopCapture) — CROSS-PLATFORM (Mac/Ubuntu/Windows). High-fps (the WebRTC
//     pipeline Google Meet uses). Chrome REQUIRES a one-time "choose what to share" picker per
//     recording (no silent variant in any browser). Records the chosen surface in the background.
//   • 'wgc' — WINDOWS-only, handled BRIDGE-SIDE via HD's Windows-Graphics-Capture recorder: no picker,
//     fully background, no wake-lock, 60fps mp4. The bridge intercepts method:'wgc' before the
//     extension; reaching here means HD/bridge WGC isn't wired on this machine.
//
// record_start is NON-BLOCKING: it shows the picker and returns immediately with a recordId in state
// 'awaiting_pick'. The picker can take as long as the user needs (the old blocking version timed out
// the relay request). Poll nbrowser_record_status until state=='recording', then drive, then stop.
// The MediaRecorder lives in the offscreen document, so it keeps recording even if the SW idles.

const OFFSCREEN_URL = 'src/offscreen-record.html';
const active = new Map(); // recordId -> { state, method, startedAt?, mimeType?, error? }
let seq = 0;

async function ensureOffscreen() {
  const has = chrome.offscreen.hasDocument ? await chrome.offscreen.hasDocument() : false;
  if (!has) await chrome.offscreen.createDocument({ url: OFFSCREEN_URL, reasons: ['USER_MEDIA'], justification: 'Record the browser to WebM for demos' });
}

export async function recordStart(tabId, args = {}) {
  const method = (args.method || 'getdisplaymedia').toLowerCase();
  if (method === 'wgc') {
    return { ok: false, error: 'method:wgc is handled by the bridge (HD WGC recorder), not the extension',
      _hint: 'the bridge intercepts method:"wgc"; reaching the extension means HD/bridge WGC is not wired here — use method:"getdisplaymedia"' };
  }
  // 'tabcapture' — record the CURRENT tab with NO picker. Needs activeTab, which is granted when the
  // user clicks the Adom toolbar icon (that gesture). This powers the friendly "Record this tab" button.
  if (method === 'tabcapture' || method === 'thistab') {
    let streamId;
    try {
      streamId = await new Promise((res, rej) => chrome.tabCapture.getMediaStreamId({ targetTabId: Number(tabId) }, (id) =>
        (chrome.runtime.lastError || !id) ? rej(new Error((chrome.runtime.lastError && chrome.runtime.lastError.message) || 'tabCapture failed')) : res(id)));
    } catch (e) {
      return { ok: false, error: String((e && e.message) || e), _hint: 'tabCapture needs activeTab — start it from the Adom toolbar button ("Record this tab"); that click grants it.' };
    }
    const recordId = 'r' + (++seq);
    await ensureOffscreen();
    const res = await chrome.runtime.sendMessage({ target: 'offscreen-record', action: 'start', streamId, source: 'tab', fps: Number(args.fps) || 30, audio: args.audio === true });
    if (res && res.ok) { active.set(recordId, { state: 'recording', method: 'tabcapture', startedAt: Date.now(), mimeType: res.mimeType }); return { ok: true, recordId, method: 'tabcapture', state: 'recording' }; }
    return { ok: false, error: (res && res.error) || 'recorder failed to start' };
  }

  const recordId = 'r' + (++seq);
  active.set(recordId, { state: 'awaiting_pick', method: 'getdisplaymedia' });
  const tab = tabId != null ? await chrome.tabs.get(Number(tabId)).catch(() => null) : null;

  // Picker callback — runs whenever the user picks (or cancels). Recording starts HERE, async.
  const onPick = async (streamId) => {
    if (chrome.runtime.lastError || !streamId) { active.set(recordId, { state: 'cancelled', method }); return; }
    try {
      await ensureOffscreen();
      const res = await chrome.runtime.sendMessage({ target: 'offscreen-record', action: 'start', streamId, source: 'desktop', fps: Number(args.fps) || 30, audio: args.audio === true });
      if (res && res.ok) active.set(recordId, { state: 'recording', method: 'getdisplaymedia', startedAt: Date.now(), mimeType: res.mimeType });
      else active.set(recordId, { state: 'error', method, error: (res && res.error) || 'recorder start failed' });
    } catch (e) { active.set(recordId, { state: 'error', method, error: String((e && e.message) || e) }); }
  };

  try {
    if (tab) chrome.desktopCapture.chooseDesktopMedia(['window', 'screen', 'tab'], tab, onPick);
    else chrome.desktopCapture.chooseDesktopMedia(['window', 'screen'], onPick);
  } catch (e) {
    active.set(recordId, { state: 'error', method, error: String((e && e.message) || e) });
    return { ok: false, error: String((e && e.message) || e) };
  }
  return { ok: true, recordId, method: 'getdisplaymedia', state: 'awaiting_pick',
    _hint: 'a share picker is up — once the user picks, state becomes "recording". Poll nbrowser_record_status {recordId}; then drive; then nbrowser_record_stop.' };
}

export async function recordStop(args = {}) {
  const recordId = args.recordId || [...active.keys()].reverse().find((id) => active.get(id).state === 'recording');
  const meta = recordId ? active.get(recordId) : null;
  if (!meta) return { ok: false, error: 'no recording found' };
  if (meta.state !== 'recording') return { ok: false, error: `recording is in state '${meta.state}', not recording`, state: meta.state };
  const res = await chrome.runtime.sendMessage({ target: 'offscreen-record', action: 'stop' });
  active.delete(recordId);
  if (![...active.values()].some((m) => m.state === 'recording') && chrome.offscreen.closeDocument) chrome.offscreen.closeDocument().catch(() => {});
  if (!res || !res.ok) return { ok: false, error: (res && res.error) || 'recorder failed to stop' };
  return { ok: true, recordId, method: meta.method, durationMs: Date.now() - meta.startedAt, mimeType: res.mimeType, sizeKB: res.sizeKB, base64: res.base64 };
}

export function recordStatus(args = {}) {
  const recordId = args.recordId || [...active.keys()].pop();
  const m = recordId ? active.get(recordId) : null;
  if (!m) return { ok: true, recordId: recordId || null, state: 'none' };
  return { ok: true, recordId, state: m.state, method: m.method, error: m.error, elapsedMs: m.startedAt ? Date.now() - m.startedAt : 0 };
}
export function recordList() {
  return { ok: true, count: active.size, recordings: [...active].map(([id, m]) => ({ recordId: id, state: m.state, method: m.method, elapsedMs: m.startedAt ? Date.now() - m.startedAt : 0 })) };
}