// Composants partagés — Dashboard Livana

const { useState, useRef, useEffect, useMemo } = React;

const initials = (first, last) => `${(first||'')[0]||''}${(last||'')[0]||''}`.toUpperCase();

// Avatar with optional mood ring
function Avatar({ first, last, mood, size = 42, className = '' }) {
  const m = mood ? window.moodOf(mood) : null;
  const style = { width: size, height: size, fontSize: size * 0.32, '--mc': m ? m.color : 'transparent' };
  return (
    <span className={`avatar ${className}`} style={style}>
      {m && <span className="ring"></span>}
      {initials(first, last)}
    </span>
  );
}

function MoodPill({ mood, tinted = true }) {
  const m = window.moodOf(mood);
  return (
    <span className={`mood-pill ${tinted ? 'tinted' : ''}`} style={{ '--mc': m.color }}>
      <span className="e">{m.emoji}</span>{m.short}
    </span>
  );
}

function TrendBadge({ trend }) {
  const map = {
    up:   { cls: 'up',   t: '↑ En progrès', },
    down: { cls: 'down', t: '↓ En recul' },
    flat: { cls: 'flat', t: '→ Stable' },
  };
  const x = map[trend] || map.flat;
  return <span className={`trend ${x.cls}`}>{x.t}</span>;
}

// Full mood evolution chart
function MoodChart({ timeline, big = false }) {
  const [tip, setTip] = useState(null);
  if (!timeline || timeline.length === 0) {
    return <div style={{ padding: '32px 0', textAlign: 'center', color: 'var(--text-3)', fontSize: 13 }}>Aucune donnée d'humeur — premier appel à programmer.</div>;
  }
  const W = 640, H = big ? 320 : 200, padL = 16, padR = 16, padT = 18, padB = 30;
  const innerW = W - padL - padR, innerH = H - padT - padB;
  // Un point par jour. `mood: null` = aucun appel ce jour-là : l'emplacement
  // reste sur l'axe (le temps continue) mais la courbe s'interrompt.
  const pts = timeline.map((d) => {
    const vide = d.mood == null;
    return {
      ...d,
      vide: vide,
      m: vide ? null : window.moodOf(d.mood),
      score: vide ? null : (d.score != null ? d.score : window.moodOf(d.mood).score),
    };
  });
  const stepX = pts.length > 1 ? innerW / (pts.length - 1) : 0;
  const X = (i) => padL + i * stepX;
  const Y = (v) => padT + innerH - ((v - 1) / 4) * innerH;

  // Segments continus : la ligne ne traverse pas les jours sans appel
  const segments = [];
  let courant = [];
  pts.forEach((p, i) => {
    if (p.vide) { if (courant.length) segments.push(courant); courant = []; }
    else courant.push({ p: p, i: i });
  });
  if (courant.length) segments.push(courant);
  const cheminSegment = (seg) => seg.map((e, k) => `${k === 0 ? 'M' : 'L'} ${X(e.i)} ${Y(e.p.score)}`).join(' ');

  // Axe : au plus ~8 étiquettes pour rester lisible sur les longues périodes
  const pasEtiquette = Math.max(1, Math.ceil(pts.length / 8));
  const bands = [
    { y: 5, label: 'Joyeux' }, { y: 4, label: 'Apaisé' }, { y: 3, label: '' },
    { y: 2, label: 'Anxieux' }, { y: 1, label: 'Agité' },
  ];
  return (
    <div className="chart-wrap">
      <svg className="chart-svg" viewBox={`0 0 ${W} ${H}`} onMouseLeave={() => setTip(null)}>
        <defs>
          <linearGradient id="moodArea" x1="0" y1="0" x2="0" y2="1">
            <stop offset="0%" stopColor="var(--accent)" stopOpacity="0.18" />
            <stop offset="100%" stopColor="var(--accent)" stopOpacity="0" />
          </linearGradient>
        </defs>
        {bands.map((b) => (
          <g key={b.y}>
            <line x1={padL} x2={W - padR} y1={Y(b.y)} y2={Y(b.y)} stroke="var(--border)" strokeWidth="1" strokeDasharray={b.y === 3 ? '0' : '3 4'} opacity={b.y === 3 ? 0.9 : 0.6} />
          </g>
        ))}
        {segments.map((seg, si) => (
          <path key={'a' + si}
            d={`${cheminSegment(seg)} L ${X(seg[seg.length - 1].i)} ${padT + innerH} L ${X(seg[0].i)} ${padT + innerH} Z`}
            fill="url(#moodArea)" />
        ))}
        {segments.map((seg, si) => (
          <path key={'l' + si} d={cheminSegment(seg)} fill="none" stroke="var(--accent)"
            strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" />
        ))}
        {pts.map((p, i) => (
          <g key={i}>
            {/* point plein : la couleur de l'humeur se lit d'un coup d'œil.
                Jour sans appel : petit repère creux sur la ligne médiane. */}
            {p.vide ? (
              <circle cx={X(i)} cy={Y(3)} r="2.5" fill="none" stroke="var(--border-strong)" strokeWidth="1.5"
                onMouseEnter={() => setTip({ x: X(i) / W * 100, y: Y(3) / H * 100, p })}
                style={{ cursor: 'default' }} />
            ) : (
              <circle cx={X(i)} cy={Y(p.score)} r="5.5" fill={p.m.color} stroke="var(--surface)" strokeWidth="2"
                onMouseEnter={() => setTip({ x: X(i) / W * 100, y: Y(p.score) / H * 100, p })}
                style={{ cursor: 'pointer' }} />
            )}
            {i % pasEtiquette === 0 &&
              <text x={X(i)} y={H - 8} textAnchor="middle" fontSize="10.5" fill="var(--text-3)">{p.date}</text>}
          </g>
        ))}
      </svg>
      {tip && (
        <div className="chart-tip show" style={{ left: `${tip.x}%`, top: `${tip.y}%` }}>
          {tip.p.vide
            ? `${tip.p.date} · aucun appel`
            : `${tip.p.m.emoji} ${tip.p.m.label} · ${tip.p.date}${tip.p.appels > 1 ? ` · ${tip.p.appels} appels` : ''}`}
        </div>
      )}
      {/* Légende ordonnée du meilleur au moins bon, avec des flèches aux
          extrémités : la hauteur du point sur la courbe se lit d'emblée. */}
      <div className="chart-legend">
        {Object.entries(window.MOODS)
          .sort((a, b) => b[1].score - a[1].score)
          .map(([k, m], i, tab) => (
            <span key={k}>
              <span className="dot" style={{ background: m.color }}></span>
              {m.label}
              {i === 0 && <span className="legend-arrow" title="Le plus haut sur la courbe"> ↑</span>}
              {i === tab.length - 1 && <span className="legend-arrow" title="Le plus bas sur la courbe"> ↓</span>}
            </span>
          ))}
      </div>
    </div>
  );
}

// PIN modal — 4-digit unlock
function PinModal({ onClose, onSuccess, expected = '1234' }) {
  const [digits, setDigits] = useState(['', '', '', '']);
  const [err, setErr] = useState(false);
  const [shake, setShake] = useState(false);
  const refs = [useRef(null), useRef(null), useRef(null), useRef(null)];

  useEffect(() => { setTimeout(() => refs[0].current && refs[0].current.focus(), 80); }, []);

  const submit = (val) => {
    if (val === expected) { onSuccess(); }
    else {
      setErr(true); setShake(true);
      setTimeout(() => {
        setShake(false); setDigits(['', '', '', '']);
        refs[0].current && refs[0].current.focus();
      }, 450);
    }
  };

  const setAt = (i, v) => {
    setDigits((d) => { const n = [...d]; n[i] = v; return n; });
  };

  const onChange = (i, e) => {
    const v = e.target.value.replace(/\D/g, '');
    setErr(false);
    if (v.length > 1) {
      // pasted / typed multiple
      const arr = v.slice(0, 4).split('');
      const next = ['', '', '', ''];
      arr.forEach((c, k) => { next[k] = c; });
      setDigits(next);
      const last = Math.min(arr.length, 4) - 1;
      if (arr.length >= 4) setTimeout(() => submit(next.join('')), 120);
      else refs[last + 1] && refs[last + 1].current && refs[last + 1].current.focus();
      return;
    }
    setAt(i, v);
    if (v && i < 3) refs[i + 1].current && refs[i + 1].current.focus();
    if (v && i === 3) {
      const full = [...digits.slice(0, 3), v].join('');
      if (full.length === 4) setTimeout(() => submit(full), 120);
    }
  };

  const onKeyDown = (i, e) => {
    if (e.key === 'Escape') { onClose(); return; }
    if (e.key === 'Backspace') {
      if (digits[i]) { setAt(i, ''); }
      else if (i > 0) { refs[i - 1].current && refs[i - 1].current.focus(); setAt(i - 1, ''); }
      setErr(false);
    } else if (e.key === 'ArrowLeft' && i > 0) {
      refs[i - 1].current && refs[i - 1].current.focus();
    } else if (e.key === 'ArrowRight' && i < 3) {
      refs[i + 1].current && refs[i + 1].current.focus();
    }
  };

  return (
    <div className="pin-modal-backdrop" onMouseDown={(e) => { if (e.target === e.currentTarget) onClose(); }}>
      <div className={`pin-modal ${shake ? 'shake' : ''}`}>
        <div className="pin-ic"><Icon name="lock" size={24} /></div>
        <div className="pin-title">Contenu confidentiel</div>
        <div className="pin-sub">Saisissez votre code soignant à 4 chiffres pour afficher le résumé et la transcription.</div>
        <div className="pin-inputs">
          {[0, 1, 2, 3].map((i) => (
            <input
              key={i}
              ref={refs[i]}
              className={`pin-box ${digits[i] ? 'filled' : ''} ${err ? 'err' : ''}`}
              type="password"
              inputMode="numeric"
              autoComplete="off"
              maxLength={i === 0 ? 4 : 1}
              value={digits[i]}
              onChange={(e) => onChange(i, e)}
              onKeyDown={(e) => onKeyDown(i, e)}
              onFocus={(e) => e.target.select()}
            />
          ))}
        </div>
        <div className="pin-err">{err ? 'Code incorrect, réessayez.' : ''}</div>
        <div className="pin-actions">
          <button className="btn btn-ghost btn-sm" onClick={onClose}>Annuler</button>
        </div>
      </div>
    </div>
  );
}

// Appels acceptés vs refusés — barres groupées
function CallsChart({ data }) {
  const [tip, setTip] = useState(null);
  const W = 620, H = 220, padL = 14, padR = 14, padT = 16, padB = 30;
  const innerW = W - padL - padR, innerH = H - padT - padB;
  const max = Math.max(...data.map((d) => d.accepted + d.refused), 1);
  const groupW = innerW / data.length;
  const barW = Math.min(26, groupW * 0.3);
  const gap = 7;
  const Y = (v) => padT + innerH - (v / max) * innerH;
  const totalAcc = data.reduce((s, d) => s + d.accepted, 0);
  const totalRef = data.reduce((s, d) => s + d.refused, 0);
  const rate = Math.round((totalAcc / (totalAcc + totalRef)) * 100);

  return (
    <div className="chart-wrap">
      <div className="calls-chart-legend">
        <span className="ccl-item"><span className="dot" style={{ background: 'var(--accent)' }}></span>Acceptés <b>{totalAcc}</b></span>
        <span className="ccl-item"><span className="dot" style={{ background: 'var(--border-strong)' }}></span>Refusés <b>{totalRef}</b></span>
        <span className="ccl-rate">{rate}% d'acceptation</span>
      </div>
      <svg className="chart-svg" viewBox={`0 0 ${W} ${H}`} onMouseLeave={() => setTip(null)}>
        {[0, 0.25, 0.5, 0.75, 1].map((f) => (
          <line key={f} x1={padL} x2={W - padR} y1={padT + innerH * (1 - f)} y2={padT + innerH * (1 - f)}
            stroke="var(--border)" strokeWidth="1" strokeDasharray={f === 0 ? '0' : '3 4'} opacity={f === 0 ? 0.9 : 0.55} />
        ))}
        {data.map((d, i) => {
          const cx = padL + groupW * i + groupW / 2;
          const x1 = cx - barW - gap / 2;
          const x2 = cx + gap / 2;
          return (
            <g key={i}>
              <rect x={x1} y={Y(d.accepted)} width={barW} height={padT + innerH - Y(d.accepted)} rx="4" fill="var(--accent)"
                onMouseEnter={() => setTip({ x: cx / W * 100, y: Y(Math.max(d.accepted, d.refused)) / H * 100, d })} />
              <rect x={x2} y={Y(d.refused)} width={barW} height={padT + innerH - Y(d.refused)} rx="4" fill="var(--border-strong)"
                onMouseEnter={() => setTip({ x: cx / W * 100, y: Y(Math.max(d.accepted, d.refused)) / H * 100, d })} />
              <text x={cx} y={H - 8} textAnchor="middle" fontSize="11" fill="var(--text-3)">{d.day}</text>
            </g>
          );
        })}
      </svg>
      {tip && (
        <div className="chart-tip show" style={{ left: `${tip.x}%`, top: `${tip.y}%` }}>
          {tip.d.day} · {tip.d.accepted} acceptés · {tip.d.refused} refusés
        </div>
      )}
    </div>
  );
}

Object.assign(window, { initials, Avatar, MoodPill, TrendBadge, MoodChart, CallsChart, PinModal });
