// weather-vis.jsx — atmospheric visualizations & chart primitives
// All exports attached to window at the bottom.

// ─────────────────────────────────────────────────────────────────────
// Atmospheric background — three layered SVGs that compose the "weather
// you can feel" effect: isobars (pressure curves), wind flow field,
// precipitation/condensation overlay.
// ─────────────────────────────────────────────────────────────────────

function IsobarField({ palette, opacity = 0.5, seed = 1 }) {
  // Concentric blobby isobars in the palette's accent line color.
  const cx1 = 30 + seed * 7, cy1 = 35;
  const cx2 = 75, cy2 = 70 - seed * 5;
  const ring = (i, base) => {
    const r = 12 + i * 7;
    return `M ${base.cx - r} ${base.cy} ` +
      `C ${base.cx - r} ${base.cy - r * 0.8}, ${base.cx + r} ${base.cy - r}, ${base.cx + r} ${base.cy} ` +
      `C ${base.cx + r} ${base.cy + r * 0.7}, ${base.cx - r} ${base.cy + r * 0.9}, ${base.cx - r} ${base.cy}`;
  };
  return (
    <svg viewBox="0 0 100 100" preserveAspectRatio="none"
      style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', opacity, pointerEvents: 'none' }}>
      {[0, 1, 2, 3, 4].map((i) => (
        <path key={"a" + i} d={ring(i, { cx: cx1, cy: cy1 })}
          stroke={palette.line} strokeWidth={0.25} fill="none" />
      ))}
      {[0, 1, 2, 3, 4, 5].map((i) => (
        <path key={"b" + i} d={ring(i, { cx: cx2, cy: cy2 })}
          stroke={palette.line} strokeWidth={0.25} fill="none" />
      ))}
      {/* Faint pressure labels */}
      <text x={cx1} y={cy1} fill={palette.inkSoft} fontSize="2.4"
        fontFamily="'JetBrains Mono', monospace" textAnchor="middle" opacity="0.55">1012</text>
      <text x={cx2} y={cy2} fill={palette.inkSoft} fontSize="2.4"
        fontFamily="'JetBrains Mono', monospace" textAnchor="middle" opacity="0.55">1008</text>
    </svg>
  );
}

function WindField({ palette, direction = 268, intensity = 4.1, opacity = 0.55 }) {
  // Streamlines flowing in `direction` (meteorological — direction wind
  // comes from; we render the flow going TO direction+180).
  const flowDir = ((direction + 180) % 360) * Math.PI / 180;
  const dx = Math.cos(flowDir - Math.PI / 2);
  const dy = Math.sin(flowDir - Math.PI / 2);
  const lines = [];
  const density = Math.min(28, 14 + intensity);
  for (let i = 0; i < density; i++) {
    const t = i / density;
    // start points scattered across an oblique band
    const sx = -10 + t * 120 + Math.sin(i * 7.3) * 8;
    const sy = -10 + Math.cos(i * 3.1) * 110 + t * 30;
    // 3 control points along the flow vector with slight curl
    const pts = [];
    let x = sx, y = sy;
    for (let k = 0; k < 5; k++) {
      pts.push([x, y]);
      const wob = Math.sin((x + y + i) * 0.07) * 4;
      x += dx * 30 + wob * dy * 0.3;
      y += dy * 30 - wob * dx * 0.3;
    }
    const path = "M " + pts.map(p => p.join(" ")).join(" L ");
    lines.push(
      <path key={i} d={path} stroke={palette.line} fill="none"
        strokeWidth={0.28 + (i % 3) * 0.06}
        opacity={0.35 + (i % 4) * 0.12} strokeLinecap="round" />
    );
  }
  return (
    <svg viewBox="0 0 100 100" preserveAspectRatio="none"
      style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', opacity, pointerEvents: 'none' }}>
      {lines}
    </svg>
  );
}

function PrecipOverlay({ palette, intensity = 0.3, mood = "mist" }) {
  // Mist = soft horizontal washes. Rain = vertical streaks. Storm = streaks + flashes.
  if (mood === "mist" || mood === "overcast") {
    return (
      <svg viewBox="0 0 100 100" preserveAspectRatio="none"
        style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', pointerEvents: 'none', opacity: 0.7 }}>
        <defs>
          <linearGradient id="mist-h" x1="0" x2="0" y1="0" y2="1">
            <stop offset="0" stopColor={palette.surfaceA} stopOpacity="0" />
            <stop offset="0.5" stopColor={palette.surfaceA} stopOpacity="1" />
            <stop offset="1" stopColor={palette.surfaceA} stopOpacity="0" />
          </linearGradient>
        </defs>
        {[12, 28, 44, 62, 80].map((y, i) => (
          <rect key={i} x={-5} y={y - 4} width={110} height={2 + (i % 2)}
            fill="url(#mist-h)" opacity={0.4 + (i % 2) * 0.2} />
        ))}
      </svg>
    );
  }
  if (mood === "rain" || mood === "storm") {
    const drops = [];
    for (let i = 0; i < 60; i++) {
      const x = (i * 17) % 100;
      const y = (i * 23) % 100;
      const len = 3 + (i % 4);
      drops.push(
        <line key={i} x1={x} y1={y} x2={x - 1} y2={y + len}
          stroke={palette.line} strokeWidth={0.3} opacity={0.5} />
      );
    }
    return (
      <svg viewBox="0 0 100 100" preserveAspectRatio="none"
        style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', pointerEvents: 'none', opacity: 0.55 }}>
        {drops}
      </svg>
    );
  }
  return null;
}

function AtmosphericBackdrop({ palette, weathercode = 55, hour = 8, windDir = 268, windSpd = 4 }) {
  const mood = palette.mood;
  return (
    <div style={{
      position: 'absolute', inset: 0, overflow: 'hidden',
      background: `linear-gradient(180deg, ${palette.bg0} 0%, ${palette.bg1} 60%, ${palette.bg2} 100%)`,
    }}>
      <IsobarField palette={palette} opacity={0.4} />
      <WindField palette={palette} direction={windDir} intensity={windSpd} opacity={0.5} />
      <PrecipOverlay palette={palette} mood={mood} />
      {/* film grain via SVG noise */}
      <svg style={{ position: 'absolute', inset: 0, width: '100%', height: '100%',
        opacity: palette.grain, mixBlendMode: 'multiply', pointerEvents: 'none' }}>
        <filter id="n">
          <feTurbulence baseFrequency="1.4" numOctaves="2" />
          <feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.5 0" />
        </filter>
        <rect width="100%" height="100%" filter="url(#n)" />
      </svg>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────────────
// Sun arc — sunrise/sunset arc with current sun position dot.
// ─────────────────────────────────────────────────────────────────────
function SunArc({ palette, sunrise, sunset, now, width = 320, height = 70 }) {
  const t0 = new Date(sunrise + ":00").getTime();
  const t1 = new Date(sunset + ":00").getTime();
  const tn = new Date(now + ":00").getTime();
  const p = Math.max(0, Math.min(1, (tn - t0) / (t1 - t0)));
  const w = width, h = height;
  // half-ellipse arc
  const cx = w / 2, cy = h * 0.95, rx = w * 0.42, ry = h * 0.78;
  const ang = Math.PI * (1 - p); // 0..PI from left (sunrise) to right (sunset)
  const sx = cx - rx * Math.cos(ang);
  const sy = cy - ry * Math.sin(ang);
  return (
    <svg width={w} height={h} viewBox={`0 0 ${w} ${h}`} style={{ display: 'block' }}>
      <path
        d={`M ${cx - rx} ${cy} A ${rx} ${ry} 0 0 1 ${cx + rx} ${cy}`}
        fill="none" stroke={palette.line} strokeWidth={0.6} strokeDasharray="2 3" />
      <line x1={cx - rx} y1={cy} x2={cx + rx} y2={cy} stroke={palette.hair} strokeWidth={0.5} />
      <circle cx={sx} cy={sy} r={5} fill={palette.warm} />
      <circle cx={sx} cy={sy} r={9} fill="none" stroke={palette.warm} strokeWidth={0.5} opacity={0.5} />
      <text x={cx - rx} y={cy + 12} fill={palette.inkSoft} fontSize="10"
        fontFamily="'JetBrains Mono', monospace" textAnchor="start">{sunrise.slice(11)}</text>
      <text x={cx + rx} y={cy + 12} fill={palette.inkSoft} fontSize="10"
        fontFamily="'JetBrains Mono', monospace" textAnchor="end">{sunset.slice(11)}</text>
    </svg>
  );
}

// ─────────────────────────────────────────────────────────────────────
// Moon glyph — pure SVG given phase fraction 0..1.
// ─────────────────────────────────────────────────────────────────────
function MoonGlyph({ phase, size = 26, color = "#fff", bg = "#000" }) {
  // phase 0=new 0.5=full
  const r = size / 2;
  const lit = phase; // simple cos blend
  // x offset for terminator ellipse
  const k = Math.cos(phase * 2 * Math.PI);
  return (
    <svg width={size} height={size} viewBox={`0 0 ${size} ${size}`}>
      <circle cx={r} cy={r} r={r * 0.95} fill={bg} stroke={color} strokeWidth={0.6} />
      <path d={`M ${r} ${r - r * 0.95} A ${r * 0.95} ${r * 0.95} 0 0 ${phase < 0.5 ? 0 : 1} ${r} ${r + r * 0.95}
        A ${Math.abs(k) * r * 0.95} ${r * 0.95} 0 0 ${phase < 0.5 ? 1 : 0} ${r} ${r - r * 0.95} Z`}
        fill={color} opacity={0.92} />
    </svg>
  );
}

// ─────────────────────────────────────────────────────────────────────
// SeriesChart — multi-variable hourly sparkline.
// ─────────────────────────────────────────────────────────────────────
function Sparkline({ values, width, height, color, fill, strokeWidth = 1.5, smooth = true, minPad = 0.1 }) {
  const min = Math.min(...values), max = Math.max(...values);
  const range = Math.max(max - min, 0.001);
  const pad = range * minPad;
  const lo = min - pad, hi = max + pad, span = hi - lo;
  const xs = (i) => (i / (values.length - 1)) * width;
  const ys = (v) => height - ((v - lo) / span) * height;
  if (!smooth) {
    const d = values.map((v, i) => `${i === 0 ? 'M' : 'L'} ${xs(i)} ${ys(v)}`).join(' ');
    return <path d={d} stroke={color} strokeWidth={strokeWidth} fill={fill || 'none'} />;
  }
  // Catmull-Rom -> cubic
  const pts = values.map((v, i) => [xs(i), ys(v)]);
  let d = `M ${pts[0][0]} ${pts[0][1]}`;
  for (let i = 0; i < pts.length - 1; i++) {
    const p0 = pts[Math.max(0, i - 1)];
    const p1 = pts[i];
    const p2 = pts[i + 1];
    const p3 = pts[Math.min(pts.length - 1, i + 2)];
    const c1x = p1[0] + (p2[0] - p0[0]) / 6;
    const c1y = p1[1] + (p2[1] - p0[1]) / 6;
    const c2x = p2[0] - (p3[0] - p1[0]) / 6;
    const c2y = p2[1] - (p3[1] - p1[1]) / 6;
    d += ` C ${c1x} ${c1y} ${c2x} ${c2y} ${p2[0]} ${p2[1]}`;
  }
  return (
    <>
      <path d={d} stroke={color} strokeWidth={strokeWidth} fill="none" strokeLinecap="round" />
      {fill && (
        <path d={d + ` L ${xs(values.length - 1)} ${height} L 0 ${height} Z`} fill={fill} opacity={0.4} />
      )}
    </>
  );
}

// Bar series — for precipitation
function Bars({ values, width, height, color, max }) {
  const top = max ?? Math.max(...values, 1);
  const bw = width / values.length;
  return values.map((v, i) => {
    const h = (v / top) * height;
    return <rect key={i} x={i * bw + bw * 0.18} y={height - h} width={bw * 0.64}
      height={h} fill={color} rx={0.5} />;
  });
}

// ─────────────────────────────────────────────────────────────────────
// Wind compass — rose with cardinals + current dir/speed.
// ─────────────────────────────────────────────────────────────────────
function WindCompass({ direction = 268, speed = 4.1, gusts = 10.1, palette, size = 140, lang = "pt" }) {
  const r = size / 2;
  const c = size / 2;
  const ang = (direction - 90) * Math.PI / 180;
  const ax = c + (r - 18) * Math.cos(ang);
  const ay = c + (r - 18) * Math.sin(ang);
  const card = lang === "pt" ? ["N", "L", "S", "O"] : ["N", "E", "S", "W"];
  return (
    <svg width={size} height={size} viewBox={`0 0 ${size} ${size}`}>
      {/* outer ring */}
      <circle cx={c} cy={c} r={r - 2} fill="none" stroke={palette.line} strokeWidth={0.5} />
      <circle cx={c} cy={c} r={r - 22} fill="none" stroke={palette.hair} strokeWidth={0.5} strokeDasharray="2 3" />
      {/* tick marks */}
      {Array.from({ length: 36 }).map((_, i) => {
        const t = (i * 10 - 90) * Math.PI / 180;
        const r1 = r - 2, r2 = r - (i % 9 === 0 ? 10 : 6);
        return <line key={i} x1={c + r1 * Math.cos(t)} y1={c + r1 * Math.sin(t)}
          x2={c + r2 * Math.cos(t)} y2={c + r2 * Math.sin(t)}
          stroke={palette.line} strokeWidth={i % 9 === 0 ? 1 : 0.5} />;
      })}
      {/* cardinals */}
      {[0, 90, 180, 270].map((deg, i) => {
        const t = (deg - 90) * Math.PI / 180;
        return <text key={i} x={c + (r - 32) * Math.cos(t)} y={c + (r - 32) * Math.sin(t) + 3}
          textAnchor="middle" fill={palette.inkDim} fontSize="9"
          fontFamily="'JetBrains Mono', monospace" fontWeight="600">{card[i]}</text>;
      })}
      {/* arrow */}
      <line x1={c} y1={c} x2={ax} y2={ay} stroke={palette.accent} strokeWidth={2} strokeLinecap="round" />
      <polygon points={`${ax},${ay} ${ax - 6 * Math.cos(ang - 0.45)},${ay - 6 * Math.sin(ang - 0.45)} ${ax - 6 * Math.cos(ang + 0.45)},${ay - 6 * Math.sin(ang + 0.45)}`}
        fill={palette.accent} />
      {/* center value */}
      <text x={c} y={c - 2} textAnchor="middle" fill={palette.ink} fontSize="20"
        fontFamily="'Instrument Serif', serif" fontStyle="italic">{speed.toFixed(1)}</text>
      <text x={c} y={c + 12} textAnchor="middle" fill={palette.inkSoft} fontSize="8"
        fontFamily="'JetBrains Mono', monospace" letterSpacing="0.5">m/s · {gusts.toFixed(0)}↑</text>
    </svg>
  );
}

// ─────────────────────────────────────────────────────────────────────
// Tide curve — synthesized sinusoid for a port near the city.
// ─────────────────────────────────────────────────────────────────────
function TideCurve({ palette, width = 340, height = 110, lang = "pt" }) {
  const N = 96;
  const data = Array.from({ length: N }, (_, i) => {
    const t = (i / N) * Math.PI * 4;
    return 0.5 + 0.4 * Math.sin(t + 0.6) + 0.05 * Math.sin(t * 3.2);
  });
  const xs = (i) => (i / (N - 1)) * width;
  const ys = (v) => height - 10 - v * (height - 25);
  let d = `M ${xs(0)} ${ys(data[0])}`;
  for (let i = 1; i < N; i++) d += ` L ${xs(i)} ${ys(data[i])}`;
  const nowI = Math.floor(N * (8.5 / 24));
  return (
    <svg width={width} height={height} viewBox={`0 0 ${width} ${height}`}>
      <line x1={0} y1={height - 10} x2={width} y2={height - 10} stroke={palette.hair} />
      <path d={d + ` L ${xs(N - 1)} ${height - 10} L 0 ${height - 10} Z`}
        fill={palette.accent} opacity={0.16} />
      <path d={d} stroke={palette.accent} strokeWidth={1.4} fill="none" />
      <line x1={xs(nowI)} y1={6} x2={xs(nowI)} y2={height - 10}
        stroke={palette.ink} strokeWidth={0.6} strokeDasharray="2 3" />
      <text x={xs(nowI) + 4} y={14} fill={palette.ink} fontSize="9"
        fontFamily="'JetBrains Mono', monospace">{lang === "pt" ? "AGORA" : "NOW"}</text>
      {[0, 6, 12, 18, 24].map((h) => {
        const i = (h / 24) * (N - 1);
        return <text key={h} x={xs(i)} y={height - 1} fill={palette.inkSoft} fontSize="8"
          fontFamily="'JetBrains Mono', monospace" textAnchor="middle">
          {String(h).padStart(2, "0")}h
        </text>;
      })}
    </svg>
  );
}

// Air-quality ring (AQI 0-200) — concentric meter.
function AQIRing({ value = 38, palette, size = 110, lang = "pt" }) {
  const max = 300;
  const pct = Math.min(1, value / max);
  const r = size / 2 - 8;
  const c = size / 2;
  const circ = 2 * Math.PI * r;
  const color =
    value < 50 ? "#5e9c6a" : value < 100 ? "#c5a23a" : value < 150 ? "#c97c3a" : "#a8463a";
  const label =
    value < 50 ? (lang === "pt" ? "Bom" : "Good")
    : value < 100 ? (lang === "pt" ? "Moderado" : "Moderate")
    : value < 150 ? (lang === "pt" ? "Sensíveis" : "Sensitive")
    : (lang === "pt" ? "Ruim" : "Unhealthy");
  return (
    <svg width={size} height={size} viewBox={`0 0 ${size} ${size}`}>
      <circle cx={c} cy={c} r={r} fill="none" stroke={palette.hair} strokeWidth={6} />
      <circle cx={c} cy={c} r={r} fill="none" stroke={color} strokeWidth={6}
        strokeDasharray={`${circ * pct} ${circ}`} strokeDashoffset={circ * 0.25} transform={`rotate(-90 ${c} ${c})`}
        strokeLinecap="round" />
      <text x={c} y={c - 2} textAnchor="middle" fill={palette.ink} fontSize="28"
        fontFamily="'Instrument Serif', serif" fontStyle="italic">{value}</text>
      <text x={c} y={c + 14} textAnchor="middle" fill={palette.inkSoft} fontSize="9"
        fontFamily="'JetBrains Mono', monospace" letterSpacing="0.5">AQI · {label}</text>
    </svg>
  );
}

Object.assign(window, {
  IsobarField, WindField, PrecipOverlay, AtmosphericBackdrop,
  SunArc, MoonGlyph, Sparkline, Bars, WindCompass, TideCurve, AQIRing,
});
