/* Chart primitives — Donut, LineChart, MiniAreaChart, BarChart, AnimatedNumber, Reveal */

/* Animated number that counts up when mounted */
const AnimatedNumber = ({ value, decimals=0, duration=900, suffix='', prefix='', style }) => {
  const [v, setV] = React.useState(0);
  React.useEffect(() => {
    let raf, t0;
    const step = (t) => {
      if (!t0) t0 = t;
      const p = Math.min(1, (t - t0) / duration);
      const eased = 1 - Math.pow(1 - p, 3);
      setV(value * eased);
      if (p < 1) raf = requestAnimationFrame(step);
    };
    raf = requestAnimationFrame(step);
    return () => cancelAnimationFrame(raf);
  }, [value, duration]);
  return <span className="tabular" style={style}>{prefix}{v.toFixed(decimals)}{suffix}</span>;
};
window.AnimatedNumber = AnimatedNumber;

/* On-scroll reveal wrapper */
const Reveal = ({ children, delay=0, as='div', className='', style }) => {
  const ref = React.useRef(null);
  React.useEffect(() => {
    const el = ref.current; if (!el) return;
    const io = new IntersectionObserver((entries)=>{
      entries.forEach(e => { if (e.isIntersecting) { setTimeout(()=>el.classList.add('in'), delay); io.unobserve(el); } });
    }, { threshold: 0.12 });
    io.observe(el);
    return () => io.disconnect();
  }, [delay]);
  const Tag = as;
  return <Tag ref={ref} className={`reveal ${className}`} style={style}>{children}</Tag>;
};
window.Reveal = Reveal;

/* Donut — values sum to 100; emits animated arc segments.
   Matches the reference: thick strokes, soft tints behind, centre text. */
const Donut = ({ segments, size=220, thickness=22, centerValue, centerLabel, centerSub, trackColor='#E5E2D8' }) => {
  const r = (size - thickness) / 2;
  const C = 2 * Math.PI * r;
  const cx = size/2, cy = size/2;
  const gap = segments.length > 1 ? 2 : 0; // px gap between segments

  // Compute each segment's arc length and rotation offset (in circle units).
  let acc = 0;
  const items = segments.map((s) => {
    const len = Math.max(0, (s.value/100) * C - gap);
    const rot = acc; // where this segment starts on the ring
    acc += (s.value/100) * C;
    return { ...s, len, rot };
  });

  return (
    <svg width={size} height={size} viewBox={`0 0 ${size} ${size}`} style={{display:'block'}}>
      <circle cx={cx} cy={cy} r={r} fill="none" stroke={trackColor} strokeWidth={thickness}/>
      {items.map((s,i)=>(
        <circle key={i}
          cx={cx} cy={cy} r={r}
          fill="none"
          stroke={s.color}
          strokeWidth={thickness}
          strokeLinecap="butt"
          strokeDasharray={`${s.len} ${C}`}
          strokeDashoffset={-s.rot}
          transform={`rotate(-90 ${cx} ${cy})`}
          className="donut-seg"
          style={{
            '--dash-full': C,
            '--dash-offset': 0,
            animationDelay: `${i * 140}ms`,
          }}
        />
      ))}
      {centerValue !== undefined && (
        <g>
          <text x={cx} y={cy - 4} textAnchor="middle" fontSize={36} fontWeight="600" fill="var(--ink)" style={{fontFamily:'var(--font-sans)',letterSpacing:'-0.02em'}}>{centerValue}</text>
          {centerLabel && <text x={cx} y={cy + 18} textAnchor="middle" fontSize={10} fill="var(--ink-3)" style={{letterSpacing:'0.14em',textTransform:'uppercase'}}>{centerLabel}</text>}
          {centerSub && <text x={cx} y={cy + 34} textAnchor="middle" fontSize={11} fill="var(--ink-2)">{centerSub}</text>}
        </g>
      )}
    </svg>
  );
};
window.Donut = Donut;

/* Small donut without center text */
const MiniDonut = ({ pct, color='var(--moss)', size=56, thickness=8, trackColor='#E5E2D8' }) => {
  const r = (size-thickness)/2; const C = 2*Math.PI*r;
  const len = (pct/100)*C;
  return (
    <svg width={size} height={size}>
      <circle cx={size/2} cy={size/2} r={r} fill="none" stroke={trackColor} strokeWidth={thickness}/>
      <circle cx={size/2} cy={size/2} r={r} fill="none" stroke={color} strokeWidth={thickness}
        strokeDasharray={`${len} ${C-len}`} transform={`rotate(-90 ${size/2} ${size/2})`} strokeLinecap="round"
        style={{'--dash-full':C,'--dash-offset':C-len}} className="donut-seg"/>
    </svg>
  );
};
window.MiniDonut = MiniDonut;

/* Line chart with animated stroke + gridlines + area fill */
const LineChart = ({ series, labels, width=520, height=200, color='var(--moss)', yDomain, yTicks=[0,1,2,3,4,5], yFormat=(v)=>v.toFixed(1)+'%' }) => {
  const padL = 44, padR = 16, padT = 16, padB = 28;
  const W = width - padL - padR, H = height - padT - padB;
  const yMin = yDomain ? yDomain[0] : Math.min(...series);
  const yMax = yDomain ? yDomain[1] : Math.max(...series);
  const span = yMax - yMin || 1;
  const xAt = (i) => padL + (i/(series.length-1)) * W;
  const yAt = (v) => padT + H - ((v - yMin)/span) * H;

  const pts = series.map((v,i)=>[xAt(i), yAt(v)]);
  const d = pts.map((p,i)=> (i?'L':'M') + p[0].toFixed(1) + ' ' + p[1].toFixed(1)).join(' ');
  const areaD = d + ` L ${xAt(series.length-1)} ${padT+H} L ${padL} ${padT+H} Z`;

  // approximate path length for stroke-dasharray
  let len = 0;
  for (let i = 1; i < pts.length; i++) {
    const dx = pts[i][0]-pts[i-1][0], dy = pts[i][1]-pts[i-1][1];
    len += Math.sqrt(dx*dx + dy*dy);
  }

  return (
    <svg width={width} height={height} style={{display:'block',maxWidth:'100%'}}>
      <defs>
        <linearGradient id="line-area" x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%"  stopColor={color} stopOpacity="0.18"/>
          <stop offset="100%" stopColor={color} stopOpacity="0"/>
        </linearGradient>
      </defs>
      {/* gridlines + y labels */}
      {yTicks.map((v,i)=>(
        <g key={i}>
          <line x1={padL} y1={yAt(v)} x2={padL+W} y2={yAt(v)} stroke="var(--line)" strokeWidth="1" strokeDasharray={i===0?'':'3 4'}/>
          <text x={padL-8} y={yAt(v)+3} textAnchor="end" fontSize="10" fill="var(--ink-3)" className="tabular">{yFormat(v)}</text>
        </g>
      ))}
      {/* area */}
      <path d={areaD} fill="url(#line-area)" opacity="0.9"/>
      {/* line */}
      <path d={d} fill="none" stroke={color} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"
        className="line-path" style={{'--len':len}}/>
      {/* dots */}
      {pts.map((p,i)=>(
        <circle key={i} cx={p[0]} cy={p[1]} r="4" fill="var(--canvas)" stroke={color} strokeWidth="1.8"
          className="line-dot" style={{animationDelay:`${1200 + i*80}ms`}}/>
      ))}
      {/* x labels */}
      {labels.map((l,i)=>(
        <text key={i} x={xAt(i)} y={padT+H+18} textAnchor="middle" fontSize="11" fill="var(--ink-3)" className="tabular">{l}</text>
      ))}
    </svg>
  );
};
window.LineChart = LineChart;

/* Vertical bar chart, animated */
const BarChart = ({ data, width=440, height=200, color='var(--moss)', valueFormat=(v)=>v }) => {
  const padL = 40, padR = 12, padT = 14, padB = 36;
  const W = width - padL - padR, H = height - padT - padB;
  const max = Math.max(...data.map(d=>d.v));
  const bw = W / data.length * 0.64;
  const bgap = W / data.length * 0.36;
  const ticks = [0, max/2, max];

  return (
    <svg width={width} height={height} style={{display:'block',maxWidth:'100%'}}>
      {ticks.map((v,i)=>{
        const y = padT + H - (v/max)*H;
        return (
          <g key={i}>
            <line x1={padL} y1={y} x2={padL+W} y2={y} stroke="var(--line)" strokeDasharray={i===0?'':'3 4'}/>
            <text x={padL-8} y={y+3} textAnchor="end" fontSize="10" fill="var(--ink-3)" className="tabular">{valueFormat(v)}</text>
          </g>
        );
      })}
      {data.map((d,i)=>{
        const bh = (d.v/max) * H;
        const x = padL + (W/data.length)*i + bgap/2;
        const y = padT + H - bh;
        return (
          <g key={i}>
            <rect x={x} y={y} width={bw} height={bh} rx="3" fill={d.color||color} className="bar-rise"
              style={{animationDelay:`${i*90}ms`,transformOrigin:`${x+bw/2}px ${padT+H}px`}}/>
            <text x={x+bw/2} y={padT+H+16} textAnchor="middle" fontSize="11" fill="var(--ink-2)">{d.k}</text>
            <text x={x+bw/2} y={y-6} textAnchor="middle" fontSize="11" fill="var(--ink)" className="tabular" style={{fontWeight:500}}>{valueFormat(d.v)}</text>
          </g>
        );
      })}
    </svg>
  );
};
window.BarChart = BarChart;

/* Horizontal bar list (e.g. top themes) */
const HBarList = ({ data, valueFormat=(v)=>v, color='var(--moss)' }) => {
  const max = Math.max(...data.map(d=>d.v));
  return (
    <div style={{display:'flex',flexDirection:'column',gap:14}}>
      {data.map((d,i)=>(
        <div key={d.k} style={{display:'grid',gridTemplateColumns:'120px 1fr 48px',gap:12,alignItems:'center'}}>
          <div style={{fontSize:13,color:'var(--ink-2)'}}>{d.k}</div>
          <div style={{height:8,borderRadius:4,background:'var(--canvas-3)',overflow:'hidden'}}>
            <div className="kappa-fill" style={{width:`${(d.v/max)*100}%`,height:'100%',background:d.color||color,animation:`kappa-grow 820ms ${i*90}ms var(--ease-quiet) both`}}/>
          </div>
          <div className="tabular" style={{fontSize:12,color:'var(--ink)',textAlign:'right'}}>{valueFormat(d.v)}</div>
        </div>
      ))}
    </div>
  );
};
window.HBarList = HBarList;
