// Shared primitives: panels, sparkline, etc.
const { useEffect, useState, useRef, useMemo } = React;

function Panel({ title, dot = true, actions, children, className = '', style }) {
  return (
    <div className={`panel ${className}`} style={style}>
      <div className="panel-head">
        <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
          {dot && <span className="ph-dot" />}
          <span className="ph-title">{title}</span>
        </div>
        <div className="ph-actions">{actions || '◦ ◦ ◦'}</div>
      </div>
      <div className="panel-body">{children}</div>
    </div>
  );
}

function Sparkline({ data, width = 260, height = 56, stroke = 'var(--accent)' }) {
  const max = Math.max(...data), min = Math.min(...data);
  const range = max - min || 1;
  const stepX = width / (data.length - 1);
  const pts = data.map((v, i) => [i * stepX, height - ((v - min) / range) * (height - 8) - 4]);
  const d = pts.map((p, i) => (i === 0 ? 'M' : 'L') + p[0].toFixed(1) + ',' + p[1].toFixed(1)).join(' ');
  const area = d + ` L${width},${height} L0,${height} Z`;
  return (
    <svg className="spark" viewBox={`0 0 ${width} ${height}`} width={width} height={height}>
      <path className="area" d={area} fill={stroke} style={{ opacity: .15 }} />
      <path className="line" d={d} stroke={stroke} strokeWidth="1.4" fill="none" />
      <circle cx={pts[pts.length-1][0]} cy={pts[pts.length-1][1]} r="2.5" fill={stroke} />
    </svg>
  );
}

// Bar chart with a one-shot reveal animation.
// `data` is an array of non-negative numbers.
// `labels` (optional) renders two-digit labels below bars (e.g. year suffix).
function Waveform({ data, labels, color = 'var(--accent)' }) {
  const [progress, setProgress] = useState(0);
  useEffect(() => {
    let raf;
    const t0 = performance.now();
    const loop = () => {
      const p = Math.min(1, (performance.now() - t0) / 900);
      setProgress(1 - Math.pow(1 - p, 3));
      if (p < 1) raf = requestAnimationFrame(loop);
    };
    raf = requestAnimationFrame(loop);
    return () => cancelAnimationFrame(raf);
  }, [data]);
  if (!data || data.length === 0) return null;
  const max = Math.max(...data, 1);
  const bars = data.length;
  const gap = 4;
  const barW = 14;
  const totalW = bars * (barW + gap) - gap;
  const chartH = 56;
  const labelH = labels ? 14 : 0;
  return (
    <svg viewBox={`0 0 ${totalW} ${chartH + labelH}`} preserveAspectRatio="none" style={{ width: '100%', height: '100%' }}>
      {data.map((v, i) => {
        const h = (v / max) * chartH * progress;
        const y = chartH - h;
        return (
          <g key={i}>
            <rect x={i * (barW + gap)} y={y} width={barW} height={h} fill={color} opacity={0.35 + (i / bars) * 0.65} rx="1" />
            {labels && (
              <text x={i * (barW + gap) + barW / 2} y={chartH + 10} textAnchor="middle" fontSize="8" fill="var(--ink-dimmer)" fontFamily="var(--f-mono)">
                {String(labels[i]).slice(-2)}
              </text>
            )}
          </g>
        );
      })}
    </svg>
  );
}

function Counter({ to, duration = 1400, prefix = '', suffix = '' }) {
  const [v, setV] = useState(0);
  useEffect(() => {
    let raf;
    const t0 = performance.now();
    const loop = () => {
      const p = Math.min(1, (performance.now() - t0) / duration);
      const eased = 1 - Math.pow(1 - p, 3);
      setV(Math.floor(to * eased));
      if (p < 1) raf = requestAnimationFrame(loop);
    };
    raf = requestAnimationFrame(loop);
    return () => cancelAnimationFrame(raf);
  }, [to]);
  return <>{prefix}{v.toLocaleString()}{suffix}</>;
}

// Live GitHub contribution grid (52 weeks of daily commits).
// Data source: https://github-contributions-api.jogruber.de (free, no
// auth, CORS-enabled mirror of a user's public contribution graph).
// Cached in sessionStorage for 6h to avoid repeated calls on nav.
function GitHubMatrix({ username = 'chintu0019' }) {
  const [state, setState] = useState({ loading: true });

  useEffect(() => {
    const key = `gh_contribs_${username}`;
    const ttl = 6 * 60 * 60 * 1000;
    try {
      const cached = JSON.parse(sessionStorage.getItem(key) || 'null');
      if (cached && (Date.now() - cached.ts) < ttl) {
        setState({ loading: false, data: cached.data });
        return;
      }
    } catch (_) {}
    let cancelled = false;
    fetch(`https://github-contributions-api.jogruber.de/v4/${username}?y=last`)
      .then((r) => r.ok ? r.json() : Promise.reject(r.status))
      .then((data) => {
        if (cancelled) return;
        try { sessionStorage.setItem(key, JSON.stringify({ ts: Date.now(), data })); } catch (_) {}
        setState({ loading: false, data });
      })
      .catch((err) => !cancelled && setState({ loading: false, error: String(err) }));
    return () => { cancelled = true; };
  }, [username]);

  const header = (right) => (
    <div style={{ display: 'flex', justifyContent: 'space-between', color: 'var(--ink-dimmer)', fontSize: 10, letterSpacing: '.12em', textTransform: 'uppercase', marginBottom: 8 }}>
      <span>GitHub activity · @{username}</span>
      <span>{right}</span>
    </div>
  );

  if (state.loading) return <div>{header('fetching…')}<div style={{ height: 64 }} /></div>;
  if (state.error || !state.data) return (
    <div>
      {header('offline')}
      <a href={`https://github.com/${username}`} target="_blank" rel="noopener" style={{ color: 'var(--accent)', fontFamily: 'var(--f-mono)', fontSize: 11 }}>
        github.com/{username} →
      </a>
    </div>
  );

  const contribs = state.data.contributions || [];
  const total = state.data.total?.lastYear ?? contribs.reduce((s, c) => s + c.count, 0);

  // Lay out as week columns, day rows (Sun = 0 at top).
  let col = 0;
  let row = contribs.length ? new Date(contribs[0].date + 'T00:00:00').getDay() : 0;
  const cells = contribs.map((c) => {
    const pos = { col, row, count: c.count, level: c.level, date: c.date };
    row++;
    if (row > 6) { row = 0; col++; }
    return pos;
  });
  const cols = (cells[cells.length - 1]?.col ?? 0) + 1;
  const cell = 7;
  const gap = 1.5;
  const w = cols * (cell + gap) - gap;
  const h = 7 * (cell + gap) - gap;
  const opacityByLevel = [0.08, 0.3, 0.55, 0.8, 1];

  return (
    <div>
      {header(`${total.toLocaleString()} last year`)}
      <svg viewBox={`0 0 ${w} ${h}`} preserveAspectRatio="xMidYMid meet" style={{ width: '100%', height: 'auto' }}>
        {cells.map((c, i) => (
          <rect
            key={i}
            x={c.col * (cell + gap)}
            y={c.row * (cell + gap)}
            width={cell}
            height={cell}
            rx="1.2"
            fill={c.level === 0 ? 'var(--line)' : 'var(--accent)'}
            opacity={opacityByLevel[c.level] ?? 0.08}
          >
            <title>{c.count} contribution{c.count === 1 ? '' : 's'} · {c.date}</title>
          </rect>
        ))}
      </svg>
    </div>
  );
}

Object.assign(window, { Panel, Sparkline, Waveform, Counter, GitHubMatrix });
