// Shared components + helpers
const { useState, useEffect, useMemo, useRef, useCallback } = React;

const COUNTRY_META = {
  "ARGENTINA": { iso: "AR", iso3: "ARG" },
  "BRAZIL": { iso: "BR", iso3: "BRA" },
  "CHINA": { iso: "CN", iso3: "CHN" },
  "EL SALVADOR": { iso: "SV", iso3: "SLV" },
  "HONG KONG": { iso: "HK", iso3: "HKG" },
  "INDIA": { iso: "IN", iso3: "IND" },
  "INDONESIA": { iso: "ID", iso3: "IDN" },
  "ITALY": { iso: "IT", iso3: "ITA" },
  "JAPAN": { iso: "JP", iso3: "JPN" },
  "KENYA": { iso: "KE", iso3: "KEN" },
  "LEBANON": { iso: "LB", iso3: "LBN" },
  "MEXICO": { iso: "MX", iso3: "MEX" },
  "NIGERIA": { iso: "NG", iso3: "NGA" },
  "PHILIPPINES": { iso: "PH", iso3: "PHL" },
  "POLAND": { iso: "PL", iso3: "POL" },
  "RUSSIA": { iso: "RU", iso3: "RUS" },
  "SAUDI ARABIA": { iso: "SA", iso3: "SAU" },
  "SOUTH AFRICA": { iso: "ZA", iso3: "ZAF" },
  "SOUTH KOREA": { iso: "KR", iso3: "KOR" },
  "SWITZERLAND": { iso: "CH", iso3: "CHE" },
  "TURKEY": { iso: "TR", iso3: "TUR" },
  "UAE": { iso: "AE", iso3: "ARE" },
  "UKRAINE": { iso: "UA", iso3: "UKR" },
  "US": { iso: "US", iso3: "USA" },
  "VENEZUELA": { iso: "VE", iso3: "VEN" }
};

function flagEmoji(iso) {
  return String.fromCodePoint(...iso.split('').map((c) => 0x1f1a5 + c.charCodeAt(0)));
}

// Shared metric options for the Map and Globe (identical in both)
const MAP_METRICS = [
  { k: 'awareness', l: 'Bitcoin Awareness', base: 'All survey respondents' },
  { k: 'ownEver', l: 'Ever Owned Bitcoin', base: 'All survey respondents' },
  { k: 'ownCurrent', l: 'Currently Own Bitcoin', base: 'All survey respondents' },
  { k: 'financialFreedom', l: 'Increases Financial Freedom', base: 'Among those aware of Bitcoin · rated 5–7 of 1–7' },
  { k: 'protectsPrivacy', l: 'Protects Privacy', base: 'Among those aware of Bitcoin · rated 5–7 of 1–7' },
  { k: 'confusing', l: 'Is Confusing', base: 'Among those aware of Bitcoin · rated 5–7 of 1–7' },
  { k: 'proneToFraud', l: 'Prone to Fraud', base: 'Among those aware of Bitcoin · rated 5–7 of 1–7' },
  { k: 'knows21M', l: 'Knows Supply Capped at 21M', base: 'Among those aware of Bitcoin' }
];

// Country ↔ URL slug (for shareable hash links like #/nigeria)
const COUNTRY_SLUG = {};
const SLUG_COUNTRY = {};
Object.keys(COUNTRY_META).forEach((name) => {
  const disp = { 'US': 'united-states', 'UAE': 'united-arab-emirates' }[name] ||
    name.toLowerCase().replace(/\s+/g, '-');
  COUNTRY_SLUG[name] = disp;
  SLUG_COUNTRY[disp] = name;
});

function titleCase(s) {
  if (!s) return '';
  // Display overrides for short/abbreviated keys
  const overrides = {
    'US': 'United States',
    'UAE': 'United Arab Emirates'
  };
  if (overrides[s]) return overrides[s];
  return s.toLowerCase().split(' ').map((w) => w[0]?.toUpperCase() + w.slice(1)).join(' ');
}

// Capitalize every word (for short labels like answer-choice text)
function titleCaseWords(s) {
  if (!s) return '';
  return s.split(/(\s+)/).map((part) => {
    if (/^\s+$/.test(part)) return part;
    // Preserve acronyms already in all-caps (XRP, ETH, BNB, USDT, MATIC…)
    if (/^[A-Z][A-Z0-9]+$/.test(part)) return part;
    return part.charAt(0).toUpperCase() + part.slice(1).toLowerCase();
  }).join('');
}

function fmtPct(v, digits = 0) {
  if (v == null || isNaN(v)) return '—';
  return (v * 100).toFixed(digits) + '%';
}
function fmtNum(v) {
  if (v == null || isNaN(v)) return '—';
  return v.toLocaleString();
}

// --- Question base / subsample definitions (who each question was asked to) ---
// Source: subsamples.xlsx (Cornell Brooks). Keyed by the question's base CU number.
const QUESTION_BASE_GROUPS = {
  all:              { label: 'Asked of all respondents', short: 'All survey respondents', sourceCU: [] },
  heardCrypto:      { label: 'Asked of respondents who had heard of cryptocurrency', short: 'Aware of crypto', sourceCU: ['CU3'] },
  heardBtc:         { label: 'Asked of respondents who had heard of Bitcoin', short: 'Aware of Bitcoin', sourceCU: ['CU6'] },
  everOwned:        { label: 'Asked of respondents who had ever owned Bitcoin', short: 'Ever owned Bitcoin', sourceCU: ['CU6', 'CU10'] },
  currentOwn:       { label: 'Asked of respondents who currently own Bitcoin', short: 'Current owners', sourceCU: ['CU6', 'CU10'] },
  prevOwned:        { label: 'Asked of respondents who previously owned Bitcoin', short: 'Previous owners', sourceCU: ['CU6', 'CU10'] },
  heardNever:       { label: 'Asked of respondents who had heard of Bitcoin but never owned it', short: 'Aware, never owned', sourceCU: ['CU6', 'CU10'] },
  prevOrNever:      { label: 'Asked of respondents who previously owned or never owned Bitcoin (but had heard of it)', short: 'Previous / never owned', sourceCU: ['CU6', 'CU10'] },
  heardStable:      { label: 'Asked of respondents who had heard of stablecoins', short: 'Aware of stablecoins', sourceCU: ['CU29'] },
  ownStable:        { label: 'Asked of respondents who own or have owned stablecoins', short: 'Stablecoin owners', sourceCU: ['CU29', 'CU33'] },
  heardStableNever: { label: 'Asked of respondents who had heard of stablecoins but never owned them', short: 'Aware of stablecoins, never owned', sourceCU: ['CU29', 'CU33'] }
};
const QUESTION_BASE_MAP = {
  CU1:'all', CU2:'all', CU3:'all', CU6:'all', CU7:'all', CU23:'all', CU29:'all',
  CU38:'all', CU39:'all', CU40:'all', CU41:'all', CU42:'all', CU43:'all', CU44:'all',
  CU45:'all', CU46:'all', CU47:'all', CU48:'all', CU49:'all', CU50:'all',
  CU4:'heardCrypto', CU5:'heardCrypto',
  CU8:'heardBtc', CU9:'heardBtc', CU10:'all', CU25:'heardBtc', CU26:'heardBtc', CU27:'heardBtc', CU28:'heardBtc',
  CU11:'everOwned',
  CU12:'currentOwn', CU14:'currentOwn', CU18:'currentOwn', CU21:'currentOwn', CU22:'currentOwn',
  CU13:'prevOwned', CU15:'prevOwned', CU16:'prevOwned', CU19:'prevOwned',
  CU17:'heardNever',
  CU20:'prevOrNever',
  CU30:'heardStable', CU31:'heardStable', CU32:'heardStable', CU33:'all',
  CU34:'ownStable', CU35:'ownStable', CU36:'ownStable',
  CU37:'heardStableNever'
};
// Resolve any question id (CU27d_5, CU2_1NET, CU21_3 …) to its base group definition.
function getQuestionBase(id) {
  const m = String(id || '').match(/^CU(\d+)/);
  if (!m) return null;
  const key = QUESTION_BASE_MAP['CU' + m[1]];
  return key ? QUESTION_BASE_GROUPS[key] : null;
}

// Explanatory footnotes for questions re-based to all survey respondents.
const QUESTION_NOTE_MAP = {
  CU10: 'This question was asked of respondents who had heard of Bitcoin. All respondents who had not heard of Bitcoin are here classified as having never owned Bitcoin. China and Hong Kong (2,073 respondents) are excluded because Bitcoin-ownership questions were not fielded there, so the base is 23,807 of the 25,880 total respondents.',
  CU12: 'Respondents were not asked who controls the private keys, and no custodial-wallet option was offered. The wallet categories therefore report a method of holding rather than verified self-custody; only the physical hardware wallet is unambiguous evidence of independent custody. China and Hong Kong are not shown: Bitcoin-ownership questions were not fielded there.',
  CU13: 'Asked of former owners about the period when they held Bitcoin. Respondents were not asked who controls the private keys, and no custodial-wallet option was offered. The wallet categories therefore report a method of holding rather than verified self-custody; only the physical hardware wallet is unambiguous evidence of independent custody. China and Hong Kong are not shown: Bitcoin-ownership questions were not fielded there.',
  CU33: 'This question was asked of respondents who had heard of stablecoins. All respondents who had not heard of stablecoins are here classified as having never owned stablecoins.'
};
function getQuestionNote(id) {
  const m = String(id || '').match(/^CU(\d+)/);
  if (!m) return null;
  return QUESTION_NOTE_MAP['CU' + m[1]] || null;
}

// Tooltip singleton
function useTooltip() {
  const elRef = useRef(null);
  useEffect(() => {
    let el = document.getElementById('__tip');
    if (!el) {
      el = document.createElement('div');
      el.id = '__tip';
      el.className = 'tip';
      el.style.opacity = '0';
      document.body.appendChild(el);
    }
    elRef.current = el;
  }, []);
  const show = (e, html) => {
    const el = elRef.current;if (!el) return;
    el.innerHTML = html;
    const x = Math.min(e.clientX + 12, window.innerWidth - 280);
    const y = Math.min(e.clientY + 14, window.innerHeight - 60);
    el.style.left = x + 'px';el.style.top = y + 'px';el.style.opacity = '1';
  };
  const move = (e) => show(e, elRef.current?.innerHTML || '');
  const hide = () => {if (elRef.current) elRef.current.style.opacity = '0';};
  return { show, move, hide };
}

// --- Chart image export (with source attribution) ---
function loadHtmlToImage() {
  if (window.htmlToImage) return Promise.resolve(window.htmlToImage);
  return new Promise((res, rej) => {
    const existing = document.querySelector('script[data-h2i]');
    if (existing) { existing.addEventListener('load', () => res(window.htmlToImage)); return; }
    const s = document.createElement('script');
    s.src = 'https://unpkg.com/html-to-image@1.11.13/dist/html-to-image.js';
    s.setAttribute('data-h2i', '1');
    s.onload = () => res(window.htmlToImage); s.onerror = rej;
    document.head.appendChild(s);
  });
}
async function exportChartImage(node, filename) {
  const htmlToImage = await loadHtmlToImage();
  const cs = getComputedStyle(document.body);
  const bg = (cs.getPropertyValue('--surface') || cs.backgroundColor || '#ffffff').trim() || '#ffffff';
  const muted = (cs.getPropertyValue('--muted') || '#888').trim();
  const rule = (cs.getPropertyValue('--rule') || '#ddd').trim();
  const footer = document.createElement('div');
  footer.style.cssText = `padding:12px 18px;margin-top:8px;border-top:1px solid ${rule};font-family:'JetBrains Mono',monospace;font-size:11px;letter-spacing:.06em;color:${muted};background:${bg}`;
  footer.innerHTML = '<span>Source: Cornell Bitcoin Adoption Index</span>';
  footer.setAttribute('data-noexport-skip', '1');
  node.appendChild(footer);
  const prevPad = node.style.padding;
  if (!prevPad || prevPad === '0px') node.style.padding = '20px';
  try {
    const dataUrl = await htmlToImage.toPng(node, { pixelRatio: 2, backgroundColor: bg, cacheBust: true, filter: (n) => !(n.dataset && n.dataset.noexport) });
    const a = document.createElement('a');
    a.href = dataUrl;
    a.download = filename || 'cornell-bitcoin-adoption-index.png';
    a.click();
  } finally {
    footer.remove();
    node.style.padding = prevPad;
  }
}
function ExportButton({ targetRef, filename, style }) {
  const [busy, setBusy] = useState(false);
  return (
    <button
      data-noexport="1"
      onClick={async () => {
        if (!targetRef.current || busy) return;
        setBusy(true);
        try { await exportChartImage(targetRef.current, filename); }
        catch (e) { console.error('export failed', e); }
        setBusy(false);
      }}
      title="Download this graphic as an image (with source credit)"
      style={{ background: 'transparent', color: 'var(--ink)', border: '1px solid var(--rule)', padding: '5px 12px', fontFamily: 'JetBrains Mono, monospace', fontSize: 10, textTransform: 'uppercase', letterSpacing: '.1em', cursor: busy ? 'wait' : 'pointer', whiteSpace: 'nowrap', ...style }}>
      {busy ? 'Rendering…' : '⤓ Image'}
    </button>);
}

// Wraps a chart so it gets a floating download-image button (excluded from the capture)
function Exportable({ filename, children, style, buttonStyle }) {
  const ref = useRef(null);
  return (
    <div ref={ref} style={{ position: 'relative', ...style }}>
      <div data-noexport="1" style={{ position: 'absolute', top: 10, right: 10, zIndex: 5, ...buttonStyle }}>
        <ExportButton targetRef={ref} filename={filename} />
      </div>
      {children}
    </div>);
}

function SiteHeader({ route, setRoute, dark, setDark }) {
  const links = [
  ['home', 'Overview'],
  ['methodology', 'Methodology'],
  ['findings', 'Key Findings'],
  ['voices', 'Voices'],
  ['countries', 'Countries'],
  ['questions', 'Questions'],
  ['map', 'Explore'],
  ['updates', 'Updates'],
  ['about', 'About']];

  return (
    <header className="site-header">
      <div className="bar">
        <a className="brand-mark" onClick={() => setRoute('home')} style={{ cursor: 'pointer' }}>
          <span className="shield"></span>
          <div>
            <div className="title">Cornell Bitcoin Adoption Index</div>
            <div className="sub">Tech Policy Institute · Cornell University</div>
          </div>
        </a>
        <nav className="primary">
          {links.map(([k, l]) =>
          <button key={k} className={route === k ? 'active' : ''} onClick={() => setRoute(k)}>{l}</button>
          )}
        </nav>
        <button className="theme-toggle" onClick={() => setDark(!dark)} title="Toggle theme" aria-label="Toggle theme">
          {dark ?
          <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><circle cx="12" cy="12" r="4" /><path d="M12 2v2M12 20v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M2 12h2M20 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42" /></svg> :

          <svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor"><path d="M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z" /></svg>
          }
        </button>
      </div>
    </header>);

}

function SiteFooter({ setRoute }) {
  return (
    <footer>
      <div className="container">
        <div className="fgrid">
          <div>
            <div className="brand-mark" style={{ marginBottom: 12 }}>
              <span className="shield"></span>
              <div>
                <div className="title">Cornell Bitcoin Adoption Index</div>
                <div className="sub">2026 · 25 Countries · 25,880 respondents</div>
              </div>
            </div>
            <p style={{ color: 'var(--muted)', fontSize: 13, maxWidth: '46ch' }}>
              A global survey on public awareness, ownership, and perceptions of Bitcoin, conducted in partnership with Morning Consult.
            </p>
          </div>
          <div>
            <h4>Data</h4>
            <a onClick={() => setRoute('findings')} style={{ cursor: 'pointer' }}>Key Findings</a>
            <a onClick={() => setRoute('voices')} style={{ cursor: 'pointer' }}>Voices</a>
            <a onClick={() => setRoute('countries')} style={{ cursor: 'pointer' }}>Country Explorer</a>
            <a onClick={() => setRoute('questions')} style={{ cursor: 'pointer' }}>Question Explorer</a>
            <a onClick={() => setRoute('map')} style={{ cursor: 'pointer' }}>Explore</a>
            <a onClick={() => setRoute('methodology')} style={{ cursor: 'pointer' }}>Methodology</a>
          </div>
          <div>
            <h4>Research</h4>
            <a onClick={() => setRoute('updates')} style={{ cursor: 'pointer' }}>Updates</a>
            <a href="https://www.cornellbitcoinclub.org/" target="_blank" rel="noreferrer">Cornell Bitcoin Club</a>
            <a onClick={() => setRoute('about')} style={{ cursor: 'pointer' }}>About & Team</a>
          </div>
          <div>
            <h4>Field work</h4>
            <div style={{ fontSize: 12, color: 'var(--muted)', fontFamily: 'JetBrains Mono, monospace', textTransform: 'uppercase', letterSpacing: '.1em' }}>
              Poll 2411046<br />
              16 Dec 2024 –<br />10 Mar 2025
            </div>
          </div>
        </div>
      </div>
    </footer>);

}

Object.assign(window, {
  useState, useEffect, useMemo, useRef, useCallback,
  COUNTRY_META, COUNTRY_SLUG, SLUG_COUNTRY, flagEmoji, MAP_METRICS, titleCase, titleCaseWords, fmtPct, fmtNum, useTooltip,
  QUESTION_BASE_GROUPS, QUESTION_BASE_MAP, getQuestionBase, QUESTION_NOTE_MAP, getQuestionNote,
  exportChartImage, ExportButton, Exportable,
  SiteHeader, SiteFooter
});