// salud.jsx — Sección «Salud» de Serenia (datos del Apple Watch vía el atajo «Serenia Salud»)
// Cargado en SerenIA.html: <script type="text/babel" src="./salud.jsx">
//
// Expone:
//   window.SereniaHealth  — helpers compartidos con HealthCard (Tendencias) y buildMoodPrime (chat):
//                           load, digest, relations, summarize, writeSummary, isShareOn, setShare, dayLabel, timeAgo
//   window.SaludScreen    — la pantalla completa (Perfil → Salud y Apple Watch, o «Ver todo» en Tendencias)
//
// Claves de localStorage:
//   serenia_health_share_<uid>    '1' | '0' — interruptor «Compartir con Sereni» (por defecto activado)
//   serenia_health_summary_<uid>  { at, text } — resumen corto que buildMoodPrime añade al contexto silencioso
//
// Lenguaje: hablamos de descanso, movimiento y ánimo; nunca de diagnósticos.
(function () {
  'use strict';

  // ── Métricas conocidas (mismos tipos que health_metrics.sql y CONECTAR-APPLE-WATCH.md) ──────────
  // agg: cómo se resume el día si llegan varias filas (el atajo manda una por día con start = fecha;
  //      si mandara «total hasta ahora» varias veces, max evita duplicar; el pulso se promedia).
  // better: hacia dónde es «mejor» para colorear la comparación con el promedio.
  const METRICS = {
    resting_heart_rate: { key: 'rhr',     label: 'Pulso en reposo',  unit: 'lpm',   agg: 'mean', better: 'down' },
    heart_rate:         { key: 'hr',      label: 'Pulso promedio',   unit: 'lpm',   agg: 'mean', better: 'down' },
    hrv:                { key: 'hrv',     label: 'HRV',              unit: 'ms',    agg: 'mean', better: 'up' },
    steps:              { key: 'steps',   label: 'Pasos',            unit: 'pasos', agg: 'max',  better: 'up' },
    active_calories:    { key: 'cal',     label: 'Calorías activas', unit: 'kcal',  agg: 'max',  better: 'up' },
    sleep_hours:        { key: 'sleep',   label: 'Sueño',            unit: 'h',     agg: 'max',  better: 'up' },
    mindful_minutes:    { key: 'mindful', label: 'Atención plena',   unit: 'min',   agg: 'max',  better: 'up' },
  };
  const CARD_ORDER = ['resting_heart_rate', 'heart_rate', 'hrv', 'steps', 'active_calories', 'sleep_hours', 'mindful_minutes'];

  // ── Fechas y formato ───────────────────────────────────────────────────────────────────────────
  const pad2 = (n) => String(n).padStart(2, '0');
  // Clave de día de una fila: el atajo manda start = fecha local «yyyy-MM-dd» (medianoche UTC en la
  // tabla), así que la fecha UTC de start_ts es el día que la persona vivió.
  const dayKey = (ts) => { const d = new Date(ts); return isNaN(d) ? String(ts).slice(0, 10) : d.toISOString().slice(0, 10); };
  const localToday = () => { const d = new Date(); return `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-${pad2(d.getDate())}`; };
  const utcToday = () => new Date().toISOString().slice(0, 10);
  const shiftKey = (key, n) => { const d = new Date(key + 'T12:00:00Z'); d.setUTCDate(d.getUTCDate() + n); return d.toISOString().slice(0, 10); };
  const MESES = ['ene', 'feb', 'mar', 'abr', 'may', 'jun', 'jul', 'ago', 'sep', 'oct', 'nov', 'dic'];
  const dayLabel = (key) => {
    if (!key) return '';
    if (key === localToday() || key === utcToday()) return 'Hoy';
    if (key === shiftKey(localToday(), -1)) return 'Ayer';
    return `${Number(key.slice(8, 10))} ${MESES[Number(key.slice(5, 7)) - 1] || ''}`;
  };
  const fmtInt = (n) => Math.round(Number(n)).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ' ');
  const fmt1 = (n) => (Math.round(Number(n) * 10) / 10).toFixed(1);
  const timeAgo = (ts) => {
    if (!ts) return 'sin fecha';
    const diff = Date.now() - (typeof ts === 'number' ? ts : new Date(ts).getTime());
    if (!isFinite(diff)) return 'sin fecha';
    const m = Math.floor(diff / 60000);
    if (m < 2) return 'hace un momento';
    if (m < 60) return `hace ${m} min`;
    const h = Math.floor(m / 60);
    if (h < 24) return `hace ${h} h`;
    if (h < 48) return 'ayer';
    return `hace ${Math.floor(h / 24)} días`;
  };

  // ── Lectura ────────────────────────────────────────────────────────────────────────────────────
  // Filas por SupaHealth.getRecent (fuente única, se puede simular en pruebas) + fecha de la última
  // sincronización con una consulta mínima (created_at más reciente; getRecent no lo trae).
  async function load(userId, days = 28) {
    if (!userId || !window.SupaHealth) return { rows: [], lastSync: null };
    const rows = (await window.SupaHealth.getRecent(userId, days)) || [];
    let lastSync = null;
    if (rows.length) {
      try {
        const db = (typeof _db !== 'undefined') ? _db : window._db;
        if (db && db.from) {
          const { data } = await db.from('health_metrics').select('created_at').eq('user_id', userId)
            .order('created_at', { ascending: false }).limit(1);
          if (data && data[0]) lastSync = new Date(data[0].created_at).getTime();
        }
      } catch (_) {}
    }
    return { rows, lastSync };
  }

  // ── Resumen por día, último día y promedio de 14 días ──────────────────────────────────────────
  function digest(rows, lastSyncOverride) {
    const days = {};
    let lastSync = 0;
    for (const r of rows || []) {
      const m = METRICS[r.metric_type];
      if (!m) continue;
      const v = Number(r.value);
      if (!isFinite(v)) continue;
      const k = dayKey(r.start_ts);
      const d = days[k] = days[k] || { key: k, _n: {} };
      if (m.agg === 'mean') { d._n[m.key] = (d._n[m.key] || 0) + 1; d[m.key] = d[m.key] == null ? v : d[m.key] + (v - d[m.key]) / d._n[m.key]; }
      else d[m.key] = d[m.key] == null ? v : Math.max(d[m.key], v);
      const t = new Date(r.created_at || r.start_ts).getTime();
      if (t > lastSync) lastSync = t;
    }
    if (lastSyncOverride && lastSyncOverride > lastSync) lastSync = lastSyncOverride;
    const keys = Object.keys(days).sort();
    if (!keys.length) return { days, keys, latest: null, latestKey: null, avg: {}, count: {}, lastSync: null, hasData: false, elevated: false, hrNow: null, hrBase: null };
    const latestKey = keys[keys.length - 1], latest = days[latestKey];
    // Promedio de los 14 días anteriores al último día con datos (sin contarlo)
    const avg = {}, count = {}, from = shiftKey(latestKey, -14);
    for (const k of keys) {
      if (k >= latestKey || k < from) continue;
      for (const m of Object.values(METRICS)) {
        const v = days[k][m.key];
        if (v == null) continue;
        avg[m.key] = (avg[m.key] || 0) + v; count[m.key] = (count[m.key] || 0) + 1;
      }
    }
    for (const key of Object.keys(avg)) avg[key] = avg[key] / count[key];
    // Pulso elevado: misma regla que HealthCard — reposo de hoy > promedio de 14 d × 1.15, > 95 lpm y ≥ 4 días
    const hrSeries = keys.slice(-14).map(k => days[k].rhr != null ? days[k].rhr : days[k].hr).filter(v => v != null);
    const hrNow = latest.rhr != null ? latest.rhr : (latest.hr != null ? latest.hr : null);
    const hrBase = hrSeries.length ? hrSeries.reduce((a, b) => a + b, 0) / hrSeries.length : null;
    const elevated = hrNow != null && hrBase != null && hrSeries.length >= 4 && hrNow > hrBase * 1.15 && hrNow > 95;
    return { days, keys, latest, latestKey, avg, count, lastSync: lastSync || null, hasData: true, elevated, hrNow, hrBase };
  }

  // ── Ánimo por día (moodHook.log: { 'YYYY-MM-DD': [{ tone 1–10, mood, label, … }] }) ───────────
  function moodDays(log) {
    const out = {};
    for (const [k, list] of Object.entries(log || {})) {
      const arr = Array.isArray(list) ? list : [];
      const tones = arr.map(e => Number(e && e.tone)).filter(v => isFinite(v) && v > 0);
      if (!tones.length) continue;
      out[k] = {
        avg: tones.reduce((a, b) => a + b, 0) / tones.length,
        anx: arr.some(e => /ansios|triste/i.test(`${(e && e.mood) || ''} ${(e && e.label) || ''}`)),
      };
    }
    return out;
  }

  // ── Relación con el ánimo: solo afirmaciones con ≥ 5 días por grupo y diferencia clara ─────────
  function relations(dg, moodLog) {
    if (!dg || !dg.hasData) return [];
    const md = moodDays(moodLog);
    const both = dg.keys.filter(k => md[k]).map(k => ({ ...dg.days[k], mood: md[k].avg, anx: md[k].anx }));
    const mean = (arr, f) => arr.length ? arr.reduce((s, x) => s + f(x), 0) / arr.length : null;
    const part = (has, pred, field, minDiff) => {
      const pool = both.filter(x => has(x) && x[field] != null);
      const a = pool.filter(pred), b = pool.filter(x => !pred(x));
      if (a.length < 5 || b.length < 5) return null;
      const ma = mean(a, x => x[field]), mb = mean(b, x => x[field]);
      return Math.abs(ma - mb) >= minDiff ? { a: ma, b: mb, na: a.length, nb: b.length } : null;
    };
    const out = [];
    const steps = part(x => x.steps != null, x => x.steps > 6000, 'mood', 0.7);
    if (steps) out.push({ icon: '👟', text: `En los días con más de 6 000 pasos tu ánimo promedió ${fmt1(steps.a)}; con menos, ${fmt1(steps.b)}.` });
    const sleep = part(x => x.sleep != null, x => x.sleep < 6, 'mood', 0.7);
    if (sleep) out.push({ icon: '🌙', text: sleep.a < sleep.b
      ? `Cuando duermes menos de 6 h, tu ánimo baja ~${fmt1(sleep.b - sleep.a)} puntos (${fmt1(sleep.a)} frente a ${fmt1(sleep.b)}).`
      : `Con menos de 6 h de sueño tu ánimo promedió ${fmt1(sleep.a)}; con más descanso, ${fmt1(sleep.b)}.` });
    const hr = part(x => x.rhr != null, x => x.anx, 'rhr', 4);
    if (hr) out.push({ icon: '❤️', text: `Tu pulso en reposo fue ${hr.a > hr.b ? 'más alto' : 'más bajo'} en los días en que registraste ansiedad o tristeza (${Math.round(hr.a)} lpm frente a ${Math.round(hr.b)}).` });
    const mind = part(() => true, x => (x.mindful || 0) > 0, 'mood', 0.7);
    if (mind) out.push({ icon: '🧘', text: `En los días con minutos de atención plena tu ánimo promedió ${fmt1(mind.a)}; sin ellos, ${fmt1(mind.b)}.` });
    return out;
  }

  // ── Resumen corto para Sereni ──────────────────────────────────────────────────────────────────
  function summarize(rows, moodLog, opts) {
    const dg = digest(rows, opts && opts.lastSync);
    if (!dg.hasData) return null;
    const L = dg.latest, A = dg.avg, parts = [];
    const when = dayLabel(dg.latestKey).toLowerCase();
    if (L.rhr != null) parts.push(`pulso en reposo ${Math.round(L.rhr)} lpm` + (A.rhr != null
      ? ` (promedio ${Math.round(A.rhr)}${dg.elevated ? ': más alto de lo normal' : L.rhr > A.rhr * 1.08 ? ': algo más alto' : ''})` : ''));
    if (L.hr != null) parts.push(`pulso promedio ${Math.round(L.hr)} lpm`);
    if (L.steps != null) parts.push(`${fmtInt(L.steps)} pasos ${when}` + (A.steps != null
      ? ` (promedio ${fmtInt(A.steps)}${L.steps < A.steps * 0.7 ? ': día más quieto' : L.steps > A.steps * 1.3 ? ': día más activo' : ''})` : ''));
    if (L.sleep != null) parts.push(`durmió ${fmt1(L.sleep)} h` + (A.sleep != null
      ? ` (promedio ${fmt1(A.sleep)}${L.sleep < A.sleep - 1 ? ': durmió menos' : L.sleep < 6 ? ': poco descanso' : ''})`
      : (L.sleep < 6 ? ' (poco descanso)' : '')));
    if (L.hrv != null) parts.push(`HRV ${Math.round(L.hrv)} ms` + (A.hrv != null ? ` (promedio ${Math.round(A.hrv)})` : ''));
    if (L.cal != null) parts.push(`${fmtInt(L.cal)} kcal activas`);
    if ((L.mindful || 0) > 0) parts.push(`${Math.round(L.mindful)} min de atención plena`);
    if (!parts.length) return null;
    let text = `Salud (Apple Watch, sincronizado ${timeAgo(dg.lastSync)}): ${parts.join(', ')}.`;
    const rel = relations(dg, moodLog);
    if (rel.length) text += ` Relación observada: ${rel[0].text}`;
    return text;
  }

  // ── Interruptor «Compartir con Sereni» y escritura del resumen ─────────────────────────────────
  const shareKey = (uid) => `serenia_health_share_${uid}`;
  const summaryKey = (uid) => `serenia_health_summary_${uid}`;
  const isShareOn = (uid) => { try { return localStorage.getItem(shareKey(uid)) !== '0'; } catch (_) { return true; } };
  const setShare = (uid, on) => {
    try { localStorage.setItem(shareKey(uid), on ? '1' : '0'); if (!on) localStorage.removeItem(summaryKey(uid)); } catch (_) {}
  };
  const readMoodLog = (uid) => {
    try {
      const owner = localStorage.getItem('serenia_mood_log_owner');
      if (owner && uid && owner !== uid) return {};
      return JSON.parse(localStorage.getItem('serenia_mood_log') || '{}');
    } catch (_) { return {}; }
  };
  // Escribe (o borra) serenia_health_summary_<uid>. Devuelve el texto escrito o null.
  function writeSummary(uid, rows, moodLog, lastSync) {
    if (!uid) return null;
    try {
      if (!isShareOn(uid)) { localStorage.removeItem(summaryKey(uid)); return null; }
      const text = summarize(rows, moodLog || readMoodLog(uid), { lastSync });
      if (!text) { localStorage.removeItem(summaryKey(uid)); return null; }
      localStorage.setItem(summaryKey(uid), JSON.stringify({ at: Date.now(), text }));
      return text;
    } catch (_) { return null; }
  }

  window.SereniaHealth = { METRICS, dayKey, dayLabel, timeAgo, load, digest, moodDays, relations, summarize,
    shareKey, summaryKey, isShareOn, setShare, writeSummary, fmtInt, fmt1 };

  // ══════════════════════════════════════════════════════════════════════════════════════════════
  // UI
  // ══════════════════════════════════════════════════════════════════════════════════════════════
  const colorOf = (type, p) => ({
    resting_heart_rate: p.accent, heart_rate: p.accent, hrv: p.shellBlue, steps: p.primary,
    active_calories: p.primaryDeep, sleep_hours: p.shellBlue, mindful_minutes: p.primary,
  })[type] || p.primary;

  const Section = ({ p, title, hint, style }) => (
    <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', gap: 10, ...style }}>
      <div style={{ fontSize: 17, fontFamily: 'Fraunces, serif', fontWeight: 500, color: p.ink }}>{title}</div>
      {hint && <div style={{ fontSize: 12, color: p.inkMute, textAlign: 'right' }}>{hint}</div>}
    </div>
  );

  function Toggle({ p, on, onClick, label }) {
    return (
      <button onClick={onClick} role="switch" aria-checked={on} aria-label={label} style={{
        width: 46, height: 27, borderRadius: 999, border: 'none', padding: 2, flexShrink: 0,
        background: on ? p.primary : p.line, cursor: 'pointer', transition: 'background .2s',
      }}>
        <span style={{
          display: 'block', width: 23, height: 23, borderRadius: '50%', background: '#fff',
          transform: `translateX(${on ? 19 : 0}px)`, transition: 'transform .2s', boxShadow: '0 1px 3px rgba(0,0,0,.25)',
        }} />
      </button>
    );
  }

  function MetricCard({ p, type, value, avg, elevated }) {
    const m = METRICS[type];
    const has = value != null;
    const fmt = (v) => m.key === 'sleep' ? fmt1(v) : fmtInt(v);
    const diff = has && avg != null && avg > 0 ? (value - avg) / avg : null;
    let cmp = has ? 'sin promedio aún' : 'sin dato este día', tone = p.inkMute, arrow = '';
    if (diff != null) {
      const pct = Math.round(Math.abs(diff) * 100);
      if (pct < 3) cmp = 'igual que tus 14 días';
      else {
        const up = diff > 0, good = m.better === 'up' ? up : !up;
        arrow = up ? '↑' : '↓';
        tone = good ? p.primaryDeep : '#B5654F';
        cmp = `${pct} % vs. tus 14 días`;
      }
    }
    const color = colorOf(type, p);
    return (
      <div style={{
        background: p.surface, border: `1px solid ${elevated ? '#C0392B66' : p.line}`, borderRadius: 16,
        padding: '12px 12px 11px', position: 'relative', overflow: 'hidden', minHeight: 96,
      }}>
        <div style={{ position: 'absolute', top: -14, right: -14, width: 48, height: 48, borderRadius: '50%', background: `${color}1a` }} />
        <div style={{ fontSize: 10, color: p.inkMute, fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.06em', position: 'relative' }}>{m.label}</div>
        <div style={{ display: 'flex', alignItems: 'baseline', gap: 4, marginTop: 6, color: elevated ? '#C0392B' : p.ink, position: 'relative' }}>
          <span style={{ fontSize: 24, fontFamily: 'Fraunces, serif', fontWeight: 500, letterSpacing: '-0.01em', lineHeight: 1 }}>{has ? fmt(value) : '—'}</span>
          <span style={{ fontSize: 11, color: p.inkMute }}>{m.unit}</span>
        </div>
        <div style={{ fontSize: 11, color: tone, marginTop: 7, fontWeight: 500 }}>{arrow ? `${arrow} ${cmp}` : cmp}</div>
        {has && avg != null && <div style={{ fontSize: 10, color: p.inkMute, marginTop: 2 }}>promedio {fmt(avg)} {m.unit}</div>}
      </div>
    );
  }

  // Curva suave (Catmull-Rom → Bézier), como WeeklyMoodChart
  const smoothPath = (pts) => {
    if (!pts.length) return '';
    if (pts.length === 1) return `M${pts[0].x} ${pts[0].y}`;
    let d = `M${pts[0].x} ${pts[0].y}`;
    for (let i = 0; i < pts.length - 1; i++) {
      const p0 = pts[i - 1] || pts[i], p1 = pts[i], p2 = pts[i + 1], p3 = pts[i + 2] || p2;
      d += ` C${p1.x + (p2.x - p0.x) / 6} ${p1.y + (p2.y - p0.y) / 6}, ${p2.x - (p3.x - p1.x) / 6} ${p2.y - (p3.y - p1.y) / 6}, ${p2.x} ${p2.y}`;
    }
    return d;
  };

  // Gráfica de 14 días (SVG propio): barras o línea de la métrica + ánimo promedio del día (1–10) encima
  function HealthChart({ p, title, unit, kind, days, values, mood, color, fmt, highlight }) {
    const W = 320, H = 150, padL = 32, padR = 24, padT = 12, padB = 22;
    const x0 = padL, x1 = W - padR, y0 = padT, y1 = H - padB, iw = x1 - x0, ih = y1 - y0;
    const n = days.length, slot = iw / n;
    const vals = days.map(k => values[k]).filter(v => v != null);
    const vmax = vals.length ? Math.max(...vals) : 1, vmin = vals.length ? Math.min(...vals) : 0;
    const lo = kind === 'line' ? Math.max(0, Math.floor((vmin - 4) / 5) * 5) : 0;
    const hi = kind === 'line' ? Math.ceil((vmax + 4) / 5) * 5 : (vmax > 0 ? vmax * 1.08 : 1);
    const yOf = (v) => y1 - ((v - lo) / ((hi - lo) || 1)) * ih;
    const xOf = (i) => x0 + slot * i + slot / 2;
    const linePts = days.map((k, i) => values[k] != null ? { x: xOf(i), y: yOf(values[k]), v: values[k], k } : null).filter(Boolean);
    const moodPts = days.map((k, i) => mood[k] != null ? { x: xOf(i), y: y1 - ((mood[k] - 1) / 9) * ih, v: mood[k] } : null).filter(Boolean);
    const isHot = (v) => !!(highlight && highlight(v));
    return (
      <div style={{ background: p.surface, border: `1px solid ${p.line}`, borderRadius: 18, padding: '14px 14px 10px', minWidth: 0 }}>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline' }}>
          <div style={{ fontFamily: 'Fraunces, serif', fontSize: 15, color: p.ink }}>{title}</div>
          <div style={{ fontSize: 10, color: p.inkMute }}>{unit}</div>
        </div>
        <svg viewBox={`0 0 ${W} ${H}`} width="100%" style={{ display: 'block', marginTop: 6, overflow: 'visible' }} role="img" aria-label={`${title}, últimos 14 días`}>
          {[0, 0.5, 1].map(f => (
            <line key={f} x1={x0} x2={x1} y1={y1 - ih * f} y2={y1 - ih * f} stroke={p.line} strokeWidth="1" strokeDasharray={f ? '2 3' : ''} />
          ))}
          <text x={x0 - 5} y={y0 + 4} fontSize="8" fill={p.inkMute} textAnchor="end">{fmt(hi)}</text>
          <text x={x0 - 5} y={y1 + 3} fontSize="8" fill={p.inkMute} textAnchor="end">{fmt(lo)}</text>
          <text x={x1 + 5} y={y0 + 4} fontSize="8" fill={p.accent} fontWeight="600">10</text>
          <text x={x1 + 5} y={y1 + 3} fontSize="8" fill={p.accent} fontWeight="600">1</text>
          {kind === 'bars' && linePts.map(pt => (
            <rect key={pt.k} x={pt.x - slot * 0.32} y={pt.y} width={slot * 0.64} height={Math.max(1.5, y1 - pt.y)} rx="3"
              fill={isHot(pt.v) ? '#C0392B' : color} opacity="0.82" />
          ))}
          {kind === 'line' && linePts.length >= 2 && (
            <React.Fragment>
              <path d={`${smoothPath(linePts)} L${linePts[linePts.length - 1].x} ${y1} L${linePts[0].x} ${y1} Z`} fill={color} opacity="0.12" />
              <path d={smoothPath(linePts)} fill="none" stroke={color} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
            </React.Fragment>
          )}
          {kind === 'line' && linePts.map(pt => (
            <circle key={pt.k} cx={pt.x} cy={pt.y} r={isHot(pt.v) ? 3.6 : 2.3} fill={isHot(pt.v) ? '#C0392B' : color} />
          ))}
          {moodPts.length >= 2 && (
            <path d={smoothPath(moodPts)} fill="none" stroke={p.accent} strokeWidth="1.8" strokeDasharray="4 3" strokeLinecap="round" />
          )}
          {moodPts.map((pt, i) => <circle key={i} cx={pt.x} cy={pt.y} r="2.7" fill={p.surface} stroke={p.accent} strokeWidth="1.6" />)}
          {days.map((k, i) => (i % 2 === (n - 1) % 2) && (
            <text key={k} x={xOf(i)} y={H - 6} fontSize="8" fill={p.inkMute} textAnchor="middle">{Number(k.slice(8, 10))}</text>
          ))}
        </svg>
        <div style={{ display: 'flex', gap: 14, fontSize: 10, color: p.inkMute, marginTop: 2, flexWrap: 'wrap' }}>
          <span><span style={{ display: 'inline-block', width: 10, height: 6, background: color, borderRadius: 2, marginRight: 5, verticalAlign: 'middle' }} />{title}</span>
          <span><span style={{ display: 'inline-block', width: 14, borderTop: `2px dashed ${p.accent}`, marginRight: 5, verticalAlign: 'middle' }} />ánimo del día (1–10)</span>
        </div>
      </div>
    );
  }

  function CodeBox({ p, code, copied, onCopy, compact }) {
    return (
      <div style={{ display: 'flex', alignItems: 'center', gap: 10, background: p.bg, borderRadius: 14, padding: compact ? '8px 12px' : '10px 12px', border: `1px solid ${p.line}` }}>
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{ fontSize: 10, color: p.inkMute, textTransform: 'uppercase', letterSpacing: '0.06em', fontWeight: 600 }}>Tu código personal</div>
          <div style={{ fontFamily: 'Fraunces, serif', fontSize: compact ? 18 : 22, color: p.ink, letterSpacing: '0.04em', marginTop: 1 }}>{code || '…'}</div>
        </div>
        <button onClick={onCopy} disabled={!code} style={{
          border: 'none', borderRadius: 10, padding: '9px 13px', background: copied ? p.primarySoft : p.primary,
          color: copied ? p.primaryDeep : '#fff', fontSize: 12, fontWeight: 600, cursor: code ? 'pointer' : 'default', flexShrink: 0,
        }}>{copied ? 'Copiado ✓' : 'Copiar código'}</button>
      </div>
    );
  }

  function SaludScreen({ p, userId, moodHook, onBack, onOpenChat }) {
    const Turtle = window.Turtle;
    const [state, setState] = React.useState({ rows: null, lastSync: null, error: false });
    const [share, setShareState] = React.useState(() => isShareOn(userId));
    const [code, setCode] = React.useState(null);
    const [copied, setCopied] = React.useState(false);
    const [sosSent, setSosSent] = React.useState(false);
    const [summaryText, setSummaryText] = React.useState(null);
    const fallbackLog = React.useMemo(() => readMoodLog(userId), [userId]);
    const moodLog = (moodHook && moodHook.log) || fallbackLog;

    React.useEffect(() => {
      let cancelled = false;
      setState({ rows: null, lastSync: null, error: false });
      setShareState(isShareOn(userId));
      load(userId, 28)
        .then(r => { if (!cancelled) setState({ rows: r.rows || [], lastSync: r.lastSync, error: false }); })
        .catch(() => { if (!cancelled) setState({ rows: [], lastSync: null, error: true }); });
      if (userId && window.SupaProfile) {
        window.SupaProfile.get(userId).then(pr => { if (!cancelled && pr) setCode(pr.personal_code || null); }).catch(() => {});
      }
      return () => { cancelled = true; };
    }, [userId]);

    const dg = React.useMemo(() => state.rows ? digest(state.rows, state.lastSync) : null, [state]);
    const rel = React.useMemo(() => (dg && dg.hasData) ? relations(dg, moodLog) : [], [dg, moodLog]);
    const md = React.useMemo(() => moodDays(moodLog), [moodLog]);

    // Resumen para Sereni: se escribe o se borra cada vez que cambian los datos o el interruptor
    React.useEffect(() => {
      if (!state.rows) return;
      setSummaryText(writeSummary(userId, state.rows, moodLog, state.lastSync));
    }, [state, share, userId, moodLog]);

    const toggleShare = () => {
      const next = !share;
      setShare(userId, next); setShareState(next);
      if (window.haptic) window.haptic('light');
    };
    const copyCode = async () => {
      if (!code) return;
      try { await navigator.clipboard.writeText(code); }
      catch (_) {
        try { const ta = document.createElement('textarea'); ta.value = code; document.body.appendChild(ta); ta.select(); document.execCommand('copy'); ta.remove(); } catch (_2) {}
      }
      setCopied(true); if (window.haptic) window.haptic('success');
      setTimeout(() => setCopied(false), 2000);
    };
    const notifyFriends = async () => {
      if (!userId || !window.SupaSOS || sosSent || !dg) return;
      if (window.haptic) window.haptic('sos');
      try {
        await window.SupaSOS.trigger(userId, 'close', `⌚ Aviso automático: mi pulso está más alto de lo normal (${Math.round(dg.hrNow)} lpm). ¿Me acompañas un momento?`);
      } catch (_) {}
      setSosSent(true);
    };
    const breathe = () => { if (window.haptic) window.haptic('light'); if (onOpenChat) onOpenChat(); };

    const loading = state.rows === null;
    const hasData = !!(dg && dg.hasData);
    const chartDays = React.useMemo(() => Array.from({ length: 14 }, (_, i) => shiftKey(localToday(), i - 13)), []);
    const seriesOf = (key) => { const o = {}; if (dg) for (const k of chartDays) o[k] = dg.days[k] ? (dg.days[k][key] != null ? dg.days[k][key] : null) : null; return o; };
    const moodSeries = React.useMemo(() => { const o = {}; for (const k of chartDays) o[k] = md[k] ? md[k].avg : null; return o; }, [md, chartDays]);

    const btn = (primary) => ({
      border: 'none', borderRadius: 10, padding: '9px 14px', fontSize: 12, fontWeight: 600, cursor: 'pointer',
      background: primary ? '#C0392B' : p.surface, color: primary ? '#fff' : p.primaryDeep,
      boxShadow: primary ? 'none' : `0 0 0 1px ${p.line}`,
    });

    return (
      <div style={{ height: '100%', display: 'flex', flexDirection: 'column', background: p.bg, fontFamily: '"Inter", system-ui, sans-serif' }}>
        <style>{`
          .ser-salud-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 10px; }
          .ser-salud-charts { display: grid; grid-template-columns: repeat(auto-fit, minmax(290px, 1fr)); gap: 12px; }
          .ser-salud-screen button:active { transform: scale(.97); }
          .ser-salud-screen ol li { margin-bottom: 4px; }
        `}</style>

        {/* Cabecera */}
        <div style={{ padding: '10px 18px 12px', borderBottom: `1px solid ${p.line}`, flexShrink: 0, display: 'flex', alignItems: 'center', gap: 10 }}>
          <button onClick={onBack} aria-label="Atrás" style={{ background: 'transparent', border: 'none', cursor: 'pointer', color: p.primaryDeep, fontSize: 15, fontWeight: 600, padding: '4px 0', flexShrink: 0 }}>‹ Atrás</button>
          <div style={{ flex: 1, minWidth: 0 }}>
            <div style={{ fontFamily: 'Fraunces, serif', fontSize: 20, fontWeight: 500, color: p.ink, letterSpacing: '-0.01em', lineHeight: 1.1 }}>Salud</div>
            <div style={{ fontSize: 12, color: p.inkMute, marginTop: 2, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>desde tu Apple Watch</div>
          </div>
          <div title={hasData ? `Última sincronización ${timeAgo(dg.lastSync)}` : undefined} style={{
            fontSize: 11, fontWeight: 600, padding: '5px 10px', borderRadius: 999, flexShrink: 0, whiteSpace: 'nowrap',
            background: hasData ? p.primarySoft : p.surface, color: hasData ? p.primaryDeep : p.inkMute, border: `1px solid ${hasData ? 'transparent' : p.line}`,
          }}>
            {loading ? 'Leyendo…' : hasData ? `Sincronizado ${timeAgo(dg.lastSync)}` : 'Sin datos aún'}
          </div>
        </div>

        <div className="ser-salud-screen" style={{ flex: 1, overflowY: 'auto', overflowX: 'hidden', padding: '14px 18px 36px' }}>
          {loading && (
            <div style={{ textAlign: 'center', padding: '60px 20px', color: p.inkMute }}>
              {Turtle && <Turtle pose="rest" size={64} animate style={{ margin: '0 auto' }} />}
              <div style={{ marginTop: 10, fontSize: 13 }}>Leyendo los datos de tu reloj…</div>
            </div>
          )}

          {!loading && !hasData && (
            <div style={{ background: p.surface, border: `1px dashed ${p.line}`, borderRadius: 20, padding: '18px 18px 16px' }}>
              <div style={{ display: 'flex', gap: 12, alignItems: 'center' }}>
                {Turtle && <Turtle pose="sit" size={64} animate />}
                <div style={{ minWidth: 0 }}>
                  <div style={{ fontFamily: 'Fraunces, serif', fontSize: 18, color: p.ink }}>Conecta tu Apple Watch ⌚</div>
                  <div style={{ fontSize: 12, color: p.inkSoft, marginTop: 3, lineHeight: 1.45 }}>Pulso, pasos, sueño y su relación con tu ánimo — aquí y en Tendencias.</div>
                </div>
              </div>
              {state.error && <div style={{ marginTop: 10, fontSize: 12, color: '#B5654F' }}>No pudimos leer tus datos ahora mismo. Revisa tu conexión e inténtalo de nuevo.</div>}
              <ol style={{ margin: '14px 0 0', paddingLeft: 18, fontSize: 13, color: p.ink, lineHeight: 1.55 }}>
                <li>En tu iPhone abre <b>Atajos</b> y crea el atajo <b>«Serenia Salud»</b> (guía: <i>CONECTAR-APPLE-WATCH.md</i>).</li>
                <li>Pega en el atajo tu <b>código personal</b>: es lo que identifica tus datos.</li>
                <li>Ejecútalo una vez y acepta el permiso de <b>Salud</b>.</li>
                <li>Prográmalo a las 9, 13, 17 y 21 h para que se actualice solo.</li>
              </ol>
              <div style={{ marginTop: 14 }}>
                <CodeBox p={p} code={code} copied={copied} onCopy={copyCode} />
              </div>
            </div>
          )}

          {!loading && hasData && (
            <React.Fragment>
              {dg.elevated && (
                <div style={{ background: '#C0392B14', border: '1px solid #C0392B40', borderRadius: 16, padding: '14px 16px', marginBottom: 16 }}>
                  <div style={{ fontSize: 14, fontWeight: 600, color: p.ink }}>Tu pulso en reposo está más alto de lo normal</div>
                  <div style={{ fontSize: 12, color: p.inkSoft, marginTop: 4, lineHeight: 1.5 }}>
                    {Math.round(dg.hrNow)} lpm frente a tu promedio de {Math.round(dg.hrBase)} lpm. Puede ser cansancio, una mala noche o un día tenso.
                    ¿Respiramos un momento? Y si quieres compañía, avísale a alguien cercano.
                  </div>
                  <div style={{ display: 'flex', gap: 8, marginTop: 12, flexWrap: 'wrap' }}>
                    <button onClick={notifyFriends} disabled={sosSent} style={{ ...btn(true), background: sosSent ? p.line : '#C0392B', color: sosSent ? p.inkSoft : '#fff', cursor: sosSent ? 'default' : 'pointer' }}>
                      {sosSent ? 'Aviso enviado ✓' : 'Avisar a mis contactos cercanos'}
                    </button>
                    {onOpenChat && <button onClick={breathe} style={btn(false)}>Respirar con Sereni 🐢</button>}
                  </div>
                </div>
              )}

              <Section p={p} title={dayLabel(dg.latestKey)} hint="comparado con tus 14 días anteriores" />
              <div className="ser-salud-grid" style={{ marginTop: 10 }}>
                {CARD_ORDER.map(t => {
                  const m = METRICS[t];
                  return <MetricCard key={t} p={p} type={t} value={dg.latest[m.key]} avg={dg.avg[m.key]} elevated={t === 'resting_heart_rate' && dg.elevated} />;
                })}
              </div>

              <Section p={p} title="Últimos 14 días" hint="con tu ánimo encima" style={{ marginTop: 22 }} />
              <div className="ser-salud-charts" style={{ marginTop: 10 }}>
                <HealthChart p={p} title="Pasos" unit="pasos por día" kind="bars" days={chartDays} values={seriesOf('steps')} mood={moodSeries} color={p.primary} fmt={fmtInt} />
                <HealthChart p={p} title="Pulso en reposo" unit="lpm" kind="line" days={chartDays} values={seriesOf('rhr')} mood={moodSeries} color={p.primaryDeep} fmt={fmtInt} highlight={v => v > 95} />
                <HealthChart p={p} title="Sueño" unit="horas por noche" kind="bars" days={chartDays} values={seriesOf('sleep')} mood={moodSeries} color={p.shellBlue} fmt={fmt1} />
              </div>

              <Section p={p} title="Relación con tu ánimo" hint="últimas semanas" style={{ marginTop: 22 }} />
              <div style={{ marginTop: 10, background: p.surface, border: `1px solid ${p.line}`, borderRadius: 18, padding: '12px 16px 14px' }}>
                {rel.length ? rel.map((r, i) => (
                  <div key={i} style={{ display: 'flex', gap: 12, alignItems: 'flex-start', padding: '9px 0', borderTop: i ? `1px solid ${p.line}` : 'none' }}>
                    <div style={{ width: 32, height: 32, borderRadius: 10, background: p.primarySoft, display: 'grid', placeItems: 'center', fontSize: 15, flexShrink: 0 }}>{r.icon}</div>
                    <div style={{ fontSize: 13, color: p.ink, lineHeight: 1.5 }}>{r.text}</div>
                  </div>
                )) : (
                  <div style={{ display: 'flex', gap: 12, alignItems: 'center', padding: '4px 0' }}>
                    {Turtle && <Turtle pose="rest" size={56} />}
                    <div style={{ fontSize: 13, color: p.inkSoft, lineHeight: 1.5 }}>
                      Aún no hay suficientes días para ver una relación. Sigue registrando tu ánimo y sincronizando el reloj: con unas dos semanas de ambos aparecerá aquí.
                    </div>
                  </div>
                )}
                <div style={{ fontSize: 11, color: p.inkMute, marginTop: 10, lineHeight: 1.45, borderTop: `1px solid ${p.line}`, paddingTop: 10 }}>
                  Esto no es un diagnóstico: solo muestra cómo se acompañan tu descanso, tu movimiento y tu ánimo. Si algo te preocupa, Sereni está para escucharte.
                </div>
              </div>

              <div style={{ marginTop: 16, background: p.surface, border: `1px solid ${p.line}`, borderRadius: 18, padding: '14px 16px' }}>
                <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
                  <div style={{ width: 38, height: 38, borderRadius: '50%', background: p.primarySoft, display: 'grid', placeItems: 'center', fontSize: 19, flexShrink: 0 }}>🐢</div>
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div style={{ fontSize: 14, fontWeight: 600, color: p.ink }}>Compartir con Sereni</div>
                    <div style={{ fontSize: 11, color: p.inkMute, marginTop: 1, lineHeight: 1.4 }}>Recibe un resumen corto de tu día (pulso, pasos, sueño) para acompañarte mejor. Nunca diagnostica.</div>
                  </div>
                  <Toggle p={p} on={share} onClick={toggleShare} label="Compartir con Sereni" />
                </div>
                {share && summaryText && (
                  <div style={{ marginTop: 10, fontSize: 12, color: p.inkSoft, lineHeight: 1.5, background: p.bg, borderRadius: 12, padding: '10px 12px' }}>
                    <span style={{ fontSize: 10, color: p.inkMute, fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.06em', display: 'block', marginBottom: 3 }}>Lo que verá Sereni</span>
                    {summaryText}
                  </div>
                )}
                {!share && <div style={{ marginTop: 10, fontSize: 12, color: p.inkMute }}>Sereni no verá tus datos de salud.</div>}
              </div>

              <div style={{ marginTop: 16 }}>
                <CodeBox p={p} code={code} copied={copied} onCopy={copyCode} compact />
                <div style={{ fontSize: 11, color: p.inkMute, marginTop: 8, lineHeight: 1.45 }}>
                  Los datos llegan del atajo «Serenia Salud» de tu iPhone. ¿Cambiaste de teléfono? Vuelve a pegar este código en el atajo.
                </div>
              </div>
            </React.Fragment>
          )}
        </div>
      </div>
    );
  }

  window.SaludScreen = SaludScreen;
})();
