// ===== Charts: animated SVG components =====

const useAnimatedNumber = (target, duration = 900) => {
  const [n, setN] = React.useState(0);
  React.useEffect(() => {
    let raf, start;
    const tick = (t) => {
      if (!start) start = t;
      const p = Math.min(1, (t - start) / duration);
      const eased = 1 - Math.pow(1 - p, 3);
      setN(target * eased);
      if (p < 1) raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [target, duration]);
  return n;
};

const useReveal = (duration = 1100) => {
  const [t, setT] = React.useState(0);
  React.useEffect(() => {
    let raf, s;
    const tick = (ts) => {
      if (!s) s = ts;
      const p = Math.min(1, (ts - s) / duration);
      setT(1 - Math.pow(1 - p, 3));
      if (p < 1) raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [duration]);
  return t;
};

// ====== Animated counter (number with rolling animation) ======
const AnimatedCounter = ({ value, decimals = 0, suffix = "", duration = 1200 }) => {
  const n = useAnimatedNumber(value, duration);
  return <span className="num-display">{n.toFixed(decimals).replace(/\B(?=(\d{3})+(?!\d))/g, ",")}{suffix}</span>;
};

// ====== Donut chart ======
const DonutChart = ({ data, total, size = 240, thickness = 32, onSliceClick }) => {
  const t = useReveal(1100);
  const cx = size / 2, cy = size / 2;
  const r = (size - thickness) / 2;
  const sum = total || data.reduce((s, d) => s + d.value, 0);
  let acc = 0;
  const [hover, setHover] = React.useState(null);

  return (
    <div style={{ position: "relative", width: size, height: size }}>
      <svg width={size} height={size} style={{ overflow: "visible" }}>
        <circle cx={cx} cy={cy} r={r} stroke="rgba(255,255,255,0.04)" strokeWidth={thickness} fill="none" />
        {data.map((d, i) => {
          const frac = d.value / sum;
          const start = (acc / sum) * Math.PI * 2 - Math.PI / 2;
          const end = ((acc + d.value) / sum) * Math.PI * 2 - Math.PI / 2;
          acc += d.value;
          const lenArc = (end - start) * r;
          const circ = 2 * Math.PI * r;
          const offset = (start + Math.PI / 2) / (2 * Math.PI) * circ;
          const dashLen = (frac * circ) * t;
          const isHover = hover === i;
          return (
            <circle
              key={i}
              cx={cx} cy={cy} r={r}
              stroke={d.color}
              strokeWidth={isHover ? thickness + 6 : thickness}
              fill="none"
              strokeDasharray={`${dashLen} ${circ}`}
              strokeDashoffset={-offset}
              style={{
                transition: "stroke-width .2s",
                cursor: onSliceClick ? "pointer" : "default",
                filter: isHover ? `drop-shadow(0 0 12px ${d.color})` : "none",
              }}
              onMouseEnter={() => setHover(i)}
              onMouseLeave={() => setHover(null)}
              onClick={() => onSliceClick && onSliceClick(d)}
            />
          );
        })}
      </svg>
      <div style={{
        position: "absolute", inset: 0,
        display: "flex", flexDirection: "column",
        alignItems: "center", justifyContent: "center",
        pointerEvents: "none"
      }}>
        {hover != null ? (
          <>
            <div style={{ fontSize: 12, color: "var(--text-dim)" }}>{data[hover].label}</div>
            <div className="num-display" style={{ fontSize: 32, color: data[hover].color }}>
              {data[hover].value}
            </div>
            <div style={{ fontSize: 12, color: "var(--text-dim)" }}>
              {((data[hover].value / sum) * 100).toFixed(1)}%
            </div>
          </>
        ) : (
          <>
            <div style={{ fontSize: 12, color: "var(--text-dim)" }}>ทั้งหมด</div>
            <div className="num-display" style={{ fontSize: 36, color: "var(--text)" }}>
              <AnimatedCounter value={sum} duration={1100} />
            </div>
            <div style={{ fontSize: 12, color: "var(--text-dim)" }}>คน</div>
          </>
        )}
      </div>
    </div>
  );
};

// ====== Bar chart (horizontal-ish: vertical bars) ======
const BarChart = ({ data, height = 220, onBarClick, valueFormat = (v) => v }) => {
  const t = useReveal(1100);
  const [hover, setHover] = React.useState(null);
  const max = Math.max(...data.map(d => d.value), 1);
  const ref = React.useRef(null);
  const [w, setW] = React.useState(600);
  React.useEffect(() => {
    if (!ref.current) return;
    const ro = new ResizeObserver(([e]) => setW(e.contentRect.width));
    ro.observe(ref.current);
    return () => ro.disconnect();
  }, []);
  const padL = 40, padR = 12, padT = 18, padB = 50;
  const innerW = w - padL - padR;
  const innerH = height - padT - padB;
  const barW = data.length ? Math.min(56, innerW / data.length * 0.66) : 30;
  const gap = data.length ? (innerW - barW * data.length) / (data.length) : 0;

  return (
    <div ref={ref} style={{ width: "100%", position: "relative" }}>
      <svg width={w} height={height} style={{ overflow: "visible", display: "block" }}>
        {[0, 0.25, 0.5, 0.75, 1].map((g, i) => (
          <g key={i}>
            <line x1={padL} x2={w - padR} y1={padT + innerH * (1-g)} y2={padT + innerH * (1-g)}
              stroke="rgba(255,255,255,0.05)" strokeDasharray="3 4" />
            <text x={padL - 8} y={padT + innerH * (1-g) + 4} fontSize="10" fill="var(--text-faint)" textAnchor="end">
              {Math.round(max * g)}
            </text>
          </g>
        ))}
        {data.map((d, i) => {
          const h = innerH * (d.value / max) * t;
          const x = padL + gap / 2 + i * (barW + gap);
          const y = padT + innerH - h;
          const isHover = hover === i;
          return (
            <g key={i}
              style={{ cursor: onBarClick ? "pointer" : "default" }}
              onMouseEnter={() => setHover(i)}
              onMouseLeave={() => setHover(null)}
              onClick={() => onBarClick && onBarClick(d)}
            >
              <defs>
                <linearGradient id={`bg${i}`} x1="0" x2="0" y1="0" y2="1">
                  <stop offset="0%" stopColor={d.color} stopOpacity={isHover ? 1 : 0.95} />
                  <stop offset="100%" stopColor={d.color} stopOpacity={0.45} />
                </linearGradient>
              </defs>
              <rect x={x} y={y} width={barW} height={h} rx="4"
                fill={`url(#bg${i})`}
                style={{
                  filter: isHover ? `drop-shadow(0 0 12px ${d.color})` : "none",
                  transition: "filter .2s"
                }}
              />
              <text x={x + barW / 2} y={y - 6} textAnchor="middle" fontSize="11"
                fill={isHover ? d.color : "var(--text-dim)"}
                style={{ fontFamily: "var(--font-num)", fontWeight: 600, transition: "fill .2s" }}>
                {t > 0.85 ? valueFormat(d.value) : ""}
              </text>
              <text x={x + barW / 2} y={padT + innerH + 18} textAnchor="middle" fontSize="11"
                fill="var(--text-dim)">
                {d.label.length > 9 ? d.label.slice(0, 9) + "…" : d.label}
              </text>
              {d.sublabel && (
                <text x={x + barW / 2} y={padT + innerH + 32} textAnchor="middle" fontSize="9"
                  fill="var(--text-faint)">
                  {d.sublabel}
                </text>
              )}
              <rect x={x - 4} y={padT} width={barW + 8} height={innerH} fill="transparent" />
            </g>
          );
        })}
      </svg>
    </div>
  );
};

// ====== Line chart (in/out monthly) ======
const LineChart = ({ series, height = 240, labels, onPointClick }) => {
  const t = useReveal(1300);
  const [hover, setHover] = React.useState(null);
  const ref = React.useRef(null);
  const [w, setW] = React.useState(600);
  React.useEffect(() => {
    if (!ref.current) return;
    const ro = new ResizeObserver(([e]) => setW(e.contentRect.width));
    ro.observe(ref.current);
    return () => ro.disconnect();
  }, []);
  const padL = 36, padR = 16, padT = 16, padB = 30;
  const innerW = w - padL - padR;
  const innerH = height - padT - padB;
  const allVals = series.flatMap(s => s.data);
  const max = Math.max(...allVals, 5);
  const n = labels.length;
  const xAt = (i) => padL + (innerW * i) / Math.max(1, n - 1);
  const yAt = (v) => padT + innerH - innerH * (v / max);

  return (
    <div ref={ref} style={{ width: "100%", position: "relative" }}>
      <svg width={w} height={height} style={{ display: "block", overflow: "visible" }}>
        {[0, 0.5, 1].map((g, i) => (
          <line key={i} x1={padL} x2={w - padR}
            y1={padT + innerH * (1 - g)} y2={padT + innerH * (1 - g)}
            stroke="rgba(255,255,255,0.05)" strokeDasharray="3 4" />
        ))}
        {series.map((s, si) => {
          const pts = s.data.map((v, i) => [xAt(i), yAt(v)]);
          const path = pts.map((p, i) => (i === 0 ? `M${p[0]},${p[1]}` : `L${p[0]},${p[1]}`)).join(" ");
          const area = path + ` L${xAt(n-1)},${padT + innerH} L${xAt(0)},${padT + innerH} Z`;
          const len = pts.reduce((acc, p, i) => i === 0 ? 0 : acc + Math.hypot(p[0]-pts[i-1][0], p[1]-pts[i-1][1]), 0);
          const draw = len * t;
          return (
            <g key={si}>
              <defs>
                <linearGradient id={`ag${si}`} x1="0" x2="0" y1="0" y2="1">
                  <stop offset="0%" stopColor={s.color} stopOpacity="0.35" />
                  <stop offset="100%" stopColor={s.color} stopOpacity="0" />
                </linearGradient>
              </defs>
              <path d={area} fill={`url(#ag${si})`} style={{ opacity: t }} />
              <path d={path} fill="none" stroke={s.color} strokeWidth="2.5"
                strokeLinecap="round"
                strokeDasharray={`${draw} ${len}`} />
              {pts.map((p, i) => (
                <g key={i} onMouseEnter={() => setHover({ s: si, i })}
                  onMouseLeave={() => setHover(null)}
                  onClick={() => onPointClick && onPointClick(si, i)}
                  style={{ cursor: onPointClick ? "pointer" : "default" }}>
                  <circle cx={p[0]} cy={p[1]} r={hover && hover.s === si && hover.i === i ? 6 : 3.5}
                    fill={s.color} stroke="var(--bg-1)" strokeWidth="2"
                    style={{ opacity: t > 0.7 ? 1 : 0, transition: "r .15s" }} />
                  <circle cx={p[0]} cy={p[1]} r="14" fill="transparent" />
                </g>
              ))}
            </g>
          );
        })}
        {/* x labels */}
        {labels.map((l, i) => (
          <text key={i} x={xAt(i)} y={padT + innerH + 18} fontSize="11" textAnchor="middle"
            fill="var(--text-dim)">{l}</text>
        ))}
        {/* y labels */}
        {[0, 0.5, 1].map((g, i) => (
          <text key={i} x={padL - 8} y={padT + innerH * (1 - g) + 4} fontSize="10"
            fill="var(--text-faint)" textAnchor="end">{Math.round(max * g)}</text>
        ))}
        {/* hover label */}
        {hover && (
          <g pointerEvents="none">
            <line x1={xAt(hover.i)} x2={xAt(hover.i)} y1={padT} y2={padT + innerH}
              stroke="rgba(255,255,255,0.18)" strokeDasharray="2 3" />
          </g>
        )}
      </svg>
      {hover && (
        <div style={{
          position: "absolute",
          left: Math.min(w - 140, xAt(hover.i) + 10),
          top: 6,
          background: "var(--bg-card-solid)",
          border: "1px solid var(--border-strong)",
          padding: "8px 10px",
          borderRadius: 8,
          fontSize: 12,
          minWidth: 110,
          boxShadow: "0 8px 24px -6px rgba(0,0,0,0.5)"
        }}>
          <div style={{ color: "var(--text-dim)", marginBottom: 4 }}>{labels[hover.i]}</div>
          {series.map((s, i) => (
            <div key={i} style={{ display: "flex", alignItems: "center", gap: 6 }}>
              <span className="dot" style={{ background: s.color }} />
              <span style={{ color: "var(--text-dim)" }}>{s.label}</span>
              <span style={{ marginLeft: "auto", fontFamily: "var(--font-num)", color: s.color, fontWeight: 600 }}>{s.data[hover.i]}</span>
            </div>
          ))}
        </div>
      )}
    </div>
  );
};

// ====== Histogram (age) ======
const Histogram = ({ data, height = 220, color = "#4ec3ff", onBarClick }) => {
  const t = useReveal(1000);
  const [hover, setHover] = React.useState(null);
  const ref = React.useRef(null);
  const [w, setW] = React.useState(600);
  React.useEffect(() => {
    if (!ref.current) return;
    const ro = new ResizeObserver(([e]) => setW(e.contentRect.width));
    ro.observe(ref.current);
    return () => ro.disconnect();
  }, []);
  const padL = 32, padR = 12, padT = 18, padB = 28;
  const innerW = w - padL - padR;
  const innerH = height - padT - padB;
  const max = Math.max(...data.map(d => d.value), 1);
  const barW = innerW / data.length * 0.88;
  const gap = innerW / data.length * 0.12;

  return (
    <div ref={ref} style={{ width: "100%" }}>
      <svg width={w} height={height} style={{ overflow: "visible", display: "block" }}>
        {[0, 0.5, 1].map((g, i) => (
          <g key={i}>
            <line x1={padL} x2={w - padR} y1={padT + innerH * (1-g)} y2={padT + innerH * (1-g)}
              stroke="rgba(255,255,255,0.05)" strokeDasharray="3 4" />
            <text x={padL - 6} y={padT + innerH * (1-g) + 4} fontSize="10"
              fill="var(--text-faint)" textAnchor="end">{Math.round(max * g)}</text>
          </g>
        ))}
        {data.map((d, i) => {
          const h = innerH * (d.value / max) * t;
          const x = padL + i * (innerW / data.length) + gap / 2;
          const y = padT + innerH - h;
          const isH = hover === i;
          return (
            <g key={i}
              onMouseEnter={() => setHover(i)}
              onMouseLeave={() => setHover(null)}
              onClick={() => onBarClick && onBarClick(d)}
              style={{ cursor: onBarClick ? "pointer" : "default" }}>
              <defs>
                <linearGradient id={`hg${i}`} x1="0" x2="0" y1="0" y2="1">
                  <stop offset="0%" stopColor={color} stopOpacity={isH ? 1 : 0.9} />
                  <stop offset="100%" stopColor={color} stopOpacity={0.3} />
                </linearGradient>
              </defs>
              <rect x={x} y={y} width={barW} height={h} rx="3"
                fill={`url(#hg${i})`}
                style={{ filter: isH ? `drop-shadow(0 0 10px ${color})` : "none", transition: "filter .2s" }} />
              <text x={x + barW/2} y={padT + innerH + 16} fontSize="10" textAnchor="middle"
                fill={isH ? color : "var(--text-dim)"}>{d.label}</text>
              {isH && (
                <text x={x + barW/2} y={y - 4} fontSize="11" textAnchor="middle"
                  fill={color} style={{ fontWeight: 600, fontFamily: "var(--font-num)" }}>{d.value}</text>
              )}
            </g>
          );
        })}
      </svg>
    </div>
  );
};

Object.assign(window, { useAnimatedNumber, useReveal, AnimatedCounter, DonutChart, BarChart, LineChart, Histogram });
