// content/dom-scanner.js — scanner de miembros vía PAGINACIÓN de la API SSR de Skool
// Corre en mundo ISOLATED (document_end) en skool.com. Expone window.__SkoopScanner.
//
// Skool renderiza la página de miembros en el servidor (Next.js): cada
// /<slug>/-/members?p=N trae ~30 miembros COMPLETOS (nombre, email, firstName,
// lastName, member.lastOffline = última actividad) incrustados en
// <script id="__NEXT_DATA__">. NO hay infinite-scroll. Así que recorremos las
// páginas por fetch (same-origin → con la sesión de la usuaria) y mandamos los
// usuarios crudos al service worker, que los normaliza con el parser.

'use strict';

window.__SkoopScanner = (() => {

  let _scanning = false;
  function isScanRunning() { return _scanning; }
  function stopScan() { _scanning = false; }

  function _slug() {
    return location.pathname.split('/').filter(Boolean)[0];
  }

  // Extrae props.pageProps del <script id="__NEXT_DATA__"> de un HTML.
  function _parseNextData(html) {
    const m = html.match(
      /<script id="__NEXT_DATA__" type="application\/json">([\s\S]*?)<\/script>/
    );
    if (!m) return null;
    try {
      return JSON.parse(m[1])?.props?.pageProps || null;
    } catch (_) {
      return null;
    }
  }

  async function _fetchPage(slug, p) {
    const url = `https://www.skool.com/${slug}/-/members?p=${p}`;
    const res = await fetch(url, { credentials: 'same-origin' });
    if (!res.ok) throw new Error('HTTP ' + res.status);
    return _parseNextData(await res.text());
  }

  // Fase 2: pestaña "Cancelling" de Skool (?t=cancelling) — fuente de verdad
  // de quién canceló pero sigue con acceso.
  async function _fetchCancellingPage(slug, p) {
    const url = `https://www.skool.com/${slug}/-/members?t=cancelling&p=${p}`;
    const res = await fetch(url, { credentials: 'same-origin' });
    if (!res.ok) throw new Error('HTTP ' + res.status);
    return _parseNextData(await res.text());
  }

  // Recorre la pestaña cancelling, junta a TODOS sus miembros y se los manda al
  // SW en un solo mensaje (SET_CANCELLING), que marca el flag y limpia a los que
  // ya no aparecen.
  async function _scanCancelling(slug) {
    const all = [];
    try {
      const cp1 = await _fetchCancellingPage(slug, 1);
      const totalPages = cp1?.totalPages || 1;
      for (let p = 1; _scanning && p <= totalPages; p++) {
        const pp = p === 1 ? cp1 : await _fetchCancellingPage(slug, p).catch(() => null);
        const users = _usersFrom(pp);
        if (users.length) all.push(...users);
        await _sleep(400);
      }
    } catch (e) {
      console.error('[Skoop] Error escaneando pestaña cancelling:', e);
    }
    chrome.runtime.sendMessage({ type: 'SET_CANCELLING', slug, users: all }).catch(() => {});
    return all.length;
  }

  function _usersFrom(pp) {
    if (!pp) return [];
    return pp.users || pp.renderData?.members || [];
  }

  function startScan(onProgress, onComplete) {
    if (_scanning) return;
    _scanning = true;
    _runPaginated(onProgress, onComplete);
  }

  async function _runPaginated(onProgress, onComplete) {
    const slug = _slug();
    let scanned = 0;
    let totalPages = 1;
    let total = 0;

    try {
      // Página 1: descubre totalPages + total, y ya trae los primeros 30.
      const pp1 = await _fetchPage(slug, 1);
      if (!pp1) throw new Error('sin __NEXT_DATA__ (¿sesión cerrada?)');

      totalPages = pp1.totalPages || 1;
      total = pp1.total || pp1.totalMembers || 0;

      for (let p = 1; _scanning && p <= totalPages; p++) {
        const pp = p === 1 ? pp1 : await _fetchPage(slug, p).catch(() => null);
        const users = _usersFrom(pp);

        if (users.length) {
          // Enviar en lotes razonables al SW (cada página ya son ~30)
          chrome.runtime.sendMessage({ type: 'SCAN_PAGE_MEMBERS', slug, users }).catch(() => {});
          scanned += users.length;
        }

        onProgress?.({ scanned, total: total || totalPages * 30, page: p, totalPages });

        // Cortés con Skool para no gatillar rate-limit / WAF
        await _sleep(400);
      }

      // Fase 2: tras los miembros, marcar a los de la pestaña "Cancelling".
      if (_scanning) await _scanCancelling(slug);
    } catch (e) {
      console.error('[Skoop] Error en scan paginado:', e);
    }

    _scanning = false;
    onComplete?.({ total: scanned });
  }

  function _sleep(ms) {
    return new Promise((r) => setTimeout(r, ms));
  }

  // ── ESCÁNER DE FEED / ACTIVIDAD (Fase 3) ──────────────────
  // Pagina el feed SSR de la comunidad, extrae posts y los comentarios embebidos
  // en cada post, y los manda al SW (SCAN_PAGE_FEED). El SW los normaliza y
  // agrega la actividad por miembro (nº de comentarios, último comentario).

  async function _fetchFeedPage(slug, p) {
    const url = `https://www.skool.com/${slug}?p=${p}`;
    const res = await fetch(url, { credentials: 'same-origin' });
    if (!res.ok) throw new Error('HTTP ' + res.status);
    return _parseNextData(await res.text());
  }

  function _looksLikePost(o) {
    if (!o || typeof o !== 'object') return false;
    // Item directo o envuelto como { post: {...}, comments: [...] }
    const inner = (o.post && typeof o.post === 'object') ? o.post : o;
    const md = inner.metadata || {};
    return ('post' in o) || 'body' in inner || 'content' in inner ||
      'commentsCount' in inner || 'comments' in o || 'commentCount' in inner ||
      ('content' in md) || ('id' in inner && 'metadata' in inner && ('content' in md || 'body' in md));
  }

  function _postsFrom(pp) {
    if (!pp) return [];
    const direct =
      pp.posts || pp.feed || pp.postTrees || pp.feedData || pp.feedPosts ||
      pp.renderData?.posts || pp.renderData?.feed || pp.renderData?.postTrees ||
      pp.feed?.posts || pp.data?.posts;
    if (Array.isArray(direct) && direct.length) return direct;
    return _deepFindPostArray(pp) || [];
  }

  function _deepFindPostArray(obj, depth = 0) {
    if (depth > 3 || !obj || typeof obj !== 'object') return null;
    for (const v of Object.values(obj)) {
      if (Array.isArray(v) && v.length && _looksLikePost(v[0])) return v;
    }
    for (const v of Object.values(obj)) {
      if (v && typeof v === 'object' && !Array.isArray(v)) {
        const r = _deepFindPostArray(v, depth + 1);
        if (r) return r;
      }
    }
    return null;
  }

  function startFeedScan(onProgress, onComplete) {
    if (_scanning) return;
    _scanning = true;
    _runFeedPaginated(onProgress, onComplete);
  }

  async function _runFeedPaginated(onProgress, onComplete) {
    const slug = _slug();
    let scanned = 0;
    let totalPages = 1;

    try {
      const pp1 = await _fetchFeedPage(slug, 1);
      if (!pp1) throw new Error('sin __NEXT_DATA__ (¿sesión cerrada?)');
      totalPages = pp1.totalPages || 1;

      for (let p = 1; _scanning && p <= totalPages; p++) {
        const pp = p === 1 ? pp1 : await _fetchFeedPage(slug, p).catch(() => null);
        const posts = _postsFrom(pp);
        if (posts.length) {
          chrome.runtime.sendMessage({ type: 'SCAN_PAGE_FEED', slug, posts }).catch(() => {});
          scanned += posts.length;
        }
        onProgress?.({ scanned, page: p, totalPages });
        await _sleep(500);
      }
    } catch (e) {
      console.error('[Skoop] Error en scan de feed:', e);
    }

    _scanning = false;
    onComplete?.({ total: scanned });
  }

  return { startScan, startFeedScan, stopScan, isScanRunning };

})();
