// ====================================================================
// API ADAPTER (fase 2) — mapea el backend QO al SHAPE CONGELADO del mock.
// Por defecto el dashboard usa el mock curado (demo de cliente estable).
// Para usar datos reales del backend: abre la URL con  ?live=1
//   ej. http://localhost:4000/?live=1
// Si el backend falla o no hay datos, cae al mock sin romper la UI.
// ====================================================================
(function () {
  const params = new URLSearchParams(location.search);
  window.QO_USE_LIVE = !params.has('mock');
  const BASE = (params.get('api') || '').replace(/\/$/, ''); // mismo origen por defecto

  const j = async (path) => {
    const r = await fetch(BASE + path);
    if (!r.ok) throw new Error(path + ' ' + r.status);
    return r.json();
  };

  // backend.source -> design.platform key
  const SRC = {
    reddit: 'reddit', x: 'x', facebook: 'facebook', playstore: 'playstore',
    appstore: 'appstore', news: 'press', regulator: 'press',
    tiktok: 'tiktok', youtube: 'tiktok', instagram: 'facebook',
    forum: 'reddit', web: 'press',
  };
  // backend.topic -> design.topic (las 6 del diseño)
  const TOP = {
    cobranza: 'cobranza', tasas: 'tasas', china: 'china', ia: 'ia',
    fraude: 'fraude', servicio: 'servicio', montadeudas: 'cobranza',
    regulacion: 'servicio', producto: 'servicio', otro: 'servicio',
  };
  const SENT = { positive: 'pos', negative: 'neg', neutral: 'neu' };
  const COLORS = {
    crece: '#14C46A', kueski: '#7B2FF7', klar: '#2A6FDB', stori: '#E0533D',
    tala: '#1F9D77', kubo: '#F2A30F', moneyman: '#2BB0A6', creditea: '#3FB6C9', avafin: '#5EABD6',
    ipeso: '#C8412F', didi: '#E85D00', maxcredito: '#A8331F',
    mexacash: '#D45A2A', fintopia: '#8A2417', mexdin: '#B84A2A',
  };
  const platFromLabel = (lbl = '') => {
    const s = lbl.toLowerCase();
    if (s.includes('reddit')) return 'reddit';
    if (s.includes('facebook')) return 'facebook';
    if (s.includes('app store')) return 'appstore';
    if (s.includes('play')) return 'playstore';
    if (s.includes('x ·') || s.includes('twitter')) return 'x';
    if (s.includes('youtube') || s.includes('tiktok')) return 'tiktok';
    return 'press';
  };
  const spark = (v, n = 9) =>
    Array.from({ length: n }, (_, i) => Math.max(0, Math.round(v * (0.55 + (0.45 * i) / (n - 1)))));

  function mapMention(m) {
    const sourceType = m.actionable ? 'own_channel' : m.source === 'news' || m.source === 'regulator' ? 'press' : 'social';
    let url = m.url || '';
    return {
      id: m.id,
      platform: SRC[m.source] || 'press',
      author: m.author,
      handle: m.platform,
      reach: m.authorReach || 0,
      topic: TOP[m.topics?.[0]] || 'servicio',
      sentiment: SENT[m.sentiment] || 'neu',
      severity: m.severity,
      actionable: !!m.actionable,
      sourceType,
      link: url,
      excerpt: { es: m.text, zh: m.text_zh || m.text },
      entity: m.entity,
      publishedAt: m.publishedAt || null,
      replyText: m.replyText ? { es: m.replyText, zh: m.replyText_zh || m.replyText } : null,
      replyDate: m.replyDate || null,
    };
  }
  window.QO_MAP_MENTION = mapMention;

  window.QO_LOAD = async function () {
    const [summary, mentions, competitors, collections, alerts, sentSeries, narrativesRaw] = await Promise.all([
      j('/api/summary'), j('/api/mentions'), j('/api/competitors'),
      j('/api/collections'), j('/api/alerts'),
      j('/api/sentiment-series'),  // 30 días de sentimiento diario de Crece
      j('/api/narratives'),        // temas con volumen semanal real
    ]);
    const M = window.MOCK; // base de fallback para propiedades no cubiertas

    // KPIs
    const KPIS = {
      risk: { value: summary.riskIndexToday, unit: '/100', trend: summary.riskTrend, dir: summary.riskTrend >= 0 ? 'up' : 'down', level: 'high', spark: spark(summary.riskIndexToday) },
      contam: { value: summary.contaminationIndex, unit: '%', trend: 0, dir: 'flat', level: 'medium', spark: spark(summary.contaminationIndex) },
      mentions: { value: summary.mentions24h, unit: '', trend: 0, dir: 'flat', level: '', spark: spark(summary.mentions24h) },
      alerts: { value: summary.activeAlerts, unit: '', trend: 0, dir: 'flat', level: 'high', spark: spark(summary.activeAlerts) },
    };

    // Competidores: SOV y menciones calculados 100% desde conteo real de reseñas de Play Store únicamente
    const totalCompPlaystoreMentions = competitors
      .filter(c => c.key !== 'crece') // excluir Crece del SOV de competidores
      .reduce((acc, c) => acc + (c.summary ? (c.summary.playstoreCount ?? 0) : 0), 0) || 1;

    const COMP_KOSHER = competitors
      .filter((c) => c.tier === 'kosher' || c.key === 'crece') // solo competidores + Crece
      .map((c) => {
        const playCount = c.summary ? (c.summary.playstoreCount ?? 0) : 0;
        return {
          key: c.key,
          name: c.name,
          sov: Math.round((playCount / totalCompPlaystoreMentions) * 100),
          sentiment: c.avgSentiment > 0.1 ? 'pos' : c.avgSentiment < -0.1 ? 'neg' : 'neu',
          note: c.key === 'kueski' ? 'benchmark' : c.key === 'crece' ? 'you' : '',
          color: COLORS[c.key] || '#2A6FDB',
          mentions: playCount,
          avgSentiment: c.avgSentiment ?? 0,
          downloads: c.downloads || { playstore: 0, appstore: 0 },
          playstoreCountPeriod: c.playstoreCountPeriod,
          playstorePositivePercentPeriod: c.playstorePositivePercentPeriod,
        };
      })
      .filter((c) => c.mentions > 0 || c.sov > 0) // solo los que tienen reseñas o SOV
      .sort((a, b) => b.sov - a.sov);

    const COMP_AZTLAN = competitors
      .filter((c) => c.tier === 'controversial')
      .map((c) => {
        const playCount = c.summary ? (c.summary.playstoreCount ?? 0) : 0;
        return {
          key: c.key,
          name: c.name,
          sov: Math.round((playCount / totalCompPlaystoreMentions) * 100),
          complaints: playCount,
          color: COLORS[c.key] || '#C8412F',
          mentions: playCount,
          avgSentiment: c.avgSentiment ?? 0,
          downloads: c.downloads || { playstore: 0, appstore: 0 },
          playstoreCountPeriod: c.playstoreCountPeriod,
          playstorePositivePercentPeriod: c.playstorePositivePercentPeriod,
        };
      })
      .filter((c) => c.mentions > 0 || c.sov > 0)
      .sort((a, b) => b.sov - a.sov);

    // Heatmap: 4 capas (no redes individuales — la cobertura varía)
    // Backend rank 0-5 → 0-100; -1 = sin datos → 0
    const HEAT_PLATFORMS = ['comunidades', 'playstore', 'appstore', 'prensa'];
    const HEAT_ROWS = (collections.matrix || []).map((row) => {
      const comp = competitors.find((c) => c.key === row.entity);
      // Capa "comunidades": máximo de reddit/x/facebook/tiktok
      const comunMax = Math.max(
          row.reddit ?? -1, row.x ?? -1, row.facebook ?? -1, row.tiktok ?? -1,
      );
      // Capa "prensa": máximo de news/regulator
      const prensaMax = Math.max(row.news ?? -1, row.regulator ?? -1, row.press ?? -1);
      return {
        name: comp ? comp.name : row.entity,
        v: [
          comunMax  >= 0 ? comunMax  * 20 : -1,
          row.playstore != null && row.playstore >= 0 ? row.playstore * 20 : -1,
          row.appstore  != null && row.appstore  >= 0 ? row.appstore  * 20 : -1,
          prensaMax >= 0 ? prensaMax * 20 : -1,
        ],
      };
    });

    const RAW_COMPLAINTS = (collections.complaints || []).map((c) => {
      let url = c.url || '';
      const platformKey = platFromLabel(c.platform);
      return {
        id: c.id, platform: platformKey, reach: 0, severity: c.severity,
        aiClass: { es: c.aggression, zh: c.aggression },
        text: { es: '«' + c.text + '»', zh: '«' + c.text + '»' },
        url,
      };
    });

    const ALERTS = (alerts || []).map((a) => ({
      id: a.id, level: a.severity >= 80 ? 'crisis' : 'warn',
      topic: TOP[a.mention?.topics?.[0]] || 'cobranza', velocity: a.velocity, since: '—',
      spark: spark(a.velocity),
      title: { es: a.title, zh: a.title },
      desc: { es: a.mention?.text || '', zh: a.mention?.text_zh || a.mention?.text || '' },
      threshold: { es: 'Severidad ' + a.severity + '/100', zh: '严重度 ' + a.severity + '/100' },
    }));

    // ── Serie de sentimiento 30 días (Crece) ──────────────────────────────
    // sentSeries: [{d:0..29, pos, neu, neg}] — d=0 hace 29 días, d=29 hoy.
    // Si el backend devuelve exactamente 30 puntos, úsalos; si no, mantén mock.
    const SENTIMENT_SERIES = Array.isArray(sentSeries) && sentSeries.length === 30
      ? sentSeries
      : M.SENTIMENT_SERIES;

    // ── Narrativas con volumen semanal real ───────────────────────────────
    // /api/narratives devuelve [{topic, total, tone, weeklyVol:[7]}]
    // tone es un float; lo convertimos al string 'pos'/'neu'/'neg'.
    const toneStr = (v) => (v > 0.1 ? 'pos' : v < -0.1 ? 'neg' : 'neu');
    const NARRATIVES = Array.isArray(narrativesRaw)
      ? narrativesRaw.slice(0, 8).map((n) => ({
          topic: TOP[n.topic] || n.topic,
          // weeklyVol: 7 conteos semanales reales del backend. Fallback: flat al total.
          vol: Array.isArray(n.weeklyVol) && n.weeklyVol.length === 7
            ? n.weeklyVol
            : Array(7).fill(n.total),
          tone: toneStr(n.tone),
        }))
      : [];

    // ── Periodo del snapshot: el mes dominante de los datos ──────────────
    // El dashboard siempre muestra UN mes (el que ingirió la última corrida).
    const MESES_FULL = ['Enero','Febrero','Marzo','Abril','Mayo','Junio','Julio','Agosto','Septiembre','Octubre','Noviembre','Diciembre'];
    const monthCount = {};
    for (const m of mentions) {
      const k = (m.publishedAt || '').slice(0, 7);
      if (k.length === 7) monthCount[k] = (monthCount[k] || 0) + 1;
    }
    const dataMonth = Object.entries(monthCount).sort((a, b) => b[1] - a[1])[0]?.[0] || '2026-05';
    const [py, pm] = dataMonth.split('-').map(Number);
    const MONTHS_EN = ['January','February','March','April','May','June','July','August','September','October','November','December'];
    const PERIOD = {
      month: dataMonth,                                   // '2026-05'
      labelEs: `${MESES_FULL[pm - 1]} ${py}`,             // 'Mayo 2026'
      labelZh: `${py}年${pm}月`,                           // '2026年5月'
      labelEn: `${MONTHS_EN[pm - 1]} ${py}`,              // 'May 2026'
      days: new Date(Date.UTC(py, pm, 0)).getUTCDate(),   // 28-31
    };

    // ── Etiquetas eje X narrativas (últimas 7 semanas) ────────────────────
    // Ancladas al último día con datos, igual que el backend.
    const MESES = ['Ene','Feb','Mar','Abr','May','Jun','Jul','Ago','Sep','Oct','Nov','Dic'];
    let dataMax = 0;
    for (const m of mentions) {
      const ts = new Date(m.publishedAt).getTime();
      if (ts > dataMax) dataMax = ts;
    }
    if (!dataMax) dataMax = Date.now();
    const NARR_MONTHS = Array.from({ length: 7 }, (_, i) => {
      const d = new Date(dataMax - (6 - i) * 7 * 86400000);
      return MESES[d.getMonth()] + ' ' + d.getDate();
    });

    return {
      ...M,
      MENTIONS: mentions.map(mapMention),
      KPIS, COMP_KOSHER, COMP_AZTLAN,
      HEAT_PLATFORMS, HEAT_ROWS, RAW_COMPLAINTS,
      ALERTS,
      SENTIMENT_SERIES,
      NARRATIVES,
      NARR_MONTHS,
      PERIOD,
    };
  };
})();
