// Résonance — EmotionalWeather hero for Écho
// A living blob that breathes and diffuses like ink in water.
// Geometry + color respond to the current Nuance state.

const { useState: uSw, useEffect: uEw, useRef: uRw, useMemo: uMw } = React;

// State → visual personality
// - hue: base color(s) to diffuse
// - amp: how wobbly the silhouette is (0.0 calm sphere, 0.22 frantic)
// - speed: seconds per full wobble cycle (higher = slower/calmer)
// - breath: scale oscillation amplitude (0 none → 0.12 deep)
// - dual: whether Orange + Blue bodies coexist (Harmonie fused, Dissonance split)
// - drift: lateral drift of the two bodies (0 overlapping, 1 far apart)
// - turbulence: noise frequency for the diffusion filter
// - glow: outer halo strength
const WEATHER = {
  resonance:  { label: 'Harmonie',             hue: '#8A5B95', amp: 0.06, speed: 14, breath: 0.07, dual: true, drift: 0.00, turbulence: 0.010, glow: 0.75 },
  equilibre:  { label: 'Équilibre fragile',    hue: '#90B8F0', amp: 0.09, speed: 11, breath: 0.06, dual: true, drift: 0.18, turbulence: 0.014, glow: 0.55 },
  tension:    { label: 'Tension',              hue: '#D98964', amp: 0.14, speed: 7.5,breath: 0.04, dual: true, drift: 0.42, turbulence: 0.022, glow: 0.45 },
  dissonance: { label: 'Dissonance',           hue: '#F0A090', amp: 0.18, speed: 6,  breath: 0.03, dual: true, drift: 0.78, turbulence: 0.028, glow: 0.40 },
  alerte:     { label: 'Alerte récupération', hue: '#FC442C', amp: 0.22, speed: 5,  breath: 0.02, dual: false,drift: 0.00, turbulence: 0.035, glow: 0.85 },
};

// Build a smooth closed SVG path from radial samples (Catmull-Rom → cubic).
function blobPath(cx, cy, baseR, samples) {
  const n = samples.length;
  const pts = samples.map((r, i) => {
    const a = (i / n) * Math.PI * 2;
    return [cx + Math.cos(a) * (baseR * r), cy + Math.sin(a) * (baseR * r)];
  });
  const cr = (p0, p1, p2, p3, t = 0.5) => {
    // Catmull-Rom → Bezier control points, tension t
    const c1x = p1[0] + (p2[0] - p0[0]) / 6 * t * 2;
    const c1y = p1[1] + (p2[1] - p0[1]) / 6 * t * 2;
    const c2x = p2[0] - (p3[0] - p1[0]) / 6 * t * 2;
    const c2y = p2[1] - (p3[1] - p1[1]) / 6 * t * 2;
    return `C ${c1x.toFixed(2)} ${c1y.toFixed(2)} ${c2x.toFixed(2)} ${c2y.toFixed(2)} ${p2[0].toFixed(2)} ${p2[1].toFixed(2)}`;
  };
  let d = `M ${pts[0][0].toFixed(2)} ${pts[0][1].toFixed(2)}`;
  for (let i = 0; i < n; i++) {
    const p0 = pts[(i - 1 + n) % n];
    const p1 = pts[i];
    const p2 = pts[(i + 1) % n];
    const p3 = pts[(i + 2) % n];
    d += ' ' + cr(p0, p1, p2, p3, 1);
  }
  return d + ' Z';
}

// Stable low-frequency noise using a handful of layered sines per sample.
function sampleWobble(nSamples, amp, t, seed) {
  const out = new Array(nSamples);
  for (let i = 0; i < nSamples; i++) {
    const a = (i / nSamples) * Math.PI * 2 + seed;
    // 3 harmonics, slow phases — gives ink-in-water quality
    const w =
      Math.sin(a * 2 + t * 0.35) * 0.55 +
      Math.sin(a * 3 + t * 0.22 + 1.3) * 0.30 +
      Math.sin(a * 5 + t * 0.14 + 2.7) * 0.15;
    out[i] = 1 + w * amp;
  }
  return out;
}

function EmotionalWeather({ state = 'resonance', height = 280, onCycleState }) {
  const w = WEATHER[state] || WEATHER.resonance;
  const idA = uMw(() => 'grad-a-' + Math.random().toString(36).slice(2, 7), []);
  const idB = uMw(() => 'grad-b-' + Math.random().toString(36).slice(2, 7), []);
  const idFilter = uMw(() => 'ink-' + Math.random().toString(36).slice(2, 7), []);
  const idGlow = uMw(() => 'glow-' + Math.random().toString(36).slice(2, 7), []);

  const [tick, setTick] = uSw(0);
  const tRef = uRw(0);
  const rafRef = uRw(0);

  // Animated time — slower for calmer states, faster for agitated
  uEw(() => {
    let last = performance.now();
    const loop = (now) => {
      const dt = (now - last) / 1000;
      last = now;
      // speed: seconds per cycle → radians per second
      const omega = (Math.PI * 2) / w.speed;
      tRef.current += dt * omega;
      setTick(tRef.current);
      rafRef.current = requestAnimationFrame(loop);
    };
    rafRef.current = requestAnimationFrame(loop);
    return () => cancelAnimationFrame(rafRef.current);
  }, [w.speed]);

  const cx = 200, cy = 140;
  const baseR = 78;
  const nSamples = 14;

  // Breath: slow in/out over ~4s, independent of agitation cadence
  const breath = 1 + Math.sin(performance.now() / 1000 * 0.55) * w.breath;

  // Two bodies drift apart for Dissonance, overlap for Harmonie
  const drift = w.drift * 60;
  const samplesA = sampleWobble(nSamples, w.amp, tick, 0);
  const samplesB = sampleWobble(nSamples, w.amp * 0.9, tick + 100, 2.3);
  const pathA = blobPath(cx - drift, cy, baseR * breath, samplesA);
  const pathB = blobPath(cx + drift, cy, baseR * breath * 0.92, samplesB);

  // Orange + Blue both present except during Alerte (Orange dominant alone per DS)
  const colorA = state === 'alerte' ? '#FC442C' : '#F0A090';
  const colorB = state === 'alerte' ? '#FC442C' : '#90B8F0';
  // Core blend color where bodies overlap
  const core = w.hue;

  return (
    <div style={{
      position: 'relative', height, width: '100%',
      marginBottom: 10, userSelect: 'none',
      cursor: onCycleState ? 'pointer' : 'default',
    }} onClick={onCycleState}>
      {/* Diffuse outer glow — sits behind everything */}
      <div style={{
        position: 'absolute', inset: 0,
        background: `radial-gradient(ellipse 60% 50% at 50% 50%, ${core}${Math.round(w.glow * 80).toString(16).padStart(2,'0')} 0%, ${core}00 70%)`,
        filter: 'blur(30px)', pointerEvents: 'none',
        transition: 'background 1200ms ease',
      }}/>

      <svg viewBox="0 0 400 280" width="100%" height={height} style={{ display: 'block', overflow: 'visible' }}>
        <defs>
          {/* Ink-in-water filter: turbulent noise displacing the shape, then blur */}
          <filter id={idFilter} x="-40%" y="-40%" width="180%" height="180%">
            <feTurbulence type="fractalNoise" baseFrequency={w.turbulence} numOctaves="2" seed="4">
              <animate attributeName="baseFrequency"
                values={`${w.turbulence};${w.turbulence * 1.6};${w.turbulence}`}
                dur={`${w.speed * 0.7}s`} repeatCount="indefinite"/>
            </feTurbulence>
            <feDisplacementMap in="SourceGraphic" scale={state === 'alerte' ? 14 : 9}/>
            <feGaussianBlur stdDeviation="1.8"/>
          </filter>

          <filter id={idGlow} x="-50%" y="-50%" width="200%" height="200%">
            <feGaussianBlur stdDeviation="16"/>
          </filter>

          <radialGradient id={idA} cx="35%" cy="35%" r="70%">
            <stop offset="0%" stopColor={colorA} stopOpacity="0.95"/>
            <stop offset="55%" stopColor={colorA} stopOpacity="0.55"/>
            <stop offset="100%" stopColor={colorA} stopOpacity="0.05"/>
          </radialGradient>
          <radialGradient id={idB} cx="65%" cy="55%" r="70%">
            <stop offset="0%" stopColor={colorB} stopOpacity="0.95"/>
            <stop offset="55%" stopColor={colorB} stopOpacity="0.55"/>
            <stop offset="100%" stopColor={colorB} stopOpacity="0.05"/>
          </radialGradient>
        </defs>

        {/* Soft halo behind bodies */}
        <ellipse cx={cx} cy={cy} rx={baseR * 1.6} ry={baseR * 1.2}
          fill={core} opacity={w.glow * 0.35} filter={`url(#${idGlow})`}/>

        {/* Bodies with ink diffusion */}
        <g filter={`url(#${idFilter})`} style={{ mixBlendMode: 'multiply' }}>
          <path d={pathA} fill={`url(#${idA})`}/>
          {w.dual && <path d={pathB} fill={`url(#${idB})`}/>}
        </g>

        {/* A thin, living inner core — where bodies overlap, the fusion color shows */}
        {w.dual && w.drift < 0.5 && (
          <g filter={`url(#${idFilter})`}>
            <ellipse cx={cx} cy={cy} rx={baseR * 0.45 * (1 - w.drift)} ry={baseR * 0.38 * (1 - w.drift)}
              fill={core} opacity={0.55 - w.drift}/>
          </g>
        )}
      </svg>

      {/* Minimal label — pure type, no icon. Fades slowly when state changes */}
      <div key={state} style={{
        position: 'absolute', left: 0, right: 0, bottom: -4,
        textAlign: 'center',
        animation: 'rz-fade 800ms ease-out',
      }}>
        <div style={{
          fontSize: 10, fontWeight: 600, color: 'rgba(24,20,15,0.45)',
          textTransform: 'uppercase', letterSpacing: 2, marginBottom: 4,
        }}>
          En ce moment
        </div>
        <div style={{
          fontSize: 17, fontWeight: 600, color: RZ.fg1, letterSpacing: -0.2,
        }}>
          {w.label}
        </div>
      </div>
    </div>
  );
}

// ═════════════════════════════════════════════════════════════
// Nuage — 30-day cloud. Each day is a small blob, layered with opacity.
// Hot clusters emerge where days share a state. This is the "atmosphere"
// of a month, not a chart — you read it like a weather front.
// ═════════════════════════════════════════════════════════════
function EmotionalCloud({ days = 30, periodLabel = '30 jours', height = 260 }) {
  const idBlur = uMw(() => 'cloud-' + Math.random().toString(36).slice(2, 7), []);
  const stateKeys = ['resonance', 'equilibre', 'tension', 'dissonance', 'alerte'];
  // Deterministic 30-day sample leaning toward Équilibre/Tension (typical user)
  const data = uMw(() => {
    const weights = [0.25, 0.35, 0.22, 0.13, 0.05];
    const out = [];
    let rng = 42;
    const rand = () => { rng = (rng * 9301 + 49297) % 233280; return rng / 233280; };
    for (let i = 0; i < days; i++) {
      const r = rand();
      let acc = 0, s = 'equilibre';
      for (let k = 0; k < weights.length; k++) { acc += weights[k]; if (r < acc) { s = stateKeys[k]; break; } }
      // Position: hot "Harmonie" pulls center; "Dissonance" pushes out
      const theta = rand() * Math.PI * 2;
      const base = { resonance: 0.15, equilibre: 0.3, tension: 0.5, dissonance: 0.75, alerte: 0.85 }[s];
      const r2 = base + (rand() - 0.5) * 0.2;
      out.push({
        state: s,
        x: 200 + Math.cos(theta) * r2 * 110,
        y: 130 + Math.sin(theta) * r2 * 75,
        r: 14 + rand() * 8,
        op: 0.35 + rand() * 0.35,
        seed: rand() * Math.PI * 2,
      });
    }
    return out;
  }, [days]);

  // Count dominance
  const counts = uMw(() => {
    const c = {};
    data.forEach(d => c[d.state] = (c[d.state] || 0) + 1);
    return c;
  }, [data]);
  const dominant = Object.entries(counts).sort((a,b) => b[1] - a[1])[0][0];
  const domLabel = WEATHER[dominant].label;

  return (
    <div style={{ position: 'relative', height, width: '100%', marginBottom: 10, userSelect: 'none' }}>
      <svg viewBox="0 0 400 260" width="100%" height={height} style={{ display: 'block' }}>
        <defs>
          <filter id={idBlur}>
            <feGaussianBlur stdDeviation="6"/>
          </filter>
        </defs>
        {/* Ghosted "harmonie zone" — a faint ellipse at center, for reference */}
        <ellipse cx="200" cy="130" rx="40" ry="26" fill="none"
          stroke="rgba(138,91,149,0.25)" strokeWidth="1" strokeDasharray="3 4"/>
        <g filter={`url(#${idBlur})`}>
          {data.map((d, i) => {
            const w = WEATHER[d.state];
            const col = d.state === 'alerte' ? '#FC442C'
                       : d.state === 'dissonance' ? '#F0A090'
                       : d.state === 'tension' ? '#D98964'
                       : d.state === 'equilibre' ? '#90B8F0'
                       : '#8A5B95';
            return <circle key={i} cx={d.x} cy={d.y} r={d.r} fill={col} opacity={d.op}/>;
          })}
        </g>
        {/* A few crisp dots on top to ground the cloud */}
        {data.slice(0, 8).map((d, i) => (
          <circle key={'top'+i} cx={d.x} cy={d.y} r="2"
            fill={d.state === 'alerte' ? '#FC442C' : d.state === 'resonance' ? '#8A5B95' : d.state === 'equilibre' ? '#3F6FB8' : d.state === 'tension' ? '#A85B2E' : '#C55840'}
            opacity="0.6"/>
        ))}
      </svg>
      <div style={{ position: 'absolute', left: 0, right: 0, bottom: -4, textAlign: 'center' }}>
        <div style={{ fontSize: 10, fontWeight: 600, color: 'rgba(24,20,15,0.45)', textTransform: 'uppercase', letterSpacing: 2, marginBottom: 4 }}>
          Dominance · {periodLabel}
        </div>
        <div style={{ fontSize: 17, fontWeight: 600, color: RZ.fg1, letterSpacing: -0.2 }}>
          {domLabel}
        </div>
      </div>
    </div>
  );
}

// ═════════════════════════════════════════════════════════════
// Horizon — 3-month ribbon. A continuous landscape where color + altitude
// narrate the emotional arc. Reads like a seismograph you'd actually
// hang on a wall.
// ═════════════════════════════════════════════════════════════
function EmotionalHorizon({ weeks = 13, periodLabel = '3 mois', height = 260 }) {
  // State → altitude (higher = more Harmonie), color
  const altitude = { resonance: 0.85, equilibre: 0.65, tension: 0.40, dissonance: 0.25, alerte: 0.10 };
  const color    = { resonance: '#8A5B95', equilibre: '#90B8F0', tension: '#D98964', dissonance: '#F0A090', alerte: '#FC442C' };

  const data = uMw(() => {
    // Deterministic 13-week arc — a plausible story: dip in Feb, climb, stable, a late alerte, recovery
    const story = ['equilibre','equilibre','tension','tension','dissonance','tension','equilibre','resonance','resonance','equilibre','tension','alerte','equilibre'];
    return story.slice(0, weeks);
  }, [weeks]);

  const W = 400, H = 200;
  const step = W / (data.length - 1);
  // Build a smooth ridge path
  const points = data.map((s, i) => [i * step, H - altitude[s] * H + 20]);
  // Catmull-Rom → cubic
  const ridgeD = points.reduce((acc, [x, y], i) => {
    if (i === 0) return `M ${x} ${y}`;
    const p0 = points[i - 1];
    const p2 = points[Math.min(points.length - 1, i + 1)];
    const cp1x = p0[0] + (x - p0[0]) * 0.5;
    const cp1y = p0[1];
    const cp2x = x - (p2[0] - p0[0]) * 0.15;
    const cp2y = y;
    return acc + ` C ${cp1x} ${cp1y} ${cp2x} ${cp2y} ${x} ${y}`;
  }, '');
  const fillD = ridgeD + ` L ${W} ${H + 20} L 0 ${H + 20} Z`;

  // Dominant state = most frequent in last 4 weeks (recency bias)
  const recent = data.slice(-4);
  const cnt = {};
  recent.forEach(s => cnt[s] = (cnt[s] || 0) + 1);
  const dominant = Object.entries(cnt).sort((a,b) => b[1] - a[1])[0][0];

  const idGradH = uMw(() => 'horizon-' + Math.random().toString(36).slice(2, 7), []);

  return (
    <div style={{ position: 'relative', height, width: '100%', marginBottom: 10, userSelect: 'none' }}>
      <svg viewBox={`0 0 ${W} 240`} width="100%" height={height} style={{ display: 'block' }}>
        <defs>
          <linearGradient id={idGradH} x1="0" y1="0" x2="1" y2="0">
            {data.map((s, i) => (
              <stop key={i} offset={`${(i / (data.length - 1)) * 100}%`} stopColor={color[s]} stopOpacity="0.85"/>
            ))}
          </linearGradient>
          <linearGradient id={idGradH + '-fade'} x1="0" y1="0" x2="0" y2="1">
            <stop offset="0%" stopColor="#8A5B95" stopOpacity="0.5"/>
            <stop offset="100%" stopColor="#8A5B95" stopOpacity="0.05"/>
          </linearGradient>
        </defs>

        {/* Back wash — the overall emotional "sky" */}
        <rect x="0" y="0" width={W} height="240" fill={`url(#${idGradH}-fade)`} opacity="0.4"/>

        {/* Fill under ridge, colored by time */}
        <path d={fillD} fill={`url(#${idGradH})`} opacity="0.55"/>
        {/* Ridge stroke */}
        <path d={ridgeD} fill="none" stroke={`url(#${idGradH})`} strokeWidth="2.5" strokeLinecap="round"/>

        {/* Week markers as tiny filled discs on the ridge */}
        {points.map(([x, y], i) => (
          <circle key={i} cx={x} cy={y} r="3" fill={color[data[i]]} opacity="0.9"/>
        ))}

        {/* Month dividers */}
        {[1/3, 2/3].map((f, i) => (
          <line key={i} x1={W * f} y1="10" x2={W * f} y2="230" stroke="rgba(24,20,15,0.08)" strokeWidth="1" strokeDasharray="2 3"/>
        ))}
        <text x="8" y="232" fontSize="9" fill="rgba(24,20,15,0.4)" fontWeight="500" letterSpacing="1">FÉV</text>
        <text x={W/3 + 8} y="232" fontSize="9" fill="rgba(24,20,15,0.4)" fontWeight="500" letterSpacing="1">MAR</text>
        <text x={2*W/3 + 8} y="232" fontSize="9" fill="rgba(24,20,15,0.4)" fontWeight="500" letterSpacing="1">AVR</text>
      </svg>
      <div style={{ position: 'absolute', left: 0, right: 0, bottom: -4, textAlign: 'center' }}>
        <div style={{ fontSize: 10, fontWeight: 600, color: 'rgba(24,20,15,0.45)', textTransform: 'uppercase', letterSpacing: 2, marginBottom: 4 }}>
          Tendance · {periodLabel}
        </div>
        <div style={{ fontSize: 17, fontWeight: 600, color: RZ.fg1, letterSpacing: -0.2 }}>
          {WEATHER[dominant].label}
        </div>
      </div>
    </div>
  );
}

Object.assign(window, { EmotionalWeather, EmotionalCloud, EmotionalHorizon, WEATHER });
