/**
 * Barrido masivo de demanda en Gumroad Discover -> CSV.
 *
 * Misma medicion que discover-scan.js pero sobre una lista larga de terminos y
 * escribiendo a disco fila a fila, para poder cortarlo y reanudarlo. Los
 * terminos ya presentes en el CSV se saltan.
 *
 *   node tools/demand-scan-csv.js data/terms.txt data/gumroad-demand.csv
 *
 * Columnas: term, total_products, rank, product, price, ratings, sales, url
 */
const { chromium } = require('playwright');
const fs = require('fs');
const path = require('path');

const HEAD = 'term,total_products,rank,product,price,ratings,sales,url\n';
const q = (v) => '"' + String(v == null ? '' : v).replace(/"/g, '""') + '"';

async function scan(browser, term) {
  // Sin userAgent personalizado: con uno puesto Discover devuelve la pagina sin
  // enlaces de producto y la medicion sale a cero.
  const page = await browser.newPage({ locale: 'en-GB', viewport: { width: 1400, height: 1000 } });
  try {
    await page.goto('https://gumroad.com/discover?query=' + encodeURIComponent(term),
      { waitUntil: 'domcontentloaded', timeout: 45000 });
    await page.waitForTimeout(4500);
    await page.evaluate(() => window.scrollTo(0, 1400));
    await page.waitForTimeout(1200);
    const info = await page.evaluate(() => ({
      total: (document.body.innerText.match(/Showing\s+[\d-]+\s+of\s+([\d,]+)/) || [])[1] || '',
      links: Array.from(new Set(Array.from(document.querySelectorAll('a[href*="/l/"]'))
        .map((a) => a.href))).slice(0, 10),
    }));

    const rows = [];
    for (const link of info.links) {
      try {
        // La ficha .json se pide desde Node: cada vendedor vive en su propio
        // subdominio y el navegador lo bloquea por CORS.
        const res = await fetch(link.split('?')[0] + '.json',
          { headers: { 'User-Agent': 'Mozilla/5.0', Accept: 'application/json' } });
        if (!res.ok) continue;
        const r = await res.json();
        if (!r || !r.name) continue;
        rows.push({
          name: r.name, price: r.price_formatted || '',
          ratings: (r.ratings && r.ratings.count) || 0,
          sales: r.sales_count == null ? '' : r.sales_count,
          url: link.split('?')[0],
        });
      } catch (e) { /* una ficha ilegible no invalida el termino */ }
    }
    return { total: info.total, rows };
  } finally {
    await page.close();
  }
}

(async () => {
  const termsFile = process.argv[2];
  const outFile = process.argv[3];
  fs.mkdirSync(path.dirname(outFile), { recursive: true });
  if (!fs.existsSync(outFile)) fs.writeFileSync(outFile, HEAD);

  const done = new Set(fs.readFileSync(outFile, 'utf8').split('\n').slice(1)
    .map((l) => (l.match(/^"([^"]*)"/) || [])[1]).filter(Boolean));

  const terms = fs.readFileSync(termsFile, 'utf8').split('\n')
    .map((s) => s.trim()).filter((s) => s && !s.startsWith('#') && !done.has(s));

  console.log(`terminos pendientes: ${terms.length} (ya hechos: ${done.size})`);
  const browser = await chromium.launch({ headless: true });
  let i = 0;
  for (const term of terms) {
    i++;
    let r;
    try { r = await scan(browser, term); } catch (e) { console.log(`FALLO ${term}: ${e.message}`); continue; }
    // Un termino sin filas se escribe igual: "medido, sin resultados" es dato.
    const lines = r.rows.length
      ? r.rows.map((x, n) => [term, r.total, n + 1, x.name, x.price, x.ratings, x.sales, x.url].map(q).join(','))
      : [[term, r.total, '', '', '', '', '', ''].map(q).join(',')];
    fs.appendFileSync(outFile, lines.join('\n') + '\n');
    const tot = r.rows.reduce((s, x) => s + x.ratings, 0);
    console.log(`[${i}/${terms.length}] ${term} — ${r.total} productos · ${tot} valoraciones en el top ${r.rows.length}`);
  }
  await browser.close();
  console.log('listo ->', outFile);
})();
