// SVG-based chart components

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

// ============ Helper: smooth path ============
function smoothPath(points) {
  if (points.length < 2) return "";
  const d = [`M ${points[0].x} ${points[0].y}`];
  for (let i = 0; i < points.length - 1; i++) {
    const p0 = points[i - 1] || points[i];
    const p1 = points[i];
    const p2 = points[i + 1];
    const p3 = points[i + 2] || p2;
    const t = 0.18;
    const c1x = p1.x + (p2.x - p0.x) * t;
    const c1y = p1.y + (p2.y - p0.y) * t;
    const c2x = p2.x - (p3.x - p1.x) * t;
    const c2y = p2.y - (p3.y - p1.y) * t;
    d.push(`C ${c1x} ${c1y} ${c2x} ${c2y} ${p2.x} ${p2.y}`);
  }
  return d.join(" ");
}

// ============ Trend chart (multi-metric) ============
function TrendChart({ metric = "weight", height = 280, onSelectWeek, selectedWeek, data: propData }) {
  const data = propData || window.WEEKLY_DATA;
  const wrapRef = useRef(null);
  const lineRef = useRef(null);
  const [hoverIdx, setHoverIdx] = useState(null);
  const [drawn, setDrawn] = useState(false);
  const [pathLen, setPathLen] = useState(3000);
  const [visible, setVisible] = useState(false);
  const drawTimerRef = useRef(null);

  const ACCENT = "#3b6ea5";
  const config = {
    weight: { label: "體重", unit: "kg", color: ACCENT, fill: "rgba(59,110,165,0.10)", goal: 75 },
    bodyFatPct: { label: "體脂率", unit: "%", color: ACCENT, fill: "rgba(59,110,165,0.10)", goal: 23 },
    muscle: { label: "肌肉量", unit: "kg", color: ACCENT, fill: "rgba(59,110,165,0.10)", goal: 63.8 },
    bmi: { label: "BMI", unit: "", color: ACCENT, fill: "rgba(59,110,165,0.10)", goal: 24 },
    bmr: { label: "基礎代謝", unit: "kcal", color: ACCENT, fill: "rgba(59,110,165,0.10)", goal: null },
    bodyFat: { label: "體脂量", unit: "kg", color: ACCENT, fill: "rgba(59,110,165,0.10)", goal: 21 },
    visceralFat: { label: "內臟脂肪指數", unit: "", color: ACCENT, fill: "rgba(59,110,165,0.10)", goal: 9 },
    waterPct: { label: "體水分率", unit: "%", color: ACCENT, fill: "rgba(59,110,165,0.10)", goal: 55 },
  };
  const cfg = config[metric];

  const padding = { top: 30, right: 30, bottom: 40, left: 46 };
  const W = 720, H = height;
  const innerW = W - padding.left - padding.right;
  const innerH = H - padding.top - padding.bottom;

  const values = data.map(d => d[metric]);
  let minV = Math.min(...values, cfg.goal ?? Infinity);
  let maxV = Math.max(...values, cfg.goal ?? -Infinity);
  const range = maxV - minV || 1;
  minV -= range * 0.18;
  maxV += range * 0.18;

  const points = data.map((d, i) => ({
    x: padding.left + (i / (data.length - 1)) * innerW,
    y: padding.top + (1 - (d[metric] - minV) / (maxV - minV)) * innerH,
    v: d[metric],
    week: d.week,
    date: d.date,
  }));

  const linePath = smoothPath(points);
  const areaPath = `${linePath} L ${points[points.length-1].x} ${padding.top + innerH} L ${points[0].x} ${padding.top + innerH} Z`;

  // y-axis ticks
  const yTicks = useMemo(() => {
    const arr = [];
    for (let i = 0; i <= 4; i++) {
      const t = minV + ((maxV - minV) * (i / 4));
      arr.push({ y: padding.top + innerH - (i / 4) * innerH, v: t });
    }
    return arr;
  }, [minV, maxV]);

  const goalY = cfg.goal != null
    ? padding.top + (1 - (cfg.goal - minV) / (maxV - minV)) * innerH
    : null;

  useEffect(() => {
    if (!wrapRef.current) return;
    const io = new IntersectionObserver(([entry]) => setVisible(entry.isIntersecting), { threshold: 0.2 });
    io.observe(wrapRef.current);
    return () => io.disconnect();
  }, []);

  useEffect(() => {
    clearTimeout(drawTimerRef.current);
    if (!visible) { setDrawn(false); return; }
    if (lineRef.current) setPathLen(lineRef.current.getTotalLength() || 3000);
    setDrawn(false);
    drawTimerRef.current = setTimeout(() => setDrawn(true), 80);
    return () => clearTimeout(drawTimerRef.current);
  }, [visible, linePath]);

  return (
    <div className="chart-wrap" style={{ position: "relative" }} ref={wrapRef}>
      <svg viewBox={`0 0 ${W} ${H}`} width="100%" preserveAspectRatio="none" style={{ overflow: "visible" }}>
        <defs>
          <linearGradient id={`grad-${metric}`} x1="0" y1="0" x2="0" y2="1">
            <stop offset="0%" stopColor={cfg.color} stopOpacity="0.32" />
            <stop offset="100%" stopColor={cfg.color} stopOpacity="0.02" />
          </linearGradient>
        </defs>

        {/* y grid */}
        {yTicks.map((t, i) => (
          <g key={i}>
            <line x1={padding.left} x2={W - padding.right} y1={t.y} y2={t.y} stroke="#e6e6e2" strokeDasharray="2 4" />
            <text x={padding.left - 12} y={t.y + 4} fontSize="12" fill="#9aa0a8" textAnchor="end" fontFamily="Fraunces, serif">
              {t.v.toFixed(metric === "bmr" ? 0 : 1)}
            </text>
          </g>
        ))}

        {/* goal line */}
        {goalY != null && goalY > padding.top && goalY < padding.top + innerH && (
          <g>
            <line x1={padding.left} x2={W - padding.right} y1={goalY} y2={goalY}
                  stroke="#9aa0a8" strokeWidth="1" strokeDasharray="4 4" />
            <text x={W - padding.right} y={goalY - 6} fontSize="12" fill="#6a6e76" textAnchor="end" fontWeight="500" letterSpacing="1.5">
              理想 {cfg.goal}{cfg.unit}
            </text>
          </g>
        )}

        {/* area */}
        <path d={areaPath} fill={`url(#grad-${metric})`} style={{ opacity: drawn ? 1 : 0, transition: "opacity 0.6s ease 0.8s" }} />
        {/* line */}
        <path
          ref={lineRef}
          d={linePath} fill="none" stroke={cfg.color} strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"
          strokeDasharray={pathLen}
          strokeDashoffset={drawn ? 0 : pathLen}
          style={{ transition: drawn ? "stroke-dashoffset 1.3s cubic-bezier(0.4,0,0.2,1)" : "none" }}
        />

        {/* points */}
        {points.map((p, i) => {
          const isSel = selectedWeek === data[i].week;
          const isHover = hoverIdx === i;
          const delay = drawn ? `${0.7 + i * 0.1}s` : "0s";
          return (
            <g key={i}
               style={{ cursor: "pointer", opacity: drawn ? 1 : 0, transition: `opacity 0.35s ease ${delay}` }}
               onMouseEnter={() => setHoverIdx(i)}
               onMouseLeave={() => setHoverIdx(null)}
               onClick={() => onSelectWeek && onSelectWeek(data[i].week)}>
              <circle cx={p.x} cy={p.y} r={isHover || isSel ? 9 : 6} fill="white" stroke={cfg.color} strokeWidth="2.5" />
              {(isHover || isSel) && (
                <circle cx={p.x} cy={p.y} r={3} fill={cfg.color} />
              )}
              <circle cx={p.x} cy={p.y} r="14" fill="transparent" />
              {/* x label */}
              <text x={p.x} y={H - padding.bottom + 20} fontSize="12" fill={isSel ? cfg.color : "#6a6e76"}
                    textAnchor="middle" fontWeight={isSel ? 600 : 500} letterSpacing="1.5">
                W{String(data[i].week).padStart(2, '0')}
              </text>
              <text x={p.x} y={H - padding.bottom + 34} fontSize="12" fill="#9aa0a8" textAnchor="middle" letterSpacing="1">
                {data[i].date.slice(5)}
              </text>
            </g>
          );
        })}
      </svg>

      {hoverIdx != null && (
        <div className="chart-tooltip on" style={{
          left: `${(points[hoverIdx].x / W) * 100}%`,
          top: `${(points[hoverIdx].y / H) * 100}%`,
        }}>
          <strong>{points[hoverIdx].v}{cfg.unit}</strong>
          {cfg.label} · 第 {data[hoverIdx].week} 週
        </div>
      )}
    </div>
  );
}

// ============ Composition stacked bar (per week) ============
function CompositionChart({ height = 220, data: propData, showLegend = true }) {
  const data = propData || window.WEEKLY_DATA;
  const W = 720, H = height;
  const padding = { top: 24, right: 30, bottom: 40, left: 46 };
  const innerW = W - padding.left - padding.right;
  const innerH = H - padding.top - padding.bottom;
  const barW = innerW / data.length * 0.5;

  // each week: muscle, fat, water-other (lean - muscle), bone
  // We'll show: 肌肉 / 體脂 / 其他除脂(骨+水分結構)
  const maxVal = 110;

  return (
    <div className="chart-wrap">
      <svg viewBox={`0 0 ${W} ${H}`} width="100%" preserveAspectRatio="none">
        <defs>
          <linearGradient id="gradMuscle" x1="0" y1="0" x2="0" y2="1">
            <stop offset="0%" stopColor="#3b6ea5" />
            <stop offset="100%" stopColor="#2d568a" />
          </linearGradient>
          <linearGradient id="gradFat" x1="0" y1="0" x2="0" y2="1">
            <stop offset="0%" stopColor="#9ab4d1" />
            <stop offset="100%" stopColor="#7a9bc0" />
          </linearGradient>
          <linearGradient id="gradOther" x1="0" y1="0" x2="0" y2="1">
            <stop offset="0%" stopColor="#d4d4ce" />
            <stop offset="100%" stopColor="#bfbfb8" />
          </linearGradient>
        </defs>

        {/* y grid */}
        {[0, 25, 50, 75, 100].map((v, i) => {
          const y = padding.top + innerH - (v / maxVal) * innerH;
          return (
            <g key={i}>
              <line x1={padding.left} x2={W - padding.right} y1={y} y2={y}
                    stroke="#e6e6e2" strokeDasharray="2 4" />
              <text x={padding.left - 12} y={y + 4} fontSize="12" fill="#9aa0a8"
                    textAnchor="end" fontFamily="Fraunces, serif">{v}</text>
            </g>
          );
        })}

        {data.map((d, i) => {
          const cx = padding.left + (i + 0.5) * (innerW / data.length);
          const x = cx - barW / 2;
          const muscle = d.muscle;
          const fat = d.bodyFat;
          const other = d.weight - muscle - fat;

          const yMuscle = padding.top + innerH - (muscle / maxVal) * innerH;
          const hMuscle = (muscle / maxVal) * innerH;
          const yFat = yMuscle - (fat / maxVal) * innerH;
          const hFat = (fat / maxVal) * innerH;
          const yOther = yFat - (other / maxVal) * innerH;
          const hOther = (other / maxVal) * innerH;

          return (
            <g key={i}>
              <rect x={x} y={yMuscle} width={barW} height={hMuscle} fill="url(#gradMuscle)" />
              <rect x={x} y={yFat} width={barW} height={hFat} fill="url(#gradFat)" />
              <rect x={x} y={yOther} width={barW} height={hOther} fill="url(#gradOther)" />
              <text x={cx} y={yOther - 10} fontSize="13" fontWeight="400" fill="#16181c"
                    textAnchor="middle" fontFamily="Fraunces, serif">{d.weight}</text>
              <text x={cx} y={H - padding.bottom + 20} fontSize="12" fill="#6a6e76" textAnchor="middle" letterSpacing="1.5" fontWeight="500">
                W{String(d.week).padStart(2, '0')}
              </text>
              <text x={cx} y={H - padding.bottom + 34} fontSize="12" fill="#9aa0a8" textAnchor="middle" letterSpacing="1">
                {d.date.slice(5)}
              </text>
            </g>
          );
        })}
      </svg>
      {showLegend && (
        <div className="chart-legend" style={{ marginTop: 16, paddingLeft: 46 }}>
          <span><span className="legend-dot" style={{ background: "#3b6ea5" }}></span>肌肉量</span>
          <span><span className="legend-dot" style={{ background: "#9ab4d1" }}></span>體脂量</span>
          <span><span className="legend-dot" style={{ background: "#d4d4ce" }}></span>骨量 + 其他</span>
        </div>
      )}
    </div>
  );
}

// ============ Goal Ring ============
function GoalRing({ progress, size = 160, label }) {
  const [animPct, setAnimPct] = useState(0);
  const ringRef = useRef(null);

  useEffect(() => {
    if (!ringRef.current) return;
    const io = new IntersectionObserver(([entry]) => {
      if (entry.isIntersecting) {
        const tid = setTimeout(() => setAnimPct(progress), 150);
        return () => clearTimeout(tid);
      } else {
        setAnimPct(0);
      }
    }, { threshold: 0.5 });
    io.observe(ringRef.current);
    return () => io.disconnect();
  }, [progress]);

  const stroke = 14;
  const r = (size - stroke) / 2;
  const c = 2 * Math.PI * r;
  const offset = c * (1 - Math.max(0, Math.min(1, animPct)));

  return (
    <svg ref={ringRef} width={size} height={size}>
      <defs>
        <linearGradient id="ringGrad" x1="0" y1="0" x2="1" y2="1">
          <stop offset="0%" stopColor="#3b6ea5" />
          <stop offset="100%" stopColor="#2d568a" />
        </linearGradient>
      </defs>
      <circle cx={size/2} cy={size/2} r={r} fill="none" stroke="#e6e6e2" strokeWidth={stroke} />
      <circle cx={size/2} cy={size/2} r={r} fill="none"
              stroke="url(#ringGrad)" strokeWidth={stroke}
              strokeDasharray={c} strokeDashoffset={offset}
              strokeLinecap="round"
              transform={`rotate(-90 ${size/2} ${size/2})`}
              style={{ transition: "stroke-dashoffset 1s cubic-bezier(0.16,1,0.3,1)" }} />
      <text x={size/2} y={size/2 + 4} fontSize="34" fill="#16181c" textAnchor="middle"
            fontFamily="Fraunces, serif" fontWeight="300" letterSpacing="-1">
        {(animPct * 100).toFixed(1)}
      </text>
      <text x={size/2} y={size/2 + 22} fontSize="12" fill="#9aa0a8" textAnchor="middle" letterSpacing="3" fontWeight="500">PERCENT</text>
      {label && (
        <text x={size/2} y={size - 6} fontSize="12" fill="#6a6e76" textAnchor="middle" letterSpacing="3" fontWeight="500">
          {label.toUpperCase()}
        </text>
      )}
    </svg>
  );
}

// ============ Sparkline ============
function Sparkline({ values, color = "#3b6ea5", width = 120, height = 40 }) {
  if (color && color.startsWith("var(")) color = "#3b6ea5";
  const min = Math.min(...values);
  const max = Math.max(...values);
  const range = max - min || 1;
  const pts = values.map((v, i) => ({
    x: (i / (values.length - 1)) * width,
    y: height - ((v - min) / range) * (height - 6) - 3,
  }));
  const path = smoothPath(pts);
  const area = `${path} L ${width} ${height} L 0 ${height} Z`;
  return (
    <svg width={width} height={height} viewBox={`0 0 ${width} ${height}`}>
      <defs>
        <linearGradient id={`sg-${color.slice(1)}`} x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%" stopColor={color} stopOpacity="0.3" />
          <stop offset="100%" stopColor={color} stopOpacity="0" />
        </linearGradient>
      </defs>
      <path d={area} fill={`url(#sg-${color.slice(1)})`} />
      <path d={path} fill="none" stroke={color} strokeWidth="2" strokeLinecap="round" />
      <circle cx={pts[pts.length-1].x} cy={pts[pts.length-1].y} r="3" fill={color} />
    </svg>
  );
}

// ============ Body silhouette with part labels ============
function BodyViz({ week }) {
  const w = window.WEEKLY_DATA.find(d => d.week === week) || window.WEEKLY_DATA[window.WEEKLY_DATA.length - 1];
  const parts = w.parts;
  const maxFat = Math.max(parts.fatPct.trunk, parts.fatPct.leftArm, parts.fatPct.rightArm, parts.fatPct.leftLeg, parts.fatPct.rightLeg);
  const colorAt = (pct) => {
    const r = Math.min(1, pct / 40);
    // single-accent ramp: pale blue -> deep blue
    const l = 88 - r * 30;
    return `hsl(212, ${30 + r * 30}%, ${l}%)`;
  };
  return (
    <div className="body-viz">
      <svg viewBox="0 0 200 280" className="body-svg">
        {/* head */}
        <circle cx="100" cy="34" r="22" fill="#e6e6e2" stroke="#d4d4ce" strokeWidth="1" />
        {/* trunk */}
        <path d="M70 70 Q70 60 80 60 L120 60 Q130 60 130 70 L138 150 Q138 158 130 158 L70 158 Q62 158 62 150 Z"
              fill={colorAt(parts.fatPct.trunk)} stroke="#d4d4ce" strokeWidth="1" />
        {/* left arm */}
        <path d="M62 76 Q50 80 46 100 L38 150 Q36 160 44 162 L52 160 Q58 158 60 148 L66 100 Z"
              fill={colorAt(parts.fatPct.leftArm)} stroke="#d4d4ce" strokeWidth="1" />
        {/* right arm */}
        <path d="M138 76 Q150 80 154 100 L162 150 Q164 160 156 162 L148 160 Q142 158 140 148 L134 100 Z"
              fill={colorAt(parts.fatPct.rightArm)} stroke="#d4d4ce" strokeWidth="1" />
        {/* left leg */}
        <path d="M72 158 L70 250 Q70 262 82 262 L92 262 Q100 262 100 252 L100 158 Z"
              fill={colorAt(parts.fatPct.leftLeg)} stroke="#d4d4ce" strokeWidth="1" />
        {/* right leg */}
        <path d="M128 158 L130 250 Q130 262 118 262 L108 262 Q100 262 100 252 L100 158 Z"
              fill={colorAt(parts.fatPct.rightLeg)} stroke="#d4d4ce" strokeWidth="1" />
      </svg>
      <div className="part-grid">
        {[
          ["軀幹", parts.fatPct.trunk, parts.muscle.trunk, parts.fat.trunk],
          ["左手", parts.fatPct.leftArm, parts.muscle.leftArm, parts.fat.leftArm],
          ["右手", parts.fatPct.rightArm, parts.muscle.rightArm, parts.fat.rightArm],
          ["左腳", parts.fatPct.leftLeg, parts.muscle.leftLeg, parts.fat.leftLeg],
          ["右腳", parts.fatPct.rightLeg, parts.muscle.rightLeg, parts.fat.rightLeg],
        ].map(([name, pct, m, f], i) => (
          <div key={i} style={{ display: "flex", flexDirection: "column", gap: 4 }}>
            <div className="part-row">
              <span className="pname">{name}</span>
              <span className="pval">{pct}<span style={{ fontSize: 10, color: "#9A8B82", marginLeft: 2 }}>%</span></span>
            </div>
            <div className="part-bar"><div style={{ width: `${(pct / 40) * 100}%` }}></div></div>
            <div style={{ fontSize: 10, color: "#9A8B82" }}>肌 {m}kg · 脂 {f}kg</div>
          </div>
        ))}
      </div>
    </div>
  );
}

window.TrendChart = TrendChart;
window.CompositionChart = CompositionChart;
window.GoalRing = GoalRing;
window.Sparkline = Sparkline;
window.BodyViz = BodyViz;
