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

/* ----------------------------------------------------------
   Data — fraction of plot height (0 = baseline, 1 = top)
   Three cumulative stacking lines, organic monthly waves.
---------------------------------------------------------- */
const MONTHS = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];

const DATA = {
  gray: [0.19,0.21,0.20,0.18,0.23,0.21,0.17,0.20,0.19,0.22,0.18,0.15],
  navy: [0.44,0.48,0.57,0.52,0.47,0.54,0.62,0.70,0.73,0.67,0.56,0.50],
  red:  [0.60,0.70,0.82,0.77,0.70,0.80,0.92,1.00,0.98,0.90,0.85,0.82],
};

/* approximate absolute bookings for the tooltip readout */
const TOTAL_AT_PEAK = 1840;

/* ---------- geometry ---------- */
const VB_W = 1420, VB_H = 1180;
const X0 = 132, X1 = 905;            // Jan … Dec
const BASE_Y = 940, PLOT_TOP = 252;  // baseline … ceiling
const PLOT_H = BASE_Y - PLOT_TOP;
const X = i => X0 + (X1 - X0) * (i / (MONTHS.length - 1));
const Y = f => BASE_Y - f * PLOT_H;

/* Catmull-Rom → cubic bezier, returns the C-commands (pen already at pts[0]) */
function smoothBody(pts, k) {
  let d = '';
  for (let i = 0; i < pts.length - 1; i++) {
    const p0 = pts[i - 1] || pts[i];
    const p1 = pts[i];
    const p2 = pts[i + 1];
    const p3 = pts[i + 2] || p2;
    const c1x = p1.x + (p2.x - p0.x) / 6 * k;
    const c1y = p1.y + (p2.y - p0.y) / 6 * k;
    const c2x = p2.x - (p3.x - p1.x) / 6 * k;
    const c2y = p2.y - (p3.y - p1.y) / 6 * k;
    d += ` C ${c1x.toFixed(2)},${c1y.toFixed(2)} ${c2x.toFixed(2)},${c2y.toFixed(2)} ${p2.x.toFixed(2)},${p2.y.toFixed(2)}`;
  }
  return d;
}

function bandPath(topFracs, botFracs, smooth) {
  const k = smooth ? 1 : 0.001;
  const top = topFracs.map((f, i) => ({ x: X(i), y: Y(f) }));
  const bot = botFracs.map((f, i) => ({ x: X(i), y: Y(f) }));
  const botRev = bot.slice().reverse();
  return (
    `M ${top[0].x.toFixed(2)},${top[0].y.toFixed(2)}` +
    smoothBody(top, k) +
    ` L ${botRev[0].x.toFixed(2)},${botRev[0].y.toFixed(2)}` +
    smoothBody(botRev, k) +
    ' Z'
  );
}

/* ---------- legend / series config ---------- */
const ZERO = DATA.gray.map(() => 0);

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "accent": "#FF5252",
  "smooth": true,
  "hoverEffect": "both",
  "showValues": true
}/*EDITMODE-END*/;

function App() {
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
  const [active, setActive] = useState(null);
  const [shown, setShown] = useState(false);
  const [replayKey, setReplayKey] = useState(0);

  useEffect(() => {
    setShown(false);
    const id = requestAnimationFrame(() => requestAnimationFrame(() => setShown(true)));
    return () => cancelAnimationFrame(id);
  }, [replayKey]);

  const series = useMemo(() => ([
    {
      key: 'red',
      label: ['With Premium', 'Listing'],
      color: t.accent,
      icon: 'fa-regular fa-star',
      top: DATA.red, bot: DATA.navy,
      lineFracs: DATA.red,
      badgeY: 402,
      share: 0.38,
    },
    {
      key: 'navy',
      label: ['With RP', 'Marketplace'],
      color: 'var(--rp-navy-900)',
      iconStroke: 'var(--rp-navy-900)',
      icon: 'fa-solid fa-store',
      top: DATA.navy, bot: DATA.gray,
      lineFracs: DATA.navy,
      badgeY: 648,
      share: 0.41,
    },
    {
      key: 'gray',
      label: ['Without RP', 'Marketplace'],
      color: 'var(--rp-gray-200)',
      iconStroke: 'var(--rp-gray-500)',
      icon: 'fa-solid fa-xmark',
      top: DATA.gray, bot: ZERO,
      lineFracs: DATA.gray,
      badgeY: 892,
      share: 0.21,
    },
  ]), [t.accent]);

  const dimOthers = t.hoverEffect === 'dim' || t.hoverEffect === 'both';
  const liftActive = t.hoverEffect === 'lift' || t.hoverEffect === 'both';

  const BADGE_X = 1018, BADGE_R = 46, END_X = X1;

  return (
    <div className="chart-card">
      <svg className="chart-svg" viewBox={`0 0 ${VB_W} ${VB_H}`} role="img"
           aria-label="Bookings growth: Without RP Marketplace, With RP Marketplace, With Premium Listing">
        <defs>
          <linearGradient id="grad-red" x1="0" y1="0" x2="0" y2="1">
            <stop offset="0%" stopColor={t.accent} stopOpacity="1" />
            <stop offset="100%" stopColor={t.accent} stopOpacity="0.88" />
          </linearGradient>
          <filter id="soft-lift" x="-30%" y="-30%" width="160%" height="160%">
            <feDropShadow dx="0" dy="10" stdDeviation="16" floodColor="rgba(23,35,44,0.22)" />
          </filter>
        </defs>

        {/* ---- axes ---- */}
        <g stroke="var(--rp-gray-400)" strokeWidth="3" strokeLinecap="round" fill="none" opacity="0.9">
          <line x1={X0 - 38} y1={BASE_Y + 8} x2={X0 - 38} y2={PLOT_TOP - 10} />
          <path d={`M ${X0 - 46},${PLOT_TOP + 4} L ${X0 - 38},${PLOT_TOP - 12} L ${X0 - 30},${PLOT_TOP + 4}`} />
          <line x1={X0 - 50} y1={BASE_Y + 48} x2={935} y2={BASE_Y + 48} />
          <path d={`M ${929},${BASE_Y + 40} L ${945},${BASE_Y + 48} L ${929},${BASE_Y + 56}`} />
        </g>

        <text x={X0 - 92} y={(BASE_Y + PLOT_TOP) / 2} transform={`rotate(-90 ${X0 - 92} ${(BASE_Y + PLOT_TOP) / 2})`}
              textAnchor="middle" fontSize="34" fontWeight="600" fill="var(--rp-navy)" letterSpacing="0.5">Bookings</text>

        {/* x ticks + labels */}
        {[0, 5, 11].map((i, idx) => (
          <g key={i}>
            <circle cx={X(i)} cy={BASE_Y + 48} r="7" fill="var(--rp-gray-400)" />
            <text x={X(i)} y={BASE_Y + 118} textAnchor="middle" fontSize="40" fontWeight="600" fill="var(--rp-navy)">
              {['Jan', 'Jun', 'Dec'][idx]}
            </text>
          </g>
        ))}

        {/* ---- stacked bands ---- */}
        <g>
          {series.map((s, idx) => {
            const isActive = active === s.key;
            const dimmed = active && dimOthers && !isActive;
            const lift = isActive && liftActive ? -10 : 0;
            return (
              <path
                key={s.key + replayKey}
                d={bandPath(s.top, s.bot, t.smooth)}
                fill={s.key === 'red' ? 'url(#grad-red)' : s.color}
                onMouseEnter={() => setActive(s.key)}
                onMouseLeave={() => setActive(null)}
                style={{
                  cursor: 'pointer',
                  transformBox: 'fill-box',
                  transformOrigin: 'bottom',
                  transform: shown
                    ? `translateY(${lift}px) scaleY(1)`
                    : 'translateY(0px) scaleY(0)',
                  opacity: shown ? (dimmed ? 0.26 : 1) : 0,
                  filter: isActive && liftActive ? 'url(#soft-lift)' : 'none',
                  transition: shown
                    ? 'opacity .35s ease, transform .45s cubic-bezier(.22,1,.36,1), filter .3s ease'
                    : `opacity .5s ease ${idx * 0.14}s, transform .9s cubic-bezier(.22,1,.36,1) ${idx * 0.14}s`,
                }}
              />
            );
          })}
        </g>

        {/* ---- connectors, end dots, badges, labels ---- */}
        {series.map((s) => {
          const isActive = active === s.key;
          const anyActive = active !== null;
          const dimmed = anyActive && !isActive;
          const endY = Y(s.lineFracs[MONTHS.length - 1]);
          const isRed = s.key === 'red';
          const ringColor = isRed ? t.accent : (s.iconStroke || s.color);
          return (
            <g key={'leg-' + s.key}
               onMouseEnter={() => setActive(s.key)}
               onMouseLeave={() => setActive(null)}
               style={{
                 cursor: 'pointer',
                 opacity: dimmed ? 0.4 : 1,
                 transition: 'opacity .3s ease',
               }}>
              {/* dotted connector */}
              <line x1={END_X} y1={endY} x2={BADGE_X - BADGE_R - 6} y2={s.badgeY}
                    stroke={ringColor} strokeWidth="3.2" strokeLinecap="round"
                    strokeDasharray="0.1 13"
                    style={{
                      opacity: shown ? (isActive ? 1 : 0.85) : 0,
                      transition: 'opacity .4s ease .6s',
                    }} />

              {/* end dot on the band edge */}
              <circle cx={END_X} cy={endY} r={isActive ? 12 : 9}
                      fill={isRed ? t.accent : (s.key === 'navy' ? 'var(--rp-white)' : 'var(--rp-gray-300)')}
                      stroke={s.key === 'navy' ? 'var(--rp-navy-900)' : (isRed ? t.accent : 'var(--rp-gray-400)')}
                      strokeWidth={s.key === 'navy' ? 5 : 0}
                      style={{
                        opacity: shown ? 1 : 0,
                        transition: 'opacity .4s ease .65s, r .25s cubic-bezier(.34,1.56,.64,1)',
                      }} />

              {/* badge ring — sweeps to fill the share on hover */}
              <circle cx={BADGE_X} cy={s.badgeY} r={BADGE_R}
                      fill="none" stroke={ringColor} strokeOpacity="0.16" strokeWidth="6" />
              <circle cx={BADGE_X} cy={s.badgeY} r={BADGE_R}
                      fill="none" stroke={ringColor} strokeWidth="6" strokeLinecap="round"
                      transform={`rotate(-90 ${BADGE_X} ${s.badgeY})`}
                      strokeDasharray={2 * Math.PI * BADGE_R}
                      strokeDashoffset={2 * Math.PI * BADGE_R * (1 - (isActive ? 1 : s.share))}
                      style={{ transition: 'stroke-dashoffset .6s cubic-bezier(.22,1,.36,1)' }} />

              {/* icon */}
              <foreignObject x={BADGE_X - 34} y={s.badgeY - 34} width="68" height="68">
                <div xmlns="http://www.w3.org/1999/xhtml" style={{
                  width: '68px', height: '68px', display: 'flex', alignItems: 'center', justifyContent: 'center',
                }}>
                  <i className={s.icon} style={{
                    fontSize: '34px',
                    color: ringColor,
                    transform: isActive ? 'scale(1.12)' : 'scale(1)',
                    transition: 'transform .3s cubic-bezier(.34,1.56,.64,1)',
                  }}></i>
                </div>
              </foreignObject>

              {/* label */}
              <text x={BADGE_X + BADGE_R + 26} y={s.badgeY - (s.label.length > 1 ? 8 : -8)}
                    fontSize="33" fontWeight="500" fill="var(--rp-navy)">
                {s.label.map((line, li) => (
                  <tspan key={li} x={BADGE_X + BADGE_R + 26} dy={li === 0 ? 0 : 40}>{line}</tspan>
                ))}
              </text>

              {/* value readout on hover */}
              {t.showValues && isActive && (
                <text x={BADGE_X + BADGE_R + 26} y={s.badgeY + (s.label.length > 1 ? 70 : 44)}
                      fontSize="26" fontWeight="700" fill={ringColor} fontFamily="var(--font-display)">
                  {Math.round(TOTAL_AT_PEAK * s.share).toLocaleString()} / mo
                </text>
              )}
            </g>
          );
        })}
      </svg>

      <TweaksPanel>
        <TweakSection label="Chart" />
        <TweakColor label="Top band accent" value={t.accent}
                    options={['#FF5252', '#1E93EC', '#F19B2F', '#002E47']}
                    onChange={(v) => setTweak('accent', v)} />
        <TweakToggle label="Smooth curves" value={t.smooth}
                     onChange={(v) => setTweak('smooth', v)} />
        <TweakSection label="Interaction" />
        <TweakRadio label="Hover effect" value={t.hoverEffect}
                    options={['lift', 'dim', 'both']}
                    onChange={(v) => setTweak('hoverEffect', v)} />
        <TweakToggle label="Show values on hover" value={t.showValues}
                     onChange={(v) => setTweak('showValues', v)} />
        <TweakButton label="Replay animation" onClick={() => setReplayKey(k => k + 1)} />
      </TweaksPanel>
    </div>
  );
}

ReactDOM.createRoot(document.getElementById('root')).render(<App />);
