// anim-helpers-v3.jsx — Institutional skin (SIATA · Área Metropolitana).
// Same engine as v2, but with palette, typography and chrome aligned to
// the SIATA Plantilla Presentaciones 2025: deep institutional navy, teal
// & AMVA-green accents, Rubik typography, topographic contour decoration.

// ── Palette ─────────────────────────────────────────────────────────
const C = {
  // Institutional dark navy from the SIATA logo (azul oscuro).
  navy:    '#0A2540',
  ink:     '#081E36',
  panel:   '#10355E',
  sky:     '#1E88E5',
  // Primary accent: teal from the SIATA 2025 theme (#0DCCC0).
  cyan:    '#0DCCC0',
  teal:    '#00897B',
  // Institutional secondary: Área Metropolitana lime green.
  green:   '#84BD45',
  amber:   '#FFB300',
  orange:  '#FF8A3D',
  coral:   '#E8735A',
  rose:    '#F472B6',
  purple:  '#A78BFA',
  white:   '#FFFFFF',
  gray:    'rgba(255,255,255,0.62)',
  dim:     'rgba(255,255,255,0.28)',
  faint:   'rgba(255,255,255,0.10)',
};

// Rubik is the SIATA institutional typeface (theme1.xml).
const FONT = "'Rubik', 'Plus Jakarta Sans', sans-serif";
const MONO = "'JetBrains Mono', ui-monospace, monospace";
const TOTAL_DUR = 60;

// ── Generic helpers ─────────────────────────────────────────────────

function rand(seed) {
  // tiny deterministic LCG
  let s = seed | 0;
  return function() {
    s = (s * 1664525 + 1013904223) | 0;
    return ((s >>> 0) % 1000000) / 1000000;
  };
}

function fmt(n, digits) {
  return Number(n).toFixed(digits == null ? 1 : digits);
}

// ── WindyCanvas ─────────────────────────────────────────────────────
// A Windy-style flow-field particle renderer. Particles get pushed
// through a vector field, leaving fading trails on a canvas.
//
// Props:
//   width, height   — canvas size in px
//   count           — number of particles
//   field           — fn(x, y, t) -> [vx, vy] (vector field)
//   palette         — array of stroke colors; particles are striped over this
//   fade            — 0..1 trail fade per frame (lower = longer trails)
//   speed           — global velocity multiplier
//   life            — max age in frames before respawn
//   lineWidth       — stroke width
//   opacity         — overall opacity (0..1)
//   mask            — optional fn(x,y) -> 0..1 visibility (e.g. clip to map shape)
//   spawn           — optional fn(t) -> {x,y} spawn point (default: anywhere)
//   reactKey        — change to force restart of particle pool

function WindyCanvas(props) {
  const {
    width, height,
    count = 1200,
    field,
    palette = ['#22d3ee', '#1E88E5', '#A78BFA', '#FFB300', '#FF8A3D'],
    fade = 0.05,
    speed = 1.2,
    life = 90,
    lineWidth = 1,
    opacity = 1,
    mask = null,
    spawn = null,
    reactKey = 0,
    background = null,
  } = props;

  const time = useTime();
  const canvasRef = React.useRef(null);
  const stateRef = React.useRef(null);

  // Re-initialise pool when count/key changes
  React.useEffect(() => {
    const r = rand(42 + reactKey * 17);
    stateRef.current = {
      particles: Array.from({ length: count }, (_, i) => ({
        x: r() * width,
        y: r() * height,
        ox: 0, oy: 0,
        age: Math.floor(r() * life),
        c: palette[i % palette.length],
        sp: 0.7 + r() * 0.7,
      })),
      lastT: time,
    };
    // Clear canvas
    const c = canvasRef.current;
    if (c) {
      const ctx = c.getContext('2d');
      ctx.clearRect(0, 0, width, height);
      if (background) {
        ctx.fillStyle = background;
        ctx.fillRect(0, 0, width, height);
      }
    }
  }, [count, reactKey, width, height]);

  // Per-frame draw
  React.useEffect(() => {
    const c = canvasRef.current;
    if (!c || !stateRef.current) return;
    const ctx = c.getContext('2d');
    const st = stateRef.current;
    const dt = Math.min(0.05, Math.max(0.001, time - st.lastT));
    st.lastT = time;

    // Trail fade: paint translucent background over previous frame
    ctx.globalCompositeOperation = 'source-over';
    ctx.fillStyle = background
      ? hexWithAlpha(background, fade)
      : hexWithAlpha(C.navy, fade);
    ctx.fillRect(0, 0, width, height);

    ctx.globalCompositeOperation = 'lighter';
    ctx.lineWidth = lineWidth;
    ctx.lineCap = 'round';

    const r = rand((reactKey + 1) * 9301);
    const ps = st.particles;
    for (let i = 0; i < ps.length; i++) {
      const p = ps[i];
      p.age++;

      const v = field(p.x, p.y, time);
      const vx = v[0] * speed * p.sp * 60 * dt;
      const vy = v[1] * speed * p.sp * 60 * dt;

      p.ox = p.x;
      p.oy = p.y;
      p.x += vx;
      p.y += vy;

      let alpha = 1;
      if (mask) {
        const m = mask(p.x, p.y);
        alpha = m;
      }

      const outOfBounds = p.x < -10 || p.x > width + 10 ||
                          p.y < -10 || p.y > height + 10;
      const tooOld = p.age > life;

      if (outOfBounds || tooOld || alpha <= 0.001) {
        // Respawn
        if (spawn) {
          const s = spawn(time, i);
          p.x = s.x; p.y = s.y;
        } else if (mask) {
          // Try a few times to find a spawn inside mask
          let tries = 0;
          do {
            p.x = r() * width;
            p.y = r() * height;
            tries++;
          } while (mask(p.x, p.y) < 0.2 && tries < 6);
        } else {
          p.x = r() * width;
          p.y = r() * height;
        }
        p.ox = p.x; p.oy = p.y;
        p.age = Math.floor(r() * (life * 0.4));
        continue;
      }

      // Stroke segment
      const ageFactor = 1 - Math.abs((p.age / life) - 0.5) * 2; // peak in middle
      const a = opacity * alpha * (0.35 + 0.65 * ageFactor);
      ctx.strokeStyle = hexWithAlpha(p.c, a);
      ctx.beginPath();
      ctx.moveTo(p.ox, p.oy);
      ctx.lineTo(p.x, p.y);
      ctx.stroke();
    }
    ctx.globalCompositeOperation = 'source-over';
  });

  return (
    <canvas
      ref={canvasRef}
      width={width}
      height={height}
      style={{
        position: 'absolute',
        left: 0, top: 0,
        width: width + 'px',
        height: height + 'px',
        opacity: opacity,
      }}
    />
  );
}

function hexWithAlpha(hex, a) {
  if (hex.startsWith('rgba') || hex.startsWith('rgb(')) return hex;
  const h = hex.replace('#', '');
  const full = h.length === 3
    ? h.split('').map(ch => ch + ch).join('')
    : h.substring(0, 6);
  const r = parseInt(full.substring(0, 2), 16);
  const g = parseInt(full.substring(2, 4), 16);
  const b = parseInt(full.substring(4, 6), 16);
  return 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
}

// ── Common flow fields ──────────────────────────────────────────────

function curlField(scale, timeScale, amp) {
  scale = scale || 0.004;
  timeScale = timeScale || 0.1;
  amp = amp || 1;
  return function(x, y, t) {
    const tt = t * timeScale;
    const a = Math.sin(x * scale + tt) + Math.cos(y * scale * 1.3 - tt * 0.7);
    const b = Math.cos(x * scale * 0.8 - tt * 0.5) + Math.sin(y * scale + tt);
    return [a * amp, b * amp];
  };
}

// Directional flow with curl perturbation (used for trade winds, etc.)
function directionalField(angle, scale, timeScale, amp, swirl) {
  scale = scale || 0.005;
  timeScale = timeScale || 0.15;
  amp = amp || 1;
  swirl = swirl == null ? 0.6 : swirl;
  const cx = Math.cos(angle);
  const cy = Math.sin(angle);
  return function(x, y, t) {
    const tt = t * timeScale;
    const sw1 = Math.sin(x * scale + y * scale * 0.7 + tt);
    const sw2 = Math.cos(x * scale * 0.6 - y * scale + tt * 0.8);
    return [
      (cx + sw1 * swirl) * amp,
      (cy + sw2 * swirl) * amp,
    ];
  };
}

// ── SceneFade helper (cross-fade in/out) ────────────────────────────

function SceneFade({ children, fadeIn = 0.5, fadeOut = 0.45 }) {
  const { localTime, duration } = useSprite();
  const o = Math.min(
    Easing.easeOutCubic(clamp(localTime / fadeIn, 0, 1)),
    Easing.easeInCubic(clamp((duration - localTime) / fadeOut, 0, 1))
  );
  return (
    <div style={{ opacity: o, position: 'absolute', inset: 0 }}>
      {children}
    </div>
  );
}

// ── Caption block (kicker + title + sub) ───────────────────────────

function Caption({ kicker, title, sub, x = 80, y = 80, width = 520, align = 'left', delay = 0 }) {
  const { localTime } = useSprite();
  const t1 = Easing.easeOutCubic(clamp((localTime - delay) / 0.5, 0, 1));
  const t2 = Easing.easeOutCubic(clamp((localTime - delay - 0.2) / 0.5, 0, 1));
  const t3 = Easing.easeOutCubic(clamp((localTime - delay - 0.4) / 0.5, 0, 1));
  return (
    <div style={{
      position: 'absolute', left: x, top: y, width: width,
      textAlign: align, fontFamily: FONT,
    }}>
      {kicker && (
        <div style={{
          fontSize: 11, fontWeight: 600, letterSpacing: '0.22em',
          textTransform: 'uppercase', color: C.cyan,
          opacity: t1, transform: 'translateY(' + ((1 - t1) * 8) + 'px)',
          fontFamily: MONO,
        }}>{kicker}</div>
      )}
      {title && (
        <div style={{
          fontSize: 32, fontWeight: 700, color: C.white,
          lineHeight: 1.18, marginTop: 10,
          opacity: t2, transform: 'translateY(' + ((1 - t2) * 14) + 'px)',
        }}>{title}</div>
      )}
      {sub && (
        <div style={{
          fontSize: 15, fontWeight: 400, color: C.gray,
          lineHeight: 1.5, marginTop: 14,
          opacity: t3, transform: 'translateY(' + ((1 - t3) * 14) + 'px)',
          textWrap: 'pretty',
        }}>{sub}</div>
      )}
    </div>
  );
}

// ── Topographic contour decoration (matches plantilla SIATA 2025) ──

function TopoContours({ side = 'left', opacity = 0.18, color }) {
  const c = color || C.cyan;
  // Hand-drawn nested closed contours — mimic the topo lines on the
  // plantilla's "¡Gracias!" slide. Cluster sits in one corner.
  const paths = [
    'M -20 60 C 60 40, 140 90, 210 70 S 320 50, 360 100',
    'M -20 100 C 70 78, 150 130, 220 110 S 330 95, 370 140',
    'M -20 150 C 80 130, 160 175, 230 158 S 330 145, 380 188',
    'M -20 200 C 90 178, 175 220, 240 205 S 335 195, 390 235',
    'M -20 255 C 100 235, 180 270, 245 256 S 340 248, 400 285',
    'M 30 30 C 80 20, 120 60, 150 50 S 200 30, 230 70',
    'M 50 320 C 120 305, 180 335, 220 322 S 290 318, 340 350',
  ];
  const transform = side === 'right'
    ? 'translate(1280, 0) scale(-1, 1)'
    : '';
  return (
    <svg width="1280" height="720" style={{
      position: 'absolute', inset: 0, pointerEvents: 'none',
      opacity: opacity,
    }}>
      <g transform={transform}>
        {paths.map((d, i) => (
          <path key={i} d={d}
            fill="none" stroke={c} strokeWidth={1}
            strokeOpacity={0.5 + (i % 2) * 0.4} />
        ))}
      </g>
    </svg>
  );
}

// ── SIATA institutional brand mark (inline SVG, white on dark) ──────

function SiataMark({ width = 72 }) {
  // Stylised re-creation of the SIATA sun-cloud glyph, simplified.
  // The original logo is bundled as assets/siata-logo.png for marketing
  // use; this inline mark gives crisp rendering at any size.
  const s = width / 220;
  return (
    <svg width={width} height={width * 0.55} viewBox="0 0 220 120" style={{ display: 'block' }}>
      <g fill="none" stroke={C.white} strokeWidth={6} strokeLinecap="round">
        {/* Sun rays */}
        {[
          [12, 60], [16, 32], [40, 14], [16, 88], [40, 106],
        ].map(([x, y], i) => {
          const cx = 70, cy = 60;
          const dx = x - cx, dy = y - cy;
          const len = Math.hypot(dx, dy);
          const ux = dx / len, uy = dy / len;
          return (
            <line key={'r' + i}
              x1={cx + ux * 36} y1={cy + uy * 36}
              x2={cx + ux * 50} y2={cy + uy * 50} />
          );
        })}
        {/* Sun arc (open on right where cloud overlaps) */}
        <path d="M 70 28 A 32 32 0 1 0 70 92" />
        {/* Cloud */}
        <path d="M 88 70
                 a 22 22 0 0 1 22 -22
                 a 16 16 0 0 1 30 6
                 a 18 18 0 0 1 18 22
                 a 16 16 0 0 1 -16 16
                 l -50 0
                 a 16 16 0 0 1 -4 -22 z" />
      </g>
      <text x={106} y={68} fill={C.white}
        fontFamily="'Rubik', sans-serif" fontSize={20} fontWeight={700}
        letterSpacing="2">SIATA</text>
    </svg>
  );
}

// ── Bottom chrome: progress + scene dots + tick marker ─────────────

function NavOverlay({ scenes }) {
  const { time, duration, setTime } = useTimeline();
  let cur = 0;
  for (let i = 0; i < scenes.length; i++) {
    if (time >= scenes[i].start) cur = i;
  }
  return (
    <React.Fragment>
      {/* ── Top institutional brand strip ─────────────────────────── */}
      <div style={{
        position: 'absolute', top: 0, left: 0, right: 0, height: 44,
        background: 'linear-gradient(180deg, ' + hexWithAlpha(C.ink, 0.92) + ' 0%, ' + hexWithAlpha(C.ink, 0.4) + ' 70%, rgba(0,0,0,0) 100%)',
        borderBottom: '1px solid ' + hexWithAlpha(C.cyan, 0.14),
        display: 'flex', alignItems: 'center',
        padding: '0 18px',
        zIndex: 90, pointerEvents: 'none',
      }}>
        {/* Official SIATA + AMVA logo lockup */}
        <img src="assets/siata-amva-white.png"
          alt="SIATA · Área Metropolitana del Valle de Aburrá"
          style={{
            height: 28, width: 'auto', display: 'block',
          }} />
        <div style={{
          width: 1, height: 20, background: hexWithAlpha(C.white, 0.16),
          marginLeft: 14, marginRight: 12,
        }} />
        <div style={{
          fontFamily: MONO, fontSize: 9, color: C.cyan,
          letterSpacing: '0.22em', textTransform: 'uppercase',
        }}>Sistema de Alerta Temprana · Valle de Aburrá</div>

        {/* Right side : status pill */}
        <div style={{
          marginLeft: 'auto', display: 'flex', alignItems: 'center', gap: 8,
          padding: '4px 10px',
          background: hexWithAlpha(C.green, 0.12),
          border: '1px solid ' + hexWithAlpha(C.green, 0.4),
          borderRadius: 3,
        }}>
          <div style={{
            width: 6, height: 6, borderRadius: '50%',
            background: C.green,
            boxShadow: '0 0 8px ' + hexWithAlpha(C.green, 0.8),
          }} />
          <div style={{
            fontFamily: MONO, fontSize: 9, color: C.white,
            letterSpacing: '0.22em', textTransform: 'uppercase',
          }}>
            Operacional · 4×/día
          </div>
        </div>
      </div>

      {/* Progress bar */}
      <div style={{
        position: 'absolute', bottom: 0, left: 0, right: 0, height: 3,
        background: 'rgba(255,255,255,0.05)', zIndex: 100,
      }}>
        <div style={{
          width: (time / duration) * 100 + '%', height: '100%',
          background: 'linear-gradient(90deg, ' + C.cyan + ', ' + C.green + ')',
        }} />
      </div>

      {/* Scene chips */}
      <div style={{
        position: 'absolute', bottom: 16, left: '50%',
        transform: 'translateX(-50%)',
        display: 'flex', gap: 6, zIndex: 100, alignItems: 'center',
      }}>
        {scenes.map(function(s, i) {
          const active = cur === i;
          return (
            <div key={i} onClick={() => setTime(s.start + 0.3)}
              title={s.label}
              style={{
                width: active ? 28 : 8, height: 8, borderRadius: 4,
                background: active ? C.cyan : 'rgba(255,255,255,0.18)',
                cursor: 'pointer', transition: 'all 0.3s ease',
              }} />
          );
        })}
      </div>

      {/* Scene label, bottom-left */}
      <div style={{
        position: 'absolute', bottom: 18, left: 24, zIndex: 100,
        fontFamily: MONO, fontSize: 11, letterSpacing: '0.16em',
        textTransform: 'uppercase', color: C.gray,
      }}>
        <span style={{ color: C.cyan }}>
          {String(cur + 1).padStart(2, '0')}
        </span>
        <span style={{ color: C.dim, margin: '0 8px' }}>/</span>
        {String(scenes.length).padStart(2, '0')}
        <span style={{ color: C.white, marginLeft: 14, letterSpacing: '0.04em', textTransform: 'none', fontFamily: FONT, fontSize: 12 }}>
          {scenes[cur].label}
        </span>
      </div>

      {/* Brand, bottom-right */}
      <div style={{
        position: 'absolute', bottom: 18, right: 24, zIndex: 100,
        fontFamily: MONO, fontSize: 10, letterSpacing: '0.22em',
        textTransform: 'uppercase', color: C.dim,
      }}>
        siata.gov.co · CCT 261/2025
      </div>
    </React.Fragment>
  );
}

// ── Tiny iconography ────────────────────────────────────────────────

function GridBg({ cell = 40, color = 'rgba(255,255,255,0.025)', dot = false }) {
  return (
    <div style={{
      position: 'absolute', inset: 0, pointerEvents: 'none',
      backgroundImage: dot
        ? 'radial-gradient(' + color + ' 1px, transparent 1px)'
        : 'linear-gradient(to right, ' + color + ' 1px, transparent 1px),' +
          'linear-gradient(to bottom, ' + color + ' 1px, transparent 1px)',
      backgroundSize: cell + 'px ' + cell + 'px',
    }} />
  );
}

Object.assign(window, {
  C, FONT, MONO, TOTAL_DUR,
  WindyCanvas, curlField, directionalField,
  hexWithAlpha, rand, fmt,
  SceneFade, Caption, NavOverlay, GridBg,
  TopoContours, SiataMark,
});
