/* global React */
const { useState, useMemo, useEffect, useRef } = React;

window.fmt = {
  pct: (n, dec=0) => (n*100).toFixed(dec) + '%',
  num: (n, dec=2) => Number(n).toFixed(dec),
};

// ===== Likert Bar Chart =====
// distribution is a length-7 array of counts where index 0 = Likert 1 (very negative)
// and index 6 = Likert 7 (very positive).
window.LIKERT_COLORS = ['#ECCAB8', '#DCC8BB', '#C8C6BF', '#B0C3C5', '#98C1C6', '#72BCCC', '#57BAD1'];
window.LikertBar = function LikertBar({ distribution, mean }) {
  const total = distribution.reduce((a,b)=>a+b, 0);
  const max = Math.max(...distribution);
  const colors = window.LIKERT_COLORS;
  const labels = [
    'Extremely\nnegative',
    'Moderately\nnegative',
    'Slightly\nnegative',
    'Neither',
    'Slightly\npositive',
    'Moderately\npositive',
    'Extremely\npositive',
  ];
  return (
    <div className="likert-chart">
      <div className="likert-bars">
        {distribution.map((c, i) => {
          const pct = total ? (c/total)*100 : 0;
          const h = max ? (c/max)*100 : 0;
          return (
            <div className="bar-col" key={i}>
              <div className="bar-pct">{pct.toFixed(0)}%</div>
              <div className="bar" style={{height: h+'%', background: colors[i]}} title={`${c} respondents (${pct.toFixed(1)}%)`}/>
            </div>
          );
        })}
      </div>
      <div className="likert-axis">
        {labels.map((l, i) => (
          <div className="ax-label" key={i}>
            <span className="ax-num">
              {i+1}
            </span>
            <span className="ax-text">
              {l.split('\n').map((s, j) => <div key={j}>{s}</div>)}
            </span>
          </div>
        ))}
      </div>
    </div>
  );
};

// ===== Overall Tabs (Section 1) =====
window.OverallTabs = function OverallTabs({ data }) {
  const tabs = [
    { key: 'total', label: 'Life overall', countsKey: 'total' },
    { key: 'work', label: 'Work', countsKey: 'work' },
    { key: 'personal', label: 'Personal life', countsKey: 'personal' },
  ];
  const [activeIdx, setActiveIdx] = useState(0);
  const rootRef = useRef(null);
  const stageRef = useRef(null);
  const N = tabs.length;

  // Drive the active tab from scroll progress within the surrounding
  // .sticky-stage container (which provides ~N viewports of scroll lag).
  useEffect(() => {
    const stage = rootRef.current && rootRef.current.closest('.sticky-stage');
    if (!stage) return;
    stageRef.current = stage;
    const onScroll = () => {
      const r = stage.getBoundingClientRect();
      const total = stage.offsetHeight - window.innerHeight;
      const scrolled = Math.min(Math.max(-r.top, 0), total);
      const p = total > 0 ? scrolled / total : 0;
      const idx = Math.min(N - 1, Math.floor(p * N + 0.0001));
      setActiveIdx(prev => (prev === idx ? prev : idx));
    };
    window.addEventListener('scroll', onScroll, { passive: true });
    window.addEventListener('resize', onScroll);
    onScroll();
    return () => {
      window.removeEventListener('scroll', onScroll);
      window.removeEventListener('resize', onScroll);
    };
  }, [N]);

  const goTo = (i) => {
    const stage = stageRef.current;
    if (!stage) return;
    const total = stage.offsetHeight - window.innerHeight;
    const target = stage.offsetTop + (total * (i + 0.5)) / N;
    window.scrollTo({ top: target, behavior: 'smooth' });
  };

  const active = tabs[activeIdx].key;

  // Convert the overall counts (with named buckets) into a length-7 distribution.
  const order = [
    'Extremely negative',
    'Moderately negative',
    'Slightly negative',
    'Neither positive nor negative',
    'Slightly positive',
    'Moderately positive',
    'Extremely positive',
  ];
  const counts = data.overall[active] || {};
  const distribution = order.map(k => counts[k] || 0);
  const total = distribution.reduce((a,b)=>a+b, 0);
  // Mean on 1..7 scale
  const mean = total ? distribution.reduce((s, c, i) => s + c*(i+1), 0) / total : 0;

  return (
    <div ref={rootRef}>
      <div className="tab-nav">
        {tabs.map((t, i) => (
          <button
            key={t.key}
            className={i === activeIdx ? 'active' : ''}
            onClick={() => goTo(i)}>
            {t.label}
          </button>
        ))}
      </div>
      <LikertBar distribution={distribution} mean={mean}/>
    </div>
  );
};

// ===== Mapping: dimension key → list of subquestion column codes =====
// Sourced from the README "Additional block-level items" list.
window.DIMENSION_SUBQUESTIONS = {
  fl_satisfaction_ai: [
    ...['add_meaning_1','add_meaning_2','add_meaning_3','add_meaning_4','add_meaning_5'],
  ],
  fl_health_ai: ['bhv_health_1','bhv_health_2'],
  fl_purpose_ai: [
    'bhv_purpose_1','bhv_purpose_2',
    'add_meaning_1','add_meaning_2','add_meaning_3','add_meaning_4','add_meaning_5',
  ],
  fl_character_ai: [],
  fl_social_ai: [
    'bhv_social_1','bhv_social_2',
    ...Array.from({length:9}, (_,i)=>`add_social_${i+1}`),
  ],
  fl_financial_ai: Array.from({length:7}, (_,i)=>`add_work_${i+1}`),
  thrive_skills_ai: [
    ...Array.from({length:7}, (_,i)=>`add_learn_${i+1}`),
  ],
  thrive_learn_ai: [
    'bhv_learn_1','bhv_learn_2',
    ...Array.from({length:7}, (_,i)=>`add_learn_${i+1}`),
    ...Array.from({length:5}, (_,i)=>`add_students_${i+1}`),
  ],
  thrive_accomplish_ai: Array.from({length:7}, (_,i)=>`add_work_${i+1}`),
  ex_reasoning_1: [
    'bhv_reasoning_1','bhv_reasoning_2',
    ...Array.from({length:16}, (_,i)=>`ex_reasoning_${i+2}`), // 2..17
  ],
  ex_creativity_3: [
    'ex_creativity_1','ex_creativity_2',
    ...Array.from({length:8}, (_,i)=>`ex_creativity_${i+4}`), // 4..11
  ],
};

// ===== Single horizontal segmented bar for one sub-question's distribution =====
window.SUBQ_PALETTE = ['#EDCBB9', '#D4C7BC', '#B0C3C5', '#A7D0D9', '#5CBAD0'];
// Interpolate between palette stops for arbitrary N-level scales.
function subqColor(i, n) {
  const stops = window.SUBQ_PALETTE;
  if (n <= 1) return stops[0];
  const t = i / (n - 1);
  const pos = t * (stops.length - 1);
  const lo = Math.floor(pos);
  const hi = Math.min(stops.length - 1, lo + 1);
  const f = pos - lo;
  const a = stops[lo], b = stops[hi];
  const hex = (s) => [parseInt(s.slice(1,3),16), parseInt(s.slice(3,5),16), parseInt(s.slice(5,7),16)];
  const [r1,g1,b1] = hex(a), [r2,g2,b2] = hex(b);
  const mix = (x,y)=> Math.round(x + (y-x)*f).toString(16).padStart(2,'0');
  return '#' + mix(r1,r2) + mix(g1,g2) + mix(b1,b2);
}

window.SubqBar = function SubqBar({ dist, labels, n }) {
  const total = (dist || []).reduce((a,b)=>a+b, 0) || 1;
  const N = dist.length;
  const leftLabel = labels?.[0] ?? '';
  const rightLabel = labels?.[N-1] ?? '';
  const [hover, setHover] = useState(null); // { i, x }
  const trackRef = useRef(null);

  const handleMove = (i) => (e) => {
    const rect = trackRef.current?.getBoundingClientRect();
    const x = rect ? e.clientX - rect.left : 0;
    setHover({ i, x });
  };

  return (
    <div className="subq-bar">
      <div className="subq-bar-track" ref={trackRef} onMouseLeave={() => setHover(null)}>
        {dist.map((v, i) => {
          const pct = (v / total) * 100;
          const color = subqColor(i, N);
          return (
            <div
              key={i}
              className="subq-bar-seg"
              style={{ width: `${pct}%`, background: color }}
              onMouseEnter={handleMove(i)}
              onMouseMove={handleMove(i)}
            >
              <span className="subq-bar-pct">{pct >= 4 ? `${pct.toFixed(0)}%` : ''}</span>
            </div>
          );
        })}
        {hover && (
          <div className="subq-bar-tip" style={{ left: hover.x }}>
            {labels?.[hover.i] ?? String(hover.i + 1)}
          </div>
        )}
      </div>
      <div className="subq-bar-axis">
        <span className="subq-bar-axis-left">{leftLabel}</span>
        <span className="subq-bar-axis-right">{rightLabel}</span>
      </div>
    </div>
  );
};

// ===== Dimension Stage =====
// Scroll-jacked layout: a single 100vh panel stays pinned while the page
// scrolls, and the active dimension swaps in/out based on scroll progress.
// Layout: 33% left (section markers) | 66% right (title + chart + dive deeper).
window.DimensionStage = function DimensionStage({ dimensions, metadata, subqData }) {
  const [active, setActive] = useState(0);
  const [open, setOpen] = useState(false);
  const stageRef = useRef(null);

  // Each dimension gets one viewport-height of scroll distance.
  const N = dimensions.length;

  useEffect(() => {
    const onScroll = () => {
      const node = stageRef.current;
      if (!node) return;
      const r = node.getBoundingClientRect();
      // progress from 0 → 1 across the stage's scroll range
      const total = node.offsetHeight - window.innerHeight;
      const scrolled = Math.min(Math.max(-r.top, 0), total);
      const p = total > 0 ? scrolled / total : 0;
      // Map to dim index
      const idx = Math.min(N - 1, Math.floor(p * N + 0.0001));
      setActive(prev => (prev === idx ? prev : (setOpen(false), idx)));
    };
    window.addEventListener('scroll', onScroll, { passive: true });
    window.addEventListener('resize', onScroll);
    onScroll();
    return () => {
      window.removeEventListener('scroll', onScroll);
      window.removeEventListener('resize', onScroll);
    };
  }, [N]);

  const dim = dimensions[active];
  if (!dim) return null;

  const subquestionCodes = window.DIMENSION_SUBQUESTIONS[dim.def.key] || [];
  const subquestions = subquestionCodes.map(code => ({
    code,
    text: metadata?.[code]?.question_text || subqData?.[code]?.question_text || null,
    data: subqData?.[code] || null,
  }));
  const total = (dim.distribution || []).reduce((a, b) => a + b, 0);
  const numLabel = '§ ' + String(active + 2).padStart(2, '0');

  // Click on a marker scrolls to its slice of the stage.
  const goTo = (i) => {
    const node = stageRef.current;
    if (!node) return;
    const total = node.offsetHeight - window.innerHeight;
    const target = node.offsetTop + (total * (i + 0.5)) / N;
    window.scrollTo({ top: target, behavior: 'smooth' });
  };

  return (
    <div ref={stageRef} className="dim-stage" style={{ height: `${N * 100}vh` }}>
      <div className="dim-stage-inner">
        <div className="dim-stage-grid">
          {/* LEFT 33% — section markers */}
          <aside className="dim-stage-nav">
            <ol className="nav-list">
              {dimensions.map((d, i) => (
                <li
                  key={d.def.key}
                  className={'nav-item ' + (i === active ? 'active' : '')}
                  onClick={() => goTo(i)}>
                  <span className="nav-cat">{d.def.category}</span>
                </li>
              ))}
            </ol>
            <button className="back-to-top" onClick={() => window.scrollTo(0, 0)}>
              <span className="arrow">↑</span> Back to top
            </button>
          </aside>

          {/* RIGHT 66% — current dimension content */}
          <div className={'dim-stage-main' + (open ? ' is-open' : '')} key={active}>
            <div className="dim-titles">
              <h3>{dim.def.title}</h3>
              {total > 0 && (
                <div className="dim-stats">
                  <div className="stat"><strong>{total.toLocaleString()}</strong>Respondents</div>
                </div>
              )}
            </div>
            {dim.def.summary && (
              <p className="dim-summary">{dim.def.summary}</p>
            )}
            <div className="dim-chart-wrap">
              <LikertBar distribution={dim.distribution} mean={dim.mean}/>
            </div>
            {subquestions.length > 0 && (
              <div className="dive-deeper">
                <button
                  className={'dive-btn ' + (open ? 'open' : '')}
                  onClick={() => setOpen(o => !o)}
                  aria-expanded={open}>
                  <span className="arrow">›</span>
                  {open ? 'Hide details' : 'Dive deeper'}
                </button>
                {open && (
                  <div className="dive-panel">
                    <ul className="subq-list">
                      {subquestions.map(q => (
                        <li key={q.code} className="subq-item">
                          <div className="subq-text">{q.text || <em style={{color:'var(--ink-faint)'}}>(question text not available)</em>}</div>
                          {q.data && q.data.n > 0 && (
                            <SubqBar dist={q.data.distribution} labels={q.data.labels} n={q.data.n}/>
                          )}
                        </li>
                      ))}
                    </ul>
                  </div>
                )}
              </div>
            )}
          </div>
        </div>
      </div>
    </div>
  );
};

// ===== Sticky Section Indicator =====
// Tracks whichever .dim-section / .story is currently centered in the viewport
// and shows a small label fixed to the left edge of the screen.
window.SectionIndicator = function SectionIndicator() {
  const [active, setActive] = useState(null);
  const [sections, setSections] = useState([]);

  useEffect(() => {
    const collect = () => {
      const nodes = Array.from(document.querySelectorAll('section.dim-section, section#overall'));
      const items = nodes.map((node, i) => {
        const idx = node.dataset.dimIdx ? Number(node.dataset.dimIdx) : 1;
        const cat = node.dataset.dimCat || (node.id === 'overall' ? 'Overall AI influence' : '');
        return { node, idx, cat };
      });
      setSections(items);
      return items;
    };

    let items = collect();
    // re-collect once after dimensions mount (next frame)
    const t = setTimeout(() => { items = collect(); }, 200);

    const onScroll = () => {
      const mid = window.innerHeight / 2;
      let current = null;
      for (const it of items) {
        const r = it.node.getBoundingClientRect();
        if (r.top <= mid && r.bottom > mid) { current = it; break; }
      }
      setActive(current);
    };
    window.addEventListener('scroll', onScroll, { passive: true });
    window.addEventListener('resize', onScroll);
    onScroll();

    return () => {
      window.removeEventListener('scroll', onScroll);
      window.removeEventListener('resize', onScroll);
      clearTimeout(t);
    };
  }, []);

  if (!active) return <div className="section-indicator"/>;
  const numLabel = '§ ' + String(active.idx).padStart(2, '0');

  return (
    <div className="section-indicator visible">
      <div className="si-num">{numLabel}</div>
      <div className="si-cat">{active.cat}</div>
      <div className="si-progress">
        {sections.map(s => (
          <div key={s.idx} className={'si-tick ' + (s.idx === active.idx ? 'active' : '')}/>
        ))}
      </div>
    </div>
  );
};

// ===== Horizontal Bars (for usage / context) =====
// Each row: label on the left, gray track filled black to the value, % on the right.
window.HBars = function HBars({ rows, valueKey = 'pct', maxVal, format }) {
  const max = maxVal != null ? maxVal : Math.max(...rows.map(r => r[valueKey] || 0));
  const fmt = format || ((v) => Math.round(v * 100) + '%');
  return (
    <div className="hbar-list">
      {rows.map(r => {
        const v = r[valueKey] || 0;
        const w = max ? (v / max) * 100 : 0;
        return (
          <div className="hbar-row" key={r.key || r.label}>
            <div className="hbar-label">{r.label}</div>
            <div className="hbar-track"><div className="hbar-fill" style={{ width: w + '%' }}/></div>
            <div className="hbar-val">{fmt(v)}</div>
          </div>
        );
      })}
    </div>
  );
};

// ===== Flourishing-AI subgroup means =====
// One diverging bar per subgroup, on a -3..+3 axis, where 0 = "no real effect".
// Each subgroup's value is its mean of the per-respondent average across the
// 8 perceived AI-impact flourishing dimensions.
window.FlMeanBars = function FlMeanBars({ rows, sort = true }) {
  const data = (rows || []).filter(r => r.fl_mean != null);
  const ordered = sort ? [...data].sort((a, b) => b.fl_mean - a.fl_mean) : data;
  const AXIS_MAX = 3;
  return (
    <div className="flmean-list">
      <div className="flmean-axis-head">
        <div className="flmean-spacer"/>
        <div className="flmean-ticks">
          <span>−3</span><span>−2</span><span>−1</span><span>0</span><span>+1</span><span>+2</span><span>+3</span>
        </div>
        <div className="flmean-spacer-r"/>
      </div>
      {ordered.map(r => {
        const v = r.fl_mean;
        const pos = v >= 0;
        // axis goes -3..+3; midpoint at 50%.
        const halfWidth = Math.min(100, Math.abs(v) / AXIS_MAX * 50);
        const left = pos ? 50 : 50 - halfWidth;
        return (
          <div className="flmean-row" key={r.label}>
            <div className="flmean-label">
              <span className="flmean-name">{r.label}</span>
              <span className="flmean-n">n={r.n}</span>
            </div>
            <div className="flmean-track">
              <div className="flmean-mid"/>
              <div
                className={'flmean-fill ' + (pos ? 'pos' : 'neg')}
                style={{ left: left + '%', width: halfWidth + '%' }}
              />
            </div>
            <div className={'flmean-val ' + (pos ? 'pos' : 'neg')}>
              {pos ? '+' : ''}{v.toFixed(2)}
            </div>
          </div>
        );
      })}
    </div>
  );
};

// ===== Access panel — flourishing aggregate by provider / tier / mode =====
window.AccessFlPanel = function AccessFlPanel({ provider, tier, mode }) {
  const block = (title, rows, sort = true) => (
    <div className="access-block access-block-plain">
      <div className="access-block-head">
        <div className="access-block-title">{title}</div>
      </div>
      <window.FlMeanBars rows={rows} sort={sort}/>
    </div>
  );
  return (
    <div className="access-panel">
      {block('Provider', provider || [], true)}
      {block('User tier', tier || [], false)}
      {block('Thinking mode', mode || [], false)}
    </div>
  );
};

// ===== Access panel: three small charts (provider / tier / mode) stacked =====
window.AccessPanel = function AccessPanel({ provider, tier, mode }) {
  const fmt = (v) => v.toFixed(1) + '%';
  const block = (title, subtitle, items, max) => (
    <div className="access-block">
      <div className="access-block-head">
        <div className="access-block-title">{title}</div>
        {subtitle && <div className="access-block-sub">{subtitle}</div>}
      </div>
      <window.HBars
        rows={items.map(it => ({ key: it.label, label: it.label, pct: it.pct }))}
        valueKey="pct"
        maxVal={max}
        format={fmt}
      />
    </div>
  );
  return (
    <div className="access-panel">
      {block('Provider', `n=${provider?.n_respondents ?? 0} respondents · multi-select`,
             provider?.items || [], 100)}
      {block('User tier', `n=${tier?.n_respondents ?? 0} respondents`,
             tier?.items || [], 100)}
      {block('Thinking mode', `n=${mode?.n_respondents ?? 0} respondents`,
             mode?.items || [], 100)}
    </div>
  );
};

// ===== Demographic mean cards =====
window.DemoMeanGroup = function DemoMeanGroup({ groups, label }) {
  // groups: [{ key, label, mean, total }]
  const sorted = [...groups].sort((a, b) => b.mean - a.mean);
  const max = 3; // -3..+3 display range, we only show positive bars
  return (
    <div className="demo-group">
      <div className="demo-group-label">{label}</div>
      <div className="demo-rows">
        {sorted.map(g => {
          const centered = g.mean - 4; // 1..7 → -3..+3
          const w = Math.min(100, Math.abs(centered) / max * 100);
          const pos = centered >= 0;
          return (
            <div className="demo-row" key={g.key}>
              <div className="demo-row-label">
                <span className="demo-row-name">{g.label}</span>
                <span className="demo-row-n">n={g.total}</span>
              </div>
              <div className="demo-row-axis">
                <div className={'demo-row-fill ' + (pos ? 'pos' : 'neg')} style={{ width: w + '%' }}/>
              </div>
              <div className={'demo-row-val ' + (pos ? 'pos' : 'neg')}>
                {pos ? '+' : ''}{centered.toFixed(2)}
              </div>
            </div>
          );
        })}
      </div>
    </div>
  );
};

// ===== Insight Stage =====
// Scroll-jacked stage that takes a list of panels, each with its own render() for the chart area.
// Same nav-on-left / chart-on-right layout as DimensionStage.
window.InsightStage = function InsightStage({ panels }) {
  const [active, setActive] = useState(0);
  const stageRef = useRef(null);
  const N = panels.length;

  useEffect(() => {
    const onScroll = () => {
      const node = stageRef.current;
      if (!node) return;
      const r = node.getBoundingClientRect();
      const total = node.offsetHeight - window.innerHeight;
      const scrolled = Math.min(Math.max(-r.top, 0), total);
      const p = total > 0 ? scrolled / total : 0;
      const idx = Math.min(N - 1, Math.floor(p * N + 0.0001));
      setActive(prev => (prev === idx ? prev : idx));
    };
    window.addEventListener('scroll', onScroll, { passive: true });
    window.addEventListener('resize', onScroll);
    onScroll();
    return () => {
      window.removeEventListener('scroll', onScroll);
      window.removeEventListener('resize', onScroll);
    };
  }, [N]);

  const panel = panels[active];
  if (!panel) return null;

  const goTo = (i) => {
    const node = stageRef.current;
    if (!node) return;
    const total = node.offsetHeight - window.innerHeight;
    const target = node.offsetTop + (total * (i + 0.5)) / N;
    window.scrollTo({ top: target, behavior: 'smooth' });
  };

  return (
    <div ref={stageRef} className="dim-stage insight-stage" style={{ height: `${N * 100}vh` }}>
      <div className="dim-stage-inner">
        <div className="dim-stage-grid">
          <aside className="dim-stage-nav">
            <ol className="nav-list">
              {panels.map((p, i) => (
                <li
                  key={p.key}
                  className={'nav-item ' + (i === active ? 'active' : '')}
                  onClick={() => goTo(i)}>
                  <span className="nav-cat">{p.navLabel}</span>
                </li>
              ))}
            </ol>
            <button className="back-to-top" onClick={() => window.scrollTo(0, 0)}>
              <span className="arrow">↑</span> Back to top
            </button>
          </aside>
          <div className="dim-stage-main" key={active}>
            <div className="dim-titles">
              <h3>{panel.title}</h3>
              {panel.stat && (
                <div className="dim-stats">
                  <div className="stat"><strong>{panel.stat.value}</strong>{panel.stat.label}</div>
                </div>
              )}
            </div>
            {panel.summary && <p className="dim-summary">{panel.summary}</p>}
            <div className="dim-chart-wrap">{panel.render()}</div>
          </div>
        </div>
      </div>
    </div>
  );
};
