// background/service-worker.js — núcleo del sistema Skoop v0.2

import { parseSkoolData } from './parsers.js';
import * as Storage from './storage.js';

// ── SETUP INICIAL ─────────────────────────────────────────

chrome.runtime.onInstalled.addListener(() => {
  // Alarma de churn cada 30 minutos
  chrome.alarms.create('skoop-churn-check', { periodInMinutes: 30 });
});

// ── ALARMAS ───────────────────────────────────────────────

chrome.alarms.onAlarm.addListener(async (alarm) => {
  if (alarm.name === 'skoop-churn-check') {
    await checkAlerts();
  }
});

async function checkAlerts() {
  const config = await Storage.getAlertsConfig();
  if (!config.churnAlert && !config.newMemberAlert) return;

  const communities = await Storage.listCommunities();
  const now = Date.now();
  const windowMs = 32 * 60 * 1000; // 32 min para cubrir la ventana de 30 min de la alarma

  for (const comm of communities) {
    const name = comm.name || comm.slug;

    if (config.churnAlert) {
      const members = await Storage.getMembers(comm.slug);
      // Miembros que acaban de entrar en riesgo (lastIndexed en la ventana actual)
      const newlyAtRisk = members.filter(
        (m) => m.riskLevel === 'atRisk' && m.lastIndexed && now - m.lastIndexed < windowMs
      );
      if (newlyAtRisk.length > 0) {
        chrome.notifications.create(`churn-${comm.slug}-${now}`, {
          type: 'basic',
          iconUrl: chrome.runtime.getURL('icons/icon48.png'),
          title: `Skoop — ${name}`,
          message: `${newlyAtRisk.length} miembro${newlyAtRisk.length > 1 ? 's' : ''} en riesgo de churn: ${newlyAtRisk.slice(0, 2).map((m) => m.name || m.username).join(', ')}${newlyAtRisk.length > 2 ? '…' : ''}`,
        });
      }
    }

    if (config.churnAlert) {
      // Fase 2: alerta de cancelación en proceso recién detectada.
      const cancelling = await Storage.getCancellingMembers(comm.slug);
      const newlyCancelling = cancelling.filter(
        (m) => m.subStatus === 'cancelling' && m.lastIndexed && now - m.lastIndexed < windowMs
      );
      if (newlyCancelling.length > 0) {
        chrome.notifications.create(`cancel-${comm.slug}-${now}`, {
          type: 'basic',
          iconUrl: chrome.runtime.getURL('icons/icon48.png'),
          title: `Skoop — ${name} · ¡Por irse! 🔴`,
          message: `${newlyCancelling.length} cancelaron pero siguen activas: ${newlyCancelling.slice(0, 2).map((m) => m.name || m.username).join(', ')}${newlyCancelling.length > 2 ? '…' : ''}. Escríbeles para retenerlas.`,
        });
      }
    }

    if (config.newMemberAlert) {
      const newMembers = await Storage.getNewMembers(comm.slug, now - windowMs);
      if (newMembers.length > 0) {
        chrome.notifications.create(`new-${comm.slug}-${now}`, {
          type: 'basic',
          iconUrl: chrome.runtime.getURL('icons/icon48.png'),
          title: `Skoop — ${name}`,
          message: `${newMembers.length} nuevo${newMembers.length > 1 ? 's' : ''} miembro${newMembers.length > 1 ? 's' : ''}: ${newMembers.slice(0, 2).map((m) => m.name || m.username).join(', ')}`,
        });
      }
    }
  }
}

// ── CONEXIONES LARGAS (desde bridge.js) ──────────────────

chrome.runtime.onConnect.addListener((port) => {
  if (port.name !== 'skoop-bridge') return;

  port.onMessage.addListener(async (payload) => {
    try {
      await handlePayload(payload);
    } catch (err) {
      console.error('[Skoop SW] Error procesando payload:', err);
    }
  });
});

// ── MENSAJES DIRECTOS (desde popup / bridge) ──────────────

chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
  const tab = sender?.tab;

  switch (msg.type) {

    // ── Existentes ──────────────────────────────────────────
    case 'SKOOP_TAB_READY':
      if (tab?.id && msg.slug) {
        chrome.action.setBadgeText({ text: '●', tabId: tab.id });
        chrome.action.setBadgeBackgroundColor({ color: '#7c3aed', tabId: tab.id });
      }
      break;

    case 'GET_COMMUNITIES':
      Storage.listCommunities().then(sendResponse);
      return true;

    case 'REGISTER_COMMUNITY':
      // Asegura que la comunidad exista en storage aunque aún no se haya indexado nada.
      Storage.saveCommunityMeta(msg.slug, { slug: msg.slug }).then(() => sendResponse({ ok: true }));
      return true;

    case 'GET_STATS':
      Storage.getCommunityStats(msg.slug).then(sendResponse);
      return true;

    case 'GET_MEMBERS':
      Storage.getMembers(msg.slug, msg.riskFilter).then(sendResponse);
      return true;

    case 'GET_CANCELLING':
      Storage.getCancellingMembers(msg.slug).then(sendResponse);
      return true;

    case 'GET_FEED':
      Storage.getFeed(msg.slug, msg.limit || 50).then(sendResponse);
      return true;

    case 'GET_ENDPOINTS':
      Storage.getDiscoveredEndpoints(msg.slug).then(sendResponse);
      return true;

    case 'CLEAR_COMMUNITY':
      Storage.clearCommunity(msg.slug).then(() => sendResponse({ ok: true }));
      return true;

    case 'CLEAR_ALL':
      Storage.clearAllData().then(() => sendResponse({ ok: true }));
      return true;

    // ── DOM Scanner ───────────────────────────────────────────
    case 'DOM_SCAN_BATCH':
      Storage.saveDomScanBatch(msg.slug, msg.members, msg.totalSoFar)
        .then(() => sendResponse({ ok: true }));
      return true;

    // Lote de miembros crudos de una página SSR (?p=N) → normalizar + guardar
    case 'SCAN_PAGE_MEMBERS':
      (async () => {
        const parsed = parseSkoolData('/members', { users: msg.users }, 'GET');
        if (parsed?.members?.length) {
          await Storage.saveMembers(msg.slug, parsed.members);
        }
        sendResponse({ ok: true, saved: parsed?.members?.length || 0 });
      })();
      return true;

    case 'SCAN_PROGRESS':
      Storage.saveScanStatus(msg.slug, { scanned: msg.scanned, running: true })
        .then(() => sendResponse({ ok: true }));
      return true;

    // Fase 2: miembros de la pestaña "Cancelling" → guardar + marcar 'cancelling'
    case 'SET_CANCELLING':
      (async () => {
        const parsed = parseSkoolData('/members', { users: msg.users || [] }, 'GET');
        const members = parsed?.members || [];
        if (members.length) await Storage.saveMembers(msg.slug, members);
        const ids = members.map((m) => m.id);
        await Storage.setCancellingMembers(msg.slug, ids);
        console.log(`[Skoop] Cancelling marcados en "${msg.slug}": ${ids.length}`);
        sendResponse({ ok: true, cancelling: ids.length });
      })();
      return true;

    // Fase 3: una página del feed (posts + comentarios embebidos)
    case 'SCAN_PAGE_FEED':
      (async () => {
        const parsed = parseSkoolData('/feed', { posts: msg.posts }, 'GET');
        let savedPosts = 0, savedComments = 0;
        if (parsed?.type === 'posts' && parsed.posts.length) {
          await Storage.savePosts(msg.slug, parsed.posts);
          savedPosts = parsed.posts.length;
          // Comentarios embebidos en el _raw de cada post. Skool varía el nombre
          // del array (comments/replies/commentList/topComments/preview…) y a veces
          // lo anida, así que buscamos en profundidad cualquier array que parezca
          // de comentarios dentro del wrapper del post.
          const embedded = [];
          for (const p of parsed.posts) {
            embedded.push(..._findCommentArrays(p._raw || {}));
          }
          if (embedded.length) {
            const cp = parseSkoolData('/comments', { comments: embedded }, 'GET');
            if (cp?.comments?.length) {
              await Storage.saveComments(msg.slug, cp.comments);
              savedComments = cp.comments.length;
            }
          }
        }
        sendResponse({ ok: true, savedPosts, savedComments });
      })();
      return true;

    case 'FEED_SCAN_PROGRESS':
      Storage.saveScanStatus(msg.slug, { feedScanned: msg.scanned, feedRunning: true })
        .then(() => sendResponse({ ok: true }));
      return true;

    case 'FEED_SCAN_COMPLETE':
      (async () => {
        await Storage.saveScanStatus(msg.slug, { feedScanned: msg.total, feedRunning: false, feedCompletedAt: Date.now() });
        const stored = await chrome.storage.local.get(['skoop_feed_scan_tab_id', 'skoop_auto_feed_scan_slug']);
        if (stored.skoop_feed_scan_tab_id) {
          chrome.tabs.remove(stored.skoop_feed_scan_tab_id).catch(() => {});
          await chrome.storage.local.remove(['skoop_feed_scan_tab_id', 'skoop_auto_feed_scan_slug']);
        }
        sendResponse({ ok: true });
      })();
      return true;

    case 'SCAN_COMPLETE':
      (async () => {
        await Storage.saveScanStatus(msg.slug, { scanned: msg.total, running: false, completedAt: Date.now() });
        // Cerrar la tab de background si fue abierta por Skoop
        const stored = await chrome.storage.local.get(['skoop_scan_tab_id', 'skoop_auto_scan_slug']);
        if (stored.skoop_scan_tab_id) {
          chrome.tabs.remove(stored.skoop_scan_tab_id).catch(() => {});
          await chrome.storage.local.remove(['skoop_scan_tab_id', 'skoop_auto_scan_slug']);
        }
        sendResponse({ ok: true });
      })();
      return true;

    case 'GET_SCAN_STATUS':
      Storage.getScanStatus(msg.slug).then(sendResponse);
      return true;

    case 'SAVE_SCAN_STATUS': {
      const { slug: _ss, running, scanned, completedAt } = msg;
      const payload = {};
      if (running !== undefined) payload.running = running;
      if (scanned !== undefined) payload.scanned = scanned;
      if (completedAt !== undefined) payload.completedAt = completedAt;
      Storage.saveScanStatus(_ss, payload).then(() => sendResponse({ ok: true }));
      return true;
    }

    // START_SCAN_FROM_POPUP: usa tab activa si ya está en /members, si no abre una en background
    case 'START_SCAN_FROM_POPUP':
      (async () => {
        const slug = msg.slug;
        if (!slug) { sendResponse({ ok: false, error: 'slug requerido' }); return; }

        // Evitar tabs duplicadas: verificar si ya hay un scan en curso
        const existing = await chrome.storage.local.get(['skoop_scan_tab_id']);
        if (existing.skoop_scan_tab_id) {
          const existingTab = await chrome.tabs.get(existing.skoop_scan_tab_id).catch(() => null);
          if (existingTab) {
            sendResponse({ ok: false, error: 'Scan ya en curso en otra tab' });
            return;
          }
          // La tab fue cerrada manualmente — limpiar
          await chrome.storage.local.remove(['skoop_scan_tab_id', 'skoop_auto_scan_slug']);
        }

        // El scanner pagina la API SSR por fetch, así que sirve CUALQUIER tab de la
        // comunidad (no hace falta /members). Si hay una tab activa de la comunidad,
        // escaneamos ahí mismo (sin abrir nada).
        const activeTabs = await chrome.tabs.query({ url: `https://*.skool.com/${slug}*`, active: true, currentWindow: true });
        if (activeTabs[0]) {
          const result = await chrome.tabs.sendMessage(activeTabs[0].id, { type: 'START_SCAN' })
            .catch(() => null);
          if (result?.ok) { sendResponse(result); return; }
          // Si el content script no respondió (tab abierta ANTES de cargar la extensión)
          // caemos al flujo de tab nueva, que sí inyecta los scripts.
        }

        // Abrir tab de la comunidad en background (active: false). El scan ya no
        // depende de renderizar/scrollear — sólo hace fetch, que funciona en background.
        const membersUrl = `https://www.skool.com/${slug}/-/members`;
        const tab = await chrome.tabs.create({ url: membersUrl, active: false });

        // Guardar ID de la tab para cerrarla al terminar y señal para auto-start
        await chrome.storage.local.set({
          skoop_scan_tab_id: tab.id,
          skoop_auto_scan_slug: slug,
        });

        sendResponse({ ok: true, backgroundTab: true });
      })();
      return true;

    // Fase 3: disparar scan de actividad (feed) desde el popup
    case 'START_FEED_SCAN_FROM_POPUP':
      (async () => {
        const slug = msg.slug;
        if (!slug) { sendResponse({ ok: false, error: 'slug requerido' }); return; }

        const existing = await chrome.storage.local.get(['skoop_feed_scan_tab_id']);
        if (existing.skoop_feed_scan_tab_id) {
          const existingTab = await chrome.tabs.get(existing.skoop_feed_scan_tab_id).catch(() => null);
          if (existingTab) { sendResponse({ ok: false, error: 'Scan de actividad ya en curso' }); return; }
          await chrome.storage.local.remove(['skoop_feed_scan_tab_id', 'skoop_auto_feed_scan_slug']);
        }

        const activeTabs = await chrome.tabs.query({ url: `https://*.skool.com/${slug}*`, active: true, currentWindow: true });
        if (activeTabs[0]) {
          const result = await chrome.tabs.sendMessage(activeTabs[0].id, { type: 'START_FEED_SCAN' }).catch(() => null);
          if (result?.ok) { sendResponse(result); return; }
        }

        const feedUrl = `https://www.skool.com/${slug}`;
        const tab = await chrome.tabs.create({ url: feedUrl, active: false });
        await chrome.storage.local.set({ skoop_feed_scan_tab_id: tab.id, skoop_auto_feed_scan_slug: slug });
        sendResponse({ ok: true, backgroundTab: true });
      })();
      return true;

    case 'EXPORT_RAW':
      Storage.getCommunityRaw(msg.slug).then(sendResponse);
      return true;

    // ── Etiquetas ────────────────────────────────────────────
    case 'GET_LABEL_TYPES':
      Storage.getLabelTypes().then(sendResponse);
      return true;

    case 'SAVE_LABEL_TYPE':
      Storage.saveLabelType(msg.label).then(() => sendResponse({ ok: true }));
      return true;

    case 'DELETE_LABEL_TYPE':
      Storage.deleteLabelType(msg.id).then(() => sendResponse({ ok: true }));
      return true;

    case 'TOGGLE_MEMBER_LABEL':
      Storage.toggleMemberLabel(msg.slug, msg.memberId, msg.labelId).then(() => sendResponse({ ok: true }));
      return true;

    // ── Pipeline ─────────────────────────────────────────────
    case 'SAVE_PIPELINE':
      Storage.saveMemberPipeline(msg.slug, msg.memberId, msg.stage, msg.notes).then(() => sendResponse({ ok: true }));
      return true;

    case 'GET_PIPELINE_MEMBERS':
      Storage.getPipelineMembers(msg.slug).then(sendResponse);
      return true;

    // ── Plantillas ───────────────────────────────────────────
    case 'GET_TEMPLATES':
      Storage.getTemplates().then(sendResponse);
      return true;

    case 'SAVE_TEMPLATE':
      Storage.saveTemplate(msg.template).then(() => sendResponse({ ok: true }));
      return true;

    case 'DELETE_TEMPLATE':
      Storage.deleteTemplate(msg.id).then(() => sendResponse({ ok: true }));
      return true;

    // ── Bienvenida automática ─────────────────────────────────
    case 'GET_WELCOME':
      Storage.getWelcomeConfig().then(sendResponse);
      return true;

    case 'SAVE_WELCOME':
      Storage.saveWelcomeConfig(msg.config).then(() => sendResponse({ ok: true }));
      return true;

    case 'MARK_WELCOME_SENT':
      Storage.markWelcomeSent(msg.memberId).then(() => sendResponse({ ok: true }));
      return true;

    case 'GET_PENDING_WELCOMES':
      Storage.getPendingWelcomes(msg.slug).then(sendResponse);
      return true;

    // ── Alertas ───────────────────────────────────────────────
    case 'GET_ALERTS_CONFIG':
      Storage.getAlertsConfig().then(sendResponse);
      return true;

    case 'SAVE_ALERTS_CONFIG':
      Storage.saveAlertsConfig(msg.config).then(() => sendResponse({ ok: true }));
      return true;

    // ── Configuración visual ──────────────────────────────────
    case 'GET_SETTINGS':
      Storage.getSettings().then(sendResponse);
      return true;

    case 'SAVE_SETTINGS':
      Storage.saveSettings(msg.settings).then(() => sendResponse({ ok: true }));
      return true;

    // ── Nuevos miembros ───────────────────────────────────────
    case 'GET_NEW_MEMBERS':
      Storage.getNewMembers(msg.slug, msg.sinceMs || Date.now() - 86400000).then(sendResponse);
      return true;
  }
});

// ── PROCESAMIENTO CENTRAL ─────────────────────────────────

async function handlePayload(payload) {
  if (!payload) return;

  if (payload.type === 'page_info') {
    if (payload.slug) {
      await Storage.saveCommunityMeta(payload.slug, {
        slug: payload.slug,
        lastVisited: payload.ts,
      });
    }
    return;
  }

  if (payload.type === 'api_call') {
    const { url, data, method, ts, pageSlug } = payload;
    if (!url || !data) return;

    const slug = pageSlug || extractSlug(url);
    if (!slug) return;

    await Storage.recordEndpoint(slug, {
      url: stripQuery(url),
      method: method || 'GET',
    });

    const parsed = parseSkoolData(url, data, method);
    if (!parsed) return;

    switch (parsed.type) {
      case 'members':
        await Storage.saveMembers(slug, parsed.members);
        console.log(`[Skoop] +${parsed.members.length} miembros en "${slug}"`);
        break;

      case 'posts':
        await Storage.savePosts(slug, parsed.posts);
        console.log(`[Skoop] +${parsed.posts.length} posts en "${slug}"`);
        break;

      case 'comments':
        await Storage.saveComments(slug, parsed.comments, parsed.postId);
        console.log(`[Skoop] +${parsed.comments.length} comentarios en "${slug}"`);
        break;

      case 'profile':
        if (parsed.member) {
          await Storage.saveMember(slug, parsed.member);
          console.log(`[Skoop] Perfil indexado: ${parsed.member.name} en "${slug}"`);
        }
        break;

      case 'community':
        if (parsed.community) {
          await Storage.saveCommunityMeta(slug, {
            name: parsed.community.name,
            memberCount: parsed.community.memberCount,
            description: parsed.community.description,
          });
        }
        break;
    }
  }
}

// ── UTILIDADES ────────────────────────────────────────────

function extractSlug(url) {
  try {
    const fullUrl = url.startsWith('http') ? url : `https://skool.com${url}`;
    const u = new URL(fullUrl);
    const parts = u.pathname.split('/').filter(Boolean);
    if (!parts[0] || ['api', '_next', 'static', 'favicon'].includes(parts[0])) {
      return null;
    }
    return parts[0];
  } catch {
    return null;
  }
}

function stripQuery(url) {
  try {
    const u = new URL(url.startsWith('http') ? url : `https://skool.com${url}`);
    return u.pathname;
  } catch {
    return url.split('?')[0];
  }
}

// Busca en profundidad (hasta 4 niveles) cualquier array de objetos que parezca
// de comentarios dentro del wrapper de un post de Skool. Skool varía el nombre
// (comments/replies/commentList/topComments/preview/children…), así que en vez
// de adivinar el campo, detectamos por forma: items con content/body/text o un
// sub-objeto comment/metadata con texto.
function _findCommentArrays(obj, depth = 0, out = []) {
  if (depth > 4 || !obj || typeof obj !== 'object') return out;
  for (const [k, v] of Object.entries(obj)) {
    if (Array.isArray(v) && v.length && _looksLikeComment(v[0]) && /comment|repl|children|thread/i.test(k)) {
      out.push(...v);
    } else if (v && typeof v === 'object' && !Array.isArray(v)) {
      _findCommentArrays(v, depth + 1, out);
    }
  }
  return out;
}

function _looksLikeComment(o) {
  if (!o || typeof o !== 'object') return false;
  const inner = (o.comment && typeof o.comment === 'object') ? o.comment : o;
  const md = inner.metadata || {};
  return ('content' in inner) || ('body' in inner) || ('text' in inner) ||
    ('content' in md) || ('body' in md);
}

console.log('[Skoop] Service worker v0.2 listo ✓');
