// Usage: node transcript.mjs <loom-url> <out-file>
// Tries multiple strategies to extract a Loom transcript:
//  1. __NEXT_DATA__ from the share page (contains transcript URL if available)
//  2. Network interception for vtt/json transcript requests during playback
//  3. Direct fetch of well-known transcript endpoints

import { chromium } from 'playwright';
import fs from 'node:fs';
import path from 'node:path';

const url = process.argv[2];
const outFile = path.resolve(process.argv[3] || './transcript.txt');

if (!url) { console.error('Usage: node transcript.mjs <loom-url> <out-file>'); process.exit(1); }

const idMatch = url.match(/loom\.com\/(?:share|embed)\/([a-f0-9]+)/i);
const videoId = idMatch?.[1];
if (!videoId) { console.error('Could not extract Loom video ID from URL'); process.exit(1); }

const browser = await chromium.launch({ headless: true });
const ctx = await browser.newContext({ viewport: { width: 1280, height: 800 } });
const page = await ctx.newPage();

const candidates = [];
page.on('response', async (res) => {
  const u = res.url();
  const ct = (res.headers()['content-type'] || '').toLowerCase();
  if (u.includes('transcript') || u.endsWith('.vtt') || u.includes('captions') || u.includes('/cc/')) {
    try {
      const body = await res.text();
      candidates.push({ url: u, ct, len: body.length, body });
      console.log(`  ✓ candidate: ${u}  (${body.length} bytes, ${ct})`);
    } catch {}
  }
});

console.log(`Opening share page for video ${videoId}...`);
await page.goto(`https://www.loom.com/share/${videoId}`, { waitUntil: 'domcontentloaded', timeout: 60000 });

// Try __NEXT_DATA__
const nextData = await page.evaluate(() => {
  const el = document.getElementById('__NEXT_DATA__');
  return el ? el.textContent : null;
});
let nextDataTranscriptUrl = null;
if (nextData) {
  try {
    const parsed = JSON.parse(nextData);
    const json = JSON.stringify(parsed);
    const m = json.match(/https?:[^"]*?(?:transcript|caption|\.vtt)[^"]*/gi);
    if (m && m.length) {
      console.log(`  __NEXT_DATA__ hints: ${m.slice(0, 3).join(' , ')}`);
      nextDataTranscriptUrl = m[0];
    }
  } catch {}
}

// Now load the embed and force playback to trigger any transcript fetch
console.log(`Opening embed and playing to trigger transcript request...`);
await page.goto(`https://www.loom.com/embed/${videoId}?autoplay=true&hideEmbedTopBar=true`, { waitUntil: 'domcontentloaded' });
await page.waitForSelector('video', { timeout: 60000 });
await page.evaluate(async () => {
  const v = document.querySelector('video'); v.muted = true; try { await v.play(); } catch {}
});
// Let it run a few seconds so player loads captions if they exist
await page.waitForTimeout(8000);

// Also try toggling closed-captions UI button if present
try {
  const ccBtn = page.locator('button[aria-label*="caption" i], button[aria-label*="subtitle" i], [data-test*="caption" i]').first();
  if (await ccBtn.count() > 0) {
    await ccBtn.click({ timeout: 3000 });
    await page.waitForTimeout(4000);
  }
} catch {}

// Direct GraphQL/REST guesses (most fail w/o auth but cheap to try)
const guesses = [
  `https://www.loom.com/api/v1/videos/${videoId}/transcripts`,
  `https://www.loom.com/api/v1/videos/${videoId}/captions`,
  `https://www.loom.com/api/campaigns/sessions/${videoId}/transcript-text`,
  `https://www.loom.com/api/campaigns/sessions/${videoId}/transcripts`,
];
for (const g of guesses) {
  try {
    const r = await page.request.get(g);
    if (r.ok()) {
      const body = await r.text();
      if (body && body.length > 50 && !body.startsWith('<!DOCTYPE')) {
        candidates.push({ url: g, ct: r.headers()['content-type'] || '', len: body.length, body });
        console.log(`  ✓ guess hit: ${g} (${body.length} bytes)`);
      }
    }
  } catch {}
}

await browser.close();

if (candidates.length === 0) {
  console.log('No transcript found via any strategy.');
  fs.writeFileSync(outFile, '[no transcript available]\n');
  process.exit(3);
}

// Pick best candidate (largest non-html)
candidates.sort((a, b) => b.len - a.len);
const best = candidates[0];
fs.writeFileSync(outFile, best.body);
console.log(`\nSaved: ${outFile}  (${best.len} bytes from ${best.url})`);

// If it's VTT, also write a stripped plain-text version
if (best.ct.includes('vtt') || best.body.startsWith('WEBVTT')) {
  const plain = best.body
    .split('\n')
    .filter((l) => l && !/^\d+$/.test(l) && !l.startsWith('WEBVTT') && !/^\d\d:\d\d/.test(l) && !l.startsWith('NOTE'))
    .join(' ')
    .replace(/\s+/g, ' ')
    .trim();
  const plainFile = outFile.replace(/\.(txt|vtt)$/i, '') + '.plain.txt';
  fs.writeFileSync(plainFile, plain);
  console.log(`Plain-text version: ${plainFile}`);
}
