// SVG charts for tax dashboard. All hand-drawn, dark-theme aware via CSS vars.

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

// === Tooltip ===
function useTooltip() {
  const [tt, setTT] = useState(null);
  // anchorSvgX/Y are coords inside the SVG viewBox; tooltip shows above-right of point
  const onMove = (e, content, anchorSvgX, anchorSvgY) => {
    const svg = e.currentTarget.ownerSVGElement || e.currentTarget;
    const rect = svg.getBoundingClientRect();
    const W_TT = 220, H_TT = 140;
    let x, y;
    if (anchorSvgX != null && anchorSvgY != null && !isNaN(anchorSvgX) && !isNaN(anchorSvgY) && svg.viewBox?.baseVal?.width) {
      const sx = rect.width / svg.viewBox.baseVal.width;
      const sy = rect.height / svg.viewBox.baseVal.height;
      const px = rect.left + anchorSvgX * sx;
      const py = rect.top + anchorSvgY * sy;
      // Position tooltip above the point, horizontally centered
      x = px - W_TT / 2;
      y = py - H_TT - 12;
      // If above goes off top, place below
      if (y < 8) y = py + 14;
    } else {
      x = e.clientX + 14;
      y = e.clientY + 14;
    }
    if (x + W_TT > window.innerWidth - 8) x = window.innerWidth - W_TT - 8;
    if (x < 8) x = 8;
    if (y + H_TT > window.innerHeight - 8) y = window.innerHeight - H_TT - 8;
    if (y < 8) y = 8;
    setTT({ x, y, content });
  };
  const onLeave = () => setTT(null);
  const node = tt ? (
    ReactDOM.createPortal(
      <div className="tt" style={{ left: tt.x, top: tt.y }}>
        {tt.content}
      </div>,
      document.body
    )
  ) : null;
  return { node, onMove, onLeave };
}

// === Format ===
function fmt(n, unit) {
  if (n == null) return '—';
  if (unit === 'wan') {
    const w = n / 10000;
    return w.toFixed(w >= 100 ? 1 : 2);
  }
  return new Intl.NumberFormat('en-US').format(Math.round(n));
}
function fmtUnit(unit) { return unit === 'wan' ? '萬' : '元'; }
function pct(n) { if (n == null) return '—'; return (n * 100).toFixed(1) + '%'; }

// === LineChart (multi-series) ===
function LineChart({ data, series, unit, type = 'line', height = 280 }) {
  // data: [{year, ...keys}]; series: [{key, label, color, dashed?}]
  const W = 760, H = height;
  const padL = 60, padR = 24, padT = 20, padB = 36;
  const innerW = W - padL - padR;
  const innerH = H - padT - padB;
  const tt = useTooltip();
  const [hoverIdx, setHoverIdx] = useState(null);

  const allVals = data.flatMap(d => series.map(s => d[s.key])).filter(v => v != null);
  if (!allVals.length) return null;
  const maxV = Math.max(...allVals);
  const minV = 0;
  const range = maxV - minV || 1;

  const x = i => padL + (data.length === 1 ? innerW / 2 : (i / (data.length - 1)) * innerW);
  const y = v => padT + innerH - ((v - minV) / range) * innerH;

  // y ticks (5)
  const ticks = 5;
  const tickVals = Array.from({ length: ticks + 1 }, (_, i) => maxV * (i / ticks));

  return (
    <div style={{ position: 'relative' }}>
      <svg className="chart-svg" viewBox={`0 0 ${W} ${H}`} preserveAspectRatio="none">
        {/* grid */}
        {tickVals.map((v, i) => (
          <g key={i}>
            <line className="grid-line" x1={padL} x2={W - padR} y1={y(v)} y2={y(v)} />
            <text className="axis-text value" x={padL - 8} y={y(v) + 3.5} textAnchor="end">
              {fmt(v, unit)}
            </text>
          </g>
        ))}
        {/* x labels */}
        {data.map((d, i) => (
          <text key={i} className="axis-text" x={x(i)} y={H - padB + 18} textAnchor="middle">
            {d.year - 1911} 年度
          </text>
        ))}

        {/* areas (if type=area) */}
        {type === 'area' && series.map((s, si) => {
          const pts = data.map((d, i) => `${x(i)},${y(d[s.key] ?? 0)}`).join(' ');
          const areaPts = `${padL},${y(0)} ` + pts + ` ${x(data.length - 1)},${y(0)}`;
          return (
            <polygon key={'a-' + s.key} points={areaPts}
              fill={s.color} opacity="0.12" />
          );
        })}

        {/* lines */}
        {series.map((s, si) => {
          const pts = data.map((d, i) => `${x(i)},${y(d[s.key] ?? 0)}`).join(' ');
          return (
            <polyline key={s.key}
              fill="none"
              stroke={s.color}
              strokeWidth="2.25"
              strokeDasharray={s.dashed ? '5 4' : 'none'}
              strokeLinecap="round"
              strokeLinejoin="round"
              points={pts}
              style={{
                strokeDasharray: s.dashed ? '5 4' : 1500,
                strokeDashoffset: s.dashed ? 0 : 1500,
                animation: `drawLine 0.7s ${si * 0.1}s ease forwards`
              }} />
          );
        })}
        <style>{`
          @keyframes drawLine {
            to { stroke-dashoffset: 0; }
          }
        `}</style>

        {/* points + labels */}
        {series.map((s, si) => data.map((d, i) => {
          const v = d[s.key];
          if (v == null) return null;
          return (
            <g key={s.key + '-' + i}>
              <circle cx={x(i)} cy={y(v)} r="3.5" fill="var(--card)" stroke={s.color} strokeWidth="2"
                style={{ opacity: 0, animation: `fadeIn 0.4s ${0.5 + si * 0.1 + i * 0.05}s ease forwards` }} />
            </g>
          );
        }))}
        <style>{`@keyframes fadeIn { to { opacity: 1; } }`}</style>

        {/* hover overlays */}
        {data.map((d, i) => {
          const w = innerW / Math.max(data.length, 1);
          return (
            <rect key={'h' + i}
              x={x(i) - w / 2} y={padT}
              width={w} height={innerH}
              fill="transparent"
              onMouseMove={e => {
                setHoverIdx(i);
                const prev = data[i - 1];
                const ys = series.map(s => d[s.key] != null ? y(d[s.key]) : null).filter(v => v != null && !isNaN(v));
                const ay = ys.length ? Math.min(...ys) : padT;
                tt.onMove(e, (
                  <div>
                    <div className="tt-title">{d.year - 1911} 年度（{d.year}）</div>
                    {series.map(s => {
                      const v = d[s.key];
                      const pv = prev ? prev[s.key] : null;
                      const delta = (v != null && pv != null && pv !== 0) ? (v - pv) / pv : null;
                      return (
                        <div key={s.key} className="tt-row">
                          <span className="lbl">
                            <span style={{
                              width: 8, height: 8, borderRadius: 2,
                              background: s.color, display: 'inline-block'
                            }}></span>
                            {s.label}
                          </span>
                          <span className="val">
                            {fmt(v, unit)} {fmtUnit(unit)}
                            {delta != null && (
                              <span style={{ marginLeft: 6, fontSize: 12.5, color: delta > 0 ? '#c99a73' : '#6fa896' }}>
                                {delta > 0 ? '↑' : '↓'}{Math.abs(delta * 100).toFixed(1)}%
                              </span>
                            )}
                          </span>
                        </div>
                      );
                    })}
                  </div>
                ), x(i), ay);
              }}
              onMouseLeave={() => { setHoverIdx(null); tt.onLeave(); }}
            />
          );
        })}

        {/* hover line */}
        {hoverIdx != null && (
          <line x1={x(hoverIdx)} x2={x(hoverIdx)} y1={padT} y2={H - padB}
            stroke="var(--accent-1)" strokeWidth="1" strokeDasharray="3 3" opacity="0.5" />
        )}

        {/* point labels (above last series for clarity) */}
        {data.map((d, i) => {
          const v = d[series[0].key];
          if (v == null) return null;
          return (
            <text key={'lbl' + i} className="point-label bold"
              x={x(i)} y={y(v) - 10} textAnchor="middle"
              style={{ opacity: 0, animation: `fadeIn 0.4s ${0.7 + i * 0.05}s ease forwards` }}>
              {fmt(v, unit)}
            </text>
          );
        })}
      </svg>
      {tt.node}
    </div>
  );
}

// === Stacked bar chart with optional secondary line ===
function StackedBarChart({ data, stacks, line, unit, height = 320 }) {
  // data: [{year, ...stackKeys, lineKey}]; stacks: [{key,label,color}]; line: {key,label,color,dashed}
  const W = 760, H = height;
  const padL = 60, padR = line ? 60 : 24, padT = 20, padB = 36;
  const innerW = W - padL - padR;
  const innerH = H - padT - padB;
  const tt = useTooltip();
  const [hoverIdx, setHoverIdx] = useState(null);

  const totals = data.map(d => stacks.reduce((s, st) => s + (d[st.key] || 0), 0));
  const maxStack = Math.max(...totals, 1);
  const lineVals = line ? data.map(d => d[line.key] || 0) : [];
  const maxLine = lineVals.length ? Math.max(...lineVals, 1) : 1;

  const tickCount = 5;
  const stackTicks = Array.from({ length: tickCount + 1 }, (_, i) => maxStack * (i / tickCount));
  const lineTicks = Array.from({ length: tickCount + 1 }, (_, i) => maxLine * (i / tickCount));

  const xBand = innerW / data.length;
  const barW = Math.min(64, xBand * 0.5);

  const yL = v => padT + innerH - (v / maxStack) * innerH;
  const yR = v => padT + innerH - (v / maxLine) * innerH;

  return (
    <div style={{ position: 'relative' }}>
      <svg className="chart-svg" viewBox={`0 0 ${W} ${H}`} preserveAspectRatio="none">
        {/* grid + left axis */}
        {stackTicks.map((v, i) => (
          <g key={i}>
            <line className="grid-line" x1={padL} x2={W - padR} y1={yL(v)} y2={yL(v)} />
            <text className="axis-text value" x={padL - 8} y={yL(v) + 3.5} textAnchor="end">
              {fmt(v, unit)}
            </text>
          </g>
        ))}
        {/* right axis */}
        {line && lineTicks.map((v, i) => (
          <text key={'r' + i} className="axis-text value" x={W - padR + 8} y={yR(v) + 3.5} textAnchor="start">
            {fmt(v, unit)}
          </text>
        ))}

        {/* bars */}
        {data.map((d, i) => {
          const cx = padL + xBand * i + xBand / 2;
          let yCursor = padT + innerH;
          return (
            <g key={i}>
              {stacks.map((st, si) => {
                const v = d[st.key] || 0;
                const h = (v / maxStack) * innerH;
                const yTop = yCursor - h;
                const isFirst = si === 0;
                const isLast = si === stacks.length - 1;
                const rect = (
                  <rect key={st.key}
                    x={cx - barW / 2}
                    y={yTop}
                    width={barW}
                    height={h}
                    fill={st.color}
                    rx={isLast ? 4 : 0}
                    style={{
                      transformOrigin: `${cx}px ${padT + innerH}px`,
                      transform: 'scaleY(0)',
                      animation: `barGrow 0.5s ${i * 0.08 + si * 0.05}s ease forwards`
                    }} />
                );
                yCursor = yTop;
                return rect;
              })}
              {/* total label */}
              <text className="point-label bold"
                x={cx} y={yCursor - 8} textAnchor="middle"
                style={{ opacity: 0, animation: `fadeIn 0.4s ${0.6 + i * 0.08}s ease forwards` }}>
                {fmt(totals[i], unit)}
              </text>
              {/* hover */}
              <rect x={padL + xBand * i} y={padT} width={xBand} height={innerH} fill="transparent"
                onMouseMove={e => {
                  setHoverIdx(i);
                  const prev = data[i - 1];
                  const prevT = prev ? stacks.reduce((s, st) => s + (prev[st.key] || 0), 0) : null;
                  const delta = (prevT && totals[i]) ? (totals[i] - prevT) / prevT : null;
                  tt.onMove(e, (
                    <div>
                      <div className="tt-title">{d.year - 1911} 年度</div>
                      {stacks.map(st => (
                        <div key={st.key} className="tt-row">
                          <span className="lbl">
                            <span style={{ width: 8, height: 8, borderRadius: 2, background: st.color, display: 'inline-block' }}></span>
                            {st.label}
                          </span>
                          <span className="val">{fmt(d[st.key], unit)}</span>
                        </div>
                      ))}
                      {line && (
                        <div className="tt-row" style={{ borderTop: '1px solid rgba(255,255,255,0.1)', paddingTop: 6, marginTop: 4 }}>
                          <span className="lbl">
                            <span style={{ width: 12, height: 0, borderTop: `2px dashed ${line.color}`, display: 'inline-block' }}></span>
                            {line.label}
                          </span>
                          <span className="val">{fmt(d[line.key], unit)}</span>
                        </div>
                      )}
                      <div className="tt-foot">
                        合計 {fmt(totals[i], unit)} {fmtUnit(unit)}
                        {delta != null && (
                          <span style={{ marginLeft: 8, color: delta > 0 ? '#6fa896' : '#c97a7a' }}>
                            {delta > 0 ? '↑' : '↓'}{Math.abs(delta * 100).toFixed(1)}%
                          </span>
                        )}
                      </div>
                    </div>
                  ), padL + xBand * i + xBand / 2, yCursor);
                }}
                onMouseLeave={() => { setHoverIdx(null); tt.onLeave(); }}
              />
            </g>
          );
        })}

        {/* secondary line */}
        {line && (
          <>
            <polyline
              fill="none" stroke={line.color} strokeWidth="2"
              strokeDasharray="6 4"
              strokeLinecap="round"
              points={data.map((d, i) => `${padL + xBand * i + xBand / 2},${yR(d[line.key] || 0)}`).join(' ')}
              style={{
                strokeDasharray: '6 4',
                opacity: 0,
                animation: 'fadeIn 0.6s 0.6s ease forwards'
              }} />
            {data.map((d, i) => (
              <circle key={'lp' + i}
                cx={padL + xBand * i + xBand / 2} cy={yR(d[line.key] || 0)}
                r="3" fill={line.color}
                style={{ opacity: 0, animation: `fadeIn 0.4s ${0.7 + i * 0.05}s ease forwards` }} />
            ))}
          </>
        )}

        {/* x labels */}
        {data.map((d, i) => (
          <text key={'x' + i} className="axis-text"
            x={padL + xBand * i + xBand / 2} y={H - padB + 18} textAnchor="middle">
            {d.year - 1911} 年度
          </text>
        ))}
        <style>{`
          @keyframes barGrow { to { transform: scaleY(1); } }
        `}</style>
      </svg>
      {tt.node}
    </div>
  );
}

// === Donut chart (family contribution) ===
function DonutChart({ slices, centerLabel, centerValue, unit, size = 220 }) {
  const cx = size / 2, cy = size / 2;
  const r = size * 0.42;
  const strokeW = size * 0.13;
  const total = slices.reduce((s, sl) => s + (sl.value || 0), 0);
  if (!total) return <div style={{ color: 'var(--text-3)' }}>無資料</div>;

  let cumulative = 0;
  const circumference = 2 * Math.PI * r;

  return (
    <div style={{ position: 'relative', width: size, height: size, margin: '0 auto' }}>
      <svg width={size} height={size} style={{ transform: 'rotate(-90deg)' }}>
        <circle cx={cx} cy={cy} r={r} fill="none" stroke="var(--divider)" strokeWidth={strokeW} />
        {slices.map((sl, i) => {
          const frac = sl.value / total;
          const dash = frac * circumference;
          const offset = -cumulative * circumference;
          cumulative += frac;
          return (
            <circle key={sl.label}
              cx={cx} cy={cy} r={r}
              fill="none"
              stroke={sl.color}
              strokeWidth={strokeW}
              strokeDasharray={`${dash} ${circumference - dash}`}
              strokeDashoffset={offset}
              strokeLinecap="butt"
              style={{
                transition: 'stroke-dasharray 0.6s ease',
                opacity: 0,
                animation: `fadeIn 0.5s ${i * 0.15}s ease forwards`
              }} />
          );
        })}
      </svg>
      <div style={{
        position: 'absolute', inset: 0,
        display: 'grid', placeItems: 'center', textAlign: 'center'
      }}>
        <div>
          <div style={{ fontSize: 12.5, color: 'var(--text-3)', marginBottom: 2 }}>{centerLabel}</div>
          <div style={{ fontSize: 22, fontWeight: 700, fontVariantNumeric: 'tabular-nums' }}>
            {fmt(centerValue, unit)}
          </div>
          <div style={{ fontSize: 12, color: 'var(--text-3)', marginTop: 2 }}>{fmtUnit(unit)}</div>
        </div>
      </div>
    </div>
  );
}

// === Spouse chart: grouped bars (salary + non-salary side by side) ===
function SpouseBarChart({ data, salaryKey, salaryLabel, salaryColor, nonSalaryKey, nonSalaryLabel, nonSalaryColor, unit, height = 280 }) {
  const W = 760, H = height;
  const padL = 60, padR = 24, padT = 30, padB = 36;
  const innerW = W - padL - padR;
  const innerH = H - padT - padB;
  const tt = useTooltip();

  const salaryVals = data.map(d => d._missing ? 0 : (d[salaryKey] || 0));
  const nonSalaryVals = data.map(d => d._missing ? 0 : (d[nonSalaryKey] || 0));
  const totals = data.map((_, i) => salaryVals[i] + nonSalaryVals[i]);
  const maxV = Math.max(...totals, 1);

  const tickCount = 5;
  const ticks = Array.from({ length: tickCount + 1 }, (_, i) => maxV * (i / tickCount));
  const xBand = innerW / data.length;
  const barW = Math.min(56, xBand * 0.42);
  const yL = v => padT + innerH - (v / maxV) * innerH;

  return (
    <div style={{ position: 'relative' }}>
      <svg className="chart-svg" viewBox={`0 0 ${W} ${H}`} preserveAspectRatio="none">
        {ticks.map((v, i) => (
          <g key={i}>
            <line className="grid-line" x1={padL} x2={W - padR} y1={yL(v)} y2={yL(v)} />
            <text className="axis-text value" x={padL - 8} y={yL(v) + 3.5} textAnchor="end">{fmt(v, unit)}</text>
          </g>
        ))}
        {data.map((d, i) => {
          const cx = padL + xBand * i + xBand / 2;
          if (d._missing) {
            return (
              <g key={i}>
                <rect
                  x={cx - barW / 2} y={padT}
                  width={barW} height={innerH}
                  fill="var(--warn-bg)" rx={4} />
                <text x={cx} y={padT + innerH / 2 + 4} textAnchor="middle"
                  className="axis-text" style={{ fill: 'var(--warn-text)', fontWeight: 600 }}>尚無資料</text>
                <text className="axis-text" x={cx} y={H - padB + 18} textAnchor="middle">{d.year - 1911} 年度</text>
              </g>
            );
          }
          const sv = salaryVals[i];
          const nv = nonSalaryVals[i];
          const total = sv + nv;
          const sH = (sv / maxV) * innerH;
          const nH = (nv / maxV) * innerH;
          const x = cx - barW / 2;
          const sY = padT + innerH - sH;
          const nY = sY - nH;
          return (
            <g key={i}>
              {/* salary (bottom) */}
              <rect
                x={x} y={sY}
                width={barW} height={sH}
                fill={salaryColor}
                style={{
                  transformOrigin: `${cx}px ${padT + innerH}px`,
                  transform: 'scaleY(0)',
                  animation: `barGrow 0.5s ${i * 0.08}s ease forwards`
                }} />
              {/* non-salary (top) */}
              {nv > 0 && (
                <rect
                  x={x} y={nY}
                  width={barW} height={nH}
                  fill={nonSalaryColor}
                  style={{
                    transformOrigin: `${cx}px ${padT + innerH}px`,
                    transform: 'scaleY(0)',
                    animation: `barGrow 0.5s ${i * 0.08 + 0.1}s ease forwards`
                  }} />
              )}
              <text className="point-label bold"
                x={cx} y={yL(total) - 8} textAnchor="middle"
                style={{ opacity: 0, animation: `fadeIn 0.4s ${0.6 + i * 0.08}s ease forwards` }}>
                {fmt(total, unit)}
              </text>
              <text className="axis-text" x={cx} y={H - padB + 18} textAnchor="middle">{d.year - 1911} 年度</text>
            </g>
          );
        })}
        {/* hover hit-areas */}
        {data.map((d, i) => {
          const cx = padL + xBand * i + xBand / 2;
          if (d._missing) return null;
          return (
            <rect key={'hit' + i}
              x={padL + xBand * i} y={padT} width={xBand} height={innerH} fill="transparent"
              onMouseMove={e => tt.onMove(e, (
                <div>
                  <div className="tt-title">{d.year - 1911} 年度</div>
                  <div className="tt-row">
                    <span className="lbl">
                      <span style={{ width: 8, height: 8, borderRadius: 2, background: salaryColor, display: 'inline-block' }}></span>
                      {salaryLabel}
                    </span>
                    <span className="val">{fmt(salaryVals[i], unit)} {fmtUnit(unit)}</span>
                  </div>
                  <div className="tt-row">
                    <span className="lbl">
                      <span style={{ width: 8, height: 8, borderRadius: 2, background: nonSalaryColor, display: 'inline-block' }}></span>
                      {nonSalaryLabel}
                    </span>
                    <span className="val">{fmt(nonSalaryVals[i], unit)} {fmtUnit(unit)}</span>
                  </div>
                  <div className="tt-foot">合計 {fmt(totals[i], unit)} {fmtUnit(unit)}</div>
                </div>
              ), cx, yL(totals[i]))}
              onMouseLeave={tt.onLeave} />
          );
        })}
      </svg>
      {tt.node}
    </div>
  );
}

Object.assign(window, {
  LineChart, StackedBarChart, DonutChart, SpouseBarChart,
  fmt, fmtUnit, pct, useTooltip
});
