// 3D panels. The engine lives in qdi3d-core.js and the qdi3d-scenes-*.js files; this file wraps each scene with
// its controls. Every panel keeps a fixed size, so nothing on the page moves when someone interacts with it.
// With no WebGL the panel shows a calm static card and the controls still work.

const R3 = React;

function s3ok() { return !!(window.QDI3D && window.QDI3D.ok && window.THREE); }
function s3coarse() { return !!(window.matchMedia && window.matchMedia('(hover: none)').matches); }

// Canvas labels use Google Sans, so the font has to be ready before a scene draws them.
let s3font = null;
function s3fontReady() {
  if (!s3font) {
    const load = (document.fonts && document.fonts.load) ? document.fonts.load('500 16px "Google Sans"').catch(() => {}) : Promise.resolve();
    s3font = Promise.race([load, new Promise((r) => setTimeout(r, 1800))]);
  }
  return s3font;
}

// ─── Stage3D: mounts one scene lazily, forwards callbacks, shows a tooltip ──────────────────────────────────────
function Stage3D({ scene, options, handlers, onApi, onPhase, ratio, hint, hintPos, ariaLabel, eager, fallback }) {
  const wrapRef = R3.useRef(null);
  const hostRef = R3.useRef(null);
  const tipRef = R3.useRef(null);
  const xy = R3.useRef([0, 0]);
  const hRef = R3.useRef(handlers || {}); hRef.current = handlers || {};
  const oRef = R3.useRef(options || {}); oRef.current = options || {};
  const apiCb = R3.useRef(onApi); apiCb.current = onApi;
  const phCb = R3.useRef(onPhase); phCb.current = onPhase;
  const [phase, setPhase] = R3.useState('idle');
  const [tip, setTip] = R3.useState(null);
  const [touched, setTouched] = R3.useState(false);

  const place = R3.useCallback(() => {
    const wrap = wrapRef.current, el = tipRef.current;
    if (!wrap || !el) return;
    const r = wrap.getBoundingClientRect();
    const w = el.offsetWidth, h = el.offsetHeight;
    const x = xy.current[0] - r.left, y = xy.current[1] - r.top;
    let left = x + 16, top = y + 18;
    if (left + w > r.width - 8) left = x - w - 16;
    if (top + h > r.height - 8) top = y - h - 14;
    left = Math.max(8, Math.min(left, Math.max(8, r.width - w - 8)));
    top = Math.max(8, Math.min(top, Math.max(8, r.height - h - 8)));
    el.style.transform = 'translate(' + Math.round(left) + 'px,' + Math.round(top) + 'px)';
  }, []);

  R3.useLayoutEffect(() => { if (tip) place(); }, [tip, place]);

  R3.useEffect(() => {
    const flip = (p) => { setPhase(p); if (phCb.current) phCb.current(p); };
    if (!s3ok()) { flip('failed'); return undefined; }
    let stage = null, dead = false, io = null, tipTimer = 0, starting = false;
    const fwd = (k) => (...a) => { const f = hRef.current[k]; if (f) f(...a); };
    const mount = () => {
      if (stage || dead || starting || !hostRef.current) return;
      starting = true;
      s3fontReady().then(() => {
        if (dead || !hostRef.current) return;
        const opts = Object.assign({}, oRef.current, {
          onHover: (pick, x, y) => {
            if (s3coarse()) return;
            if (pick && pick.title) { xy.current = [x, y]; setTip({ title: pick.title, text: pick.text || '' }); } else setTip(null);
          },
          onMove: (x, y) => { xy.current = [x, y]; place(); },
          onPick: (obj) => {
            if (!s3coarse()) return;
            clearTimeout(tipTimer);
            if (obj && obj.userData && obj.userData.pick && obj.userData.pick.title) {
              const s = stage; xy.current = [s ? s.cx : 0, s ? s.cy : 0];
              setTip({ title: obj.userData.pick.title, text: obj.userData.pick.text || '' });
              tipTimer = setTimeout(() => setTip(null), 3600);
            } else setTip(null);
          },
          onFail: () => { flip('failed'); },
          onState: fwd('onState'), onSelect: fwd('onSelect'),
        });
        stage = window.QDI3D.mount(hostRef.current, scene, opts);
        if (!stage) { flip('failed'); return; }
        flip('ready');
        if (apiCb.current) apiCb.current(stage.api, stage);
      });
    };
    if (eager || typeof IntersectionObserver === 'undefined') mount();
    else {
      io = new IntersectionObserver((es) => { if (es.some((e) => e.isIntersecting)) { mount(); io.disconnect(); } }, { rootMargin: '320px 0px' });
      io.observe(wrapRef.current);
    }
    return () => {
      dead = true; clearTimeout(tipTimer);
      if (io) io.disconnect();
      if (stage) { try { stage.dispose(); } catch (e) {} stage = null; }
      if (apiCb.current) apiCb.current(null, null);
    };
  }, [scene]);

  return (
    <div
      ref={wrapRef}
      className="q3-wrap"
      style={ratio ? { '--ar': ratio } : undefined}
      onPointerDown={() => { if (!touched) setTouched(true); }}
      role={ariaLabel ? 'img' : undefined}
      aria-label={ariaLabel}
    >
      <div ref={hostRef} className="q3-host" />
      {phase !== 'ready' && (
        <div className={'q3-skel' + (phase === 'failed' ? ' q3-fail' : '')}>
          {phase === 'failed' ? (fallback || <ShieldMark />) : <span>Preparing 3D scene</span>}
          {phase === 'failed' && <span>3D view not available on this device</span>}
        </div>
      )}
      {hint && phase === 'ready' && <div className={'q3-hint' + (hintPos === 'bl' ? ' bl' : '') + (touched ? ' off' : '')}>{hint}</div>}
      <div ref={tipRef} className={'q3-tip' + (tip ? ' on' : '')} aria-hidden="true">
        {tip && <b>{tip.title}</b>}
        {tip && tip.text && <span>{tip.text}</span>}
      </div>
    </div>
  );
}

function ShieldMark() {
  return (
    <svg width="72" height="72" viewBox="0 0 64 64" fill="none" aria-hidden="true">
      <path d="M32 6l20 7v16c0 13-8.6 22.6-20 27C20.6 51.6 12 42 12 29V13l20-7z" fill="rgba(15,164,175,0.22)" stroke="#AFDDE5" strokeWidth="2" strokeLinejoin="round" />
      <path d="M23 31l7 7 12-14" stroke="#AFDDE5" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round" />
    </svg>
  );
}

// ─── Shared shell: stage on top, controls underneath, always the same size ──────────────────────────────────────
function Panel3D({ children, bar, className }) {
  return (
    <div className={'q3' + (className ? ' ' + className : '')}>
      {children}
      {bar && <div className="q3-bar">{bar}</div>}
    </div>
  );
}

function Chips({ items, value, onChange, label, light, cols }) {
  return (
    <div className={'q3-chips' + (light ? ' light' : '') + (cols ? ' grid' : '')} style={cols ? { '--cols': cols } : undefined} role="tablist" aria-label={label}>
      {items.map((it) => (
        <button key={it.id} role="tab" aria-selected={value === it.id} className={'q3-chip' + (value === it.id ? ' on' : '')} onClick={() => onChange(it.id)}>{it.label}</button>
      ))}
    </div>
  );
}

const SECTOR_LIST = [
  { id: 'infra', label: 'Infrastructure', name: 'Critical infrastructure' },
  { id: 'health', label: 'Healthcare', name: 'Healthcare' },
  { id: 'finance', label: 'Finance', name: 'Finance' },
  { id: 'defence', label: 'Defence', name: 'Defence' },
  { id: 'gov', label: 'Government', name: 'Government' },
  { id: 'intel', label: 'Intelligence', name: 'Intelligence' },
];

// ─── Home hero: the block sphere under attack ───────────────────────────────────────────────────────────────────
function HeroOrb() {
  const api = R3.useRef(null);
  const [blocked, setBlocked] = R3.useState(0);
  return (
    <Panel3D
      className="q3-dark"
      bar={(
        <div className="q3-row">
          <button className="q3-btn solid" onClick={() => api.current && api.current.launch()}>Launch a test attack</button>
          <span className="q3-stat" aria-live="polite">Attacks blocked <b>{blocked}</b></span>
        </div>
      )}
    >
      <Stage3D scene="orb" eager ratio="1.08" hint="Drag to rotate" ariaLabel="A sphere of protective blocks stopping incoming attacks"
        handlers={{ onState: (s) => { if (s && typeof s.blocked === 'number') setBlocked(s.blocked); } }}
        onApi={(a) => { api.current = a; }} />
    </Panel3D>
  );
}

// ─── Harvest now, decrypt later ─────────────────────────────────────────────────────────────────────────────────
function HarvestSim() {
  const api = R3.useRef(null);
  const [t, setT] = R3.useState(40);
  const [pqc, setPqc] = R3.useState(false);
  const [st, setSt] = R3.useState({ captured: 0, exposed: 0, sealed: 0, arrived: false });
  const sync = (a, tv, pv) => { if (a) { a.setTime(tv / 100); a.setPqc(pv); } };
  R3.useEffect(() => { sync(api.current, t, pqc); }, [t, pqc]);

  let msg;
  if (!st.arrived) msg = st.captured ? 'An attacker is copying encrypted traffic and storing it. ' + st.captured + ' packets saved so far.' : 'Move the slider to let time pass.';
  else if (pqc) msg = 'A quantum computer arrives. All ' + st.sealed + ' stored packets stay sealed.';
  else msg = 'A quantum computer arrives. ' + st.exposed + ' of ' + st.captured + ' stored packets can now be read.';

  return (
    <Panel3D
      className="q3-dark"
      bar={(
        <>
          <div className="q3-row">
            <div className="q3-seg" role="group" aria-label="Encryption type">
              <button className={!pqc ? 'on' : ''} onClick={() => setPqc(false)}>Today&#39;s encryption</button>
              <button className={pqc ? 'on' : ''} onClick={() => setPqc(true)}>Quantum safe</button>
            </div>
          </div>
          <div className="q3-row q3-slider">
            <span>Now</span>
            <input type="range" min="0" max="100" value={t} onChange={(e) => setT(+e.target.value)} aria-label="Time passing" />
            <span>Years later</span>
          </div>
          <p className="q3-status" aria-live="polite">{msg}</p>
          <p className="q3-note">An illustration of the idea, not a prediction of when quantum computers will arrive.</p>
        </>
      )}
    >
      <Stage3D scene="harvest" ratio="1.7" hint="Drag to look around" ariaLabel="Encrypted traffic being copied now and read later"
        handlers={{ onState: (s) => setSt(s) }}
        onApi={(a) => { api.current = a; sync(a, t, pqc); }} />
    </Panel3D>
  );
}

// ─── Solutions: the six sectors ─────────────────────────────────────────────────────────────────────────────────
const SECTOR_LINE = {
  infra: 'Power, water and transport networks run for decades, and many cannot be taken offline to upgrade.',
  health: 'Patient records and connected care need privacy for a lifetime, across many organisations.',
  finance: 'Transactions and archives must stay private and untampered for as long as they exist.',
  defence: 'Field units, vehicles and data centres share traffic over links nobody fully controls.',
  gov: 'Departmental and classified systems carry material with long secrecy requirements.',
  intel: 'Some material must stay secret for decades, which is the exact target of harvest now, decrypt later.',
};

function SectorStage({ sector, onSector }) {
  const api = R3.useRef(null);
  R3.useEffect(() => { if (api.current) api.current.select(sector); }, [sector]);
  const cur = SECTOR_LIST.find((s) => s.id === sector) || SECTOR_LIST[0];
  return (
    <Panel3D
      className="q3-dark"
      bar={(
        <>
          <Chips cols={3} items={SECTOR_LIST} value={sector} onChange={onSector} label="Sector" />
          <p className="q3-status"><b>{cur.name}.</b> {SECTOR_LINE[sector]}</p>
        </>
      )}
    >
      <Stage3D scene="sectors" ratio="1.25" hint="Click a sector" ariaLabel="Six sectors QDI protects"
        handlers={{ onSelect: (id) => onSector(id) }}
        onApi={(a) => { api.current = a; if (a) a.select(sector); }} />
    </Panel3D>
  );
}

// ─── Solutions: how the chip works in each sector ───────────────────────────────────────────────────────────────
const CHIP_SECTOR = {
  infra: 'Sits on the link between a control room and a substation. Telemetry keeps flowing and nothing goes offline.',
  health: 'Sits between a records system and the wider network, so referrals and results are sealed in transit.',
  finance: 'Seals transaction batches between sites and protects archives that must stay private for decades.',
  defence: 'Travels with field units and seals traffic back to the data centre, even over untrusted links.',
  gov: 'Works on isolated on premise systems, so departmental material is sealed before it leaves the room.',
  intel: 'Protects material that must stay secret for decades, even from an attacker who stores it today.',
};
const CHIP_LAYERS = [
  { id: 0, title: 'Entropy source', text: 'Hardware random numbers (TRNG or QRNG) seed every key.' },
  { id: 1, title: 'Key engine', text: 'Kyber1024 (ML-KEM) generates and handles post quantum keys inside the device.' },
  { id: 2, title: 'Cipher stack', text: 'Up to four stacked ciphers protect the data on top of the key exchange.' },
  { id: 3, title: 'Key vault', text: 'Private keys are sealed here and never leave the hardware.' },
];

function ChipLab({ sector }) {
  const api = R3.useRef(null);
  const [open, setOpen] = R3.useState(true);
  const [layer, setLayer] = R3.useState(-1);
  R3.useEffect(() => { if (api.current) api.current.setSector(sector); }, [sector]);
  const toggle = () => { const v = !open; setOpen(v); if (api.current) api.current.explode(v); };
  const pick = (i) => { setLayer(i); setOpen(true); if (api.current) { api.current.explode(true); api.current.select(i); } };
  const info = layer >= 0 ? CHIP_LAYERS[layer] : null;
  return (
    <Panel3D
      className="q3-dark"
      bar={(
        <>
          <div className="q3-row">
            <button className="q3-btn" onClick={toggle}>{open ? 'Close the chip' : 'Open the chip'}</button>
            <button className="q3-btn solid" onClick={() => { setOpen(true); if (api.current) api.current.run(); }}>Send data through</button>
          </div>
          <Chips cols={4} items={CHIP_LAYERS.map((l) => ({ id: l.id, label: l.title }))} value={layer} onChange={pick} label="Chip layer" />
          <p className="q3-status">{info ? <><b>{info.title}.</b> {info.text}</> : CHIP_SECTOR[sector]}</p>
        </>
      )}
    >
      <Stage3D scene="chip" ratio="1.25" hint="Drag to rotate" ariaLabel="A QDI chip between your system and the network"
        options={{ sector }}
        handlers={{ onSelect: (i) => setLayer(i) }}
        onApi={(a) => { api.current = a; if (a) { a.setSector(sector); a.explode(true); } }} />
    </Panel3D>
  );
}

// ─── The key: five steps ────────────────────────────────────────────────────────────────────────────────────────
const KEY_STEPS = [
  { t: 'Bind', b: 'The key unlocks with a password or fingerprint and checks its own hardware serial number, so the software only runs from its paired key.' },
  { t: 'Generate', b: 'A Kyber1024 key pair is created inside the key from hardware randomness. The private half never leaves.' },
  { t: 'Exchange', b: 'Only the public key travels, over ordinary email or file transfer. There is nothing secret in it.' },
  { t: 'Encrypt', b: 'Messages and files are locked with up to four stacked ciphers on top of the key exchange.' },
  { t: 'Decrypt', b: 'Only the paired key holding the matching private key can unlock it. The private key never touches the computer, the network or the cloud.' },
];

function KeyFlow() {
  const api = R3.useRef(null);
  const [step, setStep] = R3.useState(0);
  R3.useEffect(() => { if (api.current) api.current.step(step); }, [step]);
  const s = KEY_STEPS[step];
  return (
    <Panel3D
      className="q3-dark"
      bar={(
        <>
          <ol className="q3-steps" aria-label="Steps">
            {KEY_STEPS.map((k, i) => (
              <li key={k.t}><button className={(i === step ? 'on ' : '') + (i < step ? 'done' : '')} onClick={() => setStep(i)} aria-current={i === step ? 'step' : undefined}><i>{i + 1}</i><span>{k.t}</span></button></li>
            ))}
          </ol>
          <p className="q3-status"><b>{s.t}.</b> {s.b}</p>
          <div className="q3-row">
            <button className="q3-btn" disabled={step === 0} onClick={() => setStep(step - 1)}>Back</button>
            <button className="q3-btn solid" disabled={step === KEY_STEPS.length - 1} onClick={() => setStep(step + 1)}>Next step</button>
          </div>
        </>
      )}
    >
      <Stage3D scene="flow" ratio="1.5" hint="Drag to look around" ariaLabel="Two computers exchanging keys and encrypted files"
        onApi={(a) => { api.current = a; if (a) a.step(step); }} />
    </Panel3D>
  );
}

// ─── The key, taken apart (Partners, and a small teaser on Home) ────────────────────────────────────────────────
const FORGE_PARTS = [
  { id: 'pcb', label: 'Hardware key', text: 'The secure chip and its board. Private keys stay here.' },
  { id: 'crypto', label: 'Crypto engine', text: 'Kyber1024 key exchange and a stack of ciphers.' },
  { id: 'apps', label: 'Applications', text: 'Encrypted messaging and file encryption that run from the key.' },
  { id: 'back', label: 'Enclosure', text: 'Designed and built by Qryptec.' },
];

function KeyForge() {
  const api = R3.useRef(null);
  const [v, setV] = R3.useState(0);
  const [part, setPart] = R3.useState(null);
  R3.useEffect(() => { if (api.current) api.current.assemble(v / 100); }, [v]);
  const choose = (id) => { setPart(id); if (api.current) api.current.select(id); };
  const info = FORGE_PARTS.find((p) => p.id === part || (part === 'front' && p.id === 'back'));
  return (
    <Panel3D
      className="q3-dark"
      bar={(
        <>
          <div className="q3-row q3-slider">
            <span>Taken apart</span>
            <input type="range" min="0" max="100" value={v} onChange={(e) => setV(+e.target.value)} aria-label="Assemble the key" />
            <span>Assembled</span>
          </div>
          <Chips cols={4} items={FORGE_PARTS.map((p) => ({ id: p.id, label: p.label }))} value={part === 'front' ? 'back' : part} onChange={choose} label="Part" />
          <p className="q3-status">{info ? <><b>{info.label}.</b> {info.text}</> : 'QDI designs the security. Qryptec designs and builds the hardware.'}</p>
        </>
      )}
    >
      <Stage3D scene="forge" ratio="1.25" hint="Drag to rotate" hintPos="bl" ariaLabel="The hardware key, shown as separate layers"
        handlers={{ onSelect: (id) => setPart(id) }}
        onApi={(a) => { api.current = a; if (a) a.assemble(v / 100); }} />
    </Panel3D>
  );
}

function KeyTeaser() {
  return (
    <Panel3D className="q3-dark" bar={<p className="q3-status">Click the key to put it together or take it apart.</p>}>
      <Stage3D scene="forge" eager ratio="1.4" options={{ mode: 'teaser' }} hint="Click to assemble" ariaLabel="The QDI hardware key" />
    </Panel3D>
  );
}

// ─── Team: where we work ────────────────────────────────────────────────────────────────────────────────────────
function GlobeStage() {
  const api = R3.useRef(null);
  const [loc, setLoc] = R3.useState(null);
  const locs = (window.QDI3D && window.QDI3D.LOCATIONS) || [];
  const items = locs.map((l) => ({ id: l.id, label: l.name }));
  const choose = (id) => { setLoc(id); if (api.current) api.current.focus(id); };
  const cur = locs.find((l) => l.id === loc);
  return (
    <Panel3D
      className="q3-dark"
      bar={(
        <>
          <Chips cols={3} items={items} value={loc} onChange={choose} label="Location" />
          <p className="q3-status">{cur ? <><b>{cur.name}.</b> {cur.note}</> : 'Our team is based across six countries.'}</p>
        </>
      )}
    >
      <Stage3D scene="globe" ratio="1.4" hint="Drag to turn the globe" ariaLabel="A globe showing the countries our team works from"
        handlers={{ onSelect: (id) => setLoc(id) }}
        onApi={(a) => { api.current = a; }} />
    </Panel3D>
  );
}

// ─── Contact: the secure channel ────────────────────────────────────────────────────────────────────────────────
function ContactStage({ apiRef }) {
  return (
    <Panel3D className="q3-dark" bar={<p className="q3-status">A ball of blocks to play with. Send your message and watch it react.</p>}>
      <Stage3D scene="cubes" eager ratio="0.85" hint="Move over the blocks, click to send a wave" ariaLabel="A sphere of blocks you can push around"
        onApi={(a) => { if (apiRef) apiRef.current = a; }} />
    </Panel3D>
  );
}

Object.assign(window, { Stage3D, Panel3D, Chips, SECTOR_LIST, HeroOrb, HarvestSim, SectorStage, ChipLab, KeyFlow, KeyForge, KeyTeaser, GlobeStage, ContactStage, KEY_STEPS, CHIP_SECTOR, SECTOR_LINE });
