// A small working web app inside a Mac frame. Used on Solutions to show the software side, one sample per sector.
// The crypto here is the browser's AES-256-GCM, labelled as a stand in for QDI's Kyber1024 and stacked ciphers.

const RM = React;

const MAC_SAMPLES = {
  infra: { to: 'Control room', app: 'Grid link', text: 'Substation 14 telemetry: breaker closed, load steady at 62 percent. No action needed.' },
  health: { to: 'Referral desk', app: 'Care link', text: 'Referral for patient 0042: cardiology outpatient review within two weeks. Results attached.' },
  finance: { to: 'Treasury operations', app: 'Settlement link', text: 'Settlement batch 7731 approved by two signatories. Release at 16:00.' },
  defence: { to: 'Command post', app: 'Field link', text: 'Position report 0600: route clear, supplies adequate. Requesting a resupply window.' },
  gov: { to: 'Policy office', app: 'Briefing link', text: 'Draft briefing, OFFICIAL SENSITIVE: summary of the departmental review for ministers.' },
  intel: { to: 'Analysis cell', app: 'Source link', text: 'Source report filed. Retention requirement: fifty years. Distribution: named readers only.' },
};

const hex = (u8) => Array.from(u8).map((b) => b.toString(16).padStart(2, '0')).join('');

// Real AES-GCM when the browser allows it (secure pages). Otherwise a clearly simulated cipher so the demo still works.
const macCrypto = (() => {
  const subtle = window.crypto && window.crypto.subtle;
  let keyP = null;
  const getKey = () => keyP || (keyP = subtle.generateKey({ name: 'AES-GCM', length: 256 }, false, ['encrypt', 'decrypt']));
  const fakeKey = Array.from({ length: 32 }, () => Math.floor(Math.random() * 256));
  const stream = (n, iv) => { let s = 2166136261; fakeKey.concat(Array.from(iv)).forEach((b) => { s = Math.imul(s ^ b, 16777619) >>> 0; }); const o = new Uint8Array(n); for (let i = 0; i < n; i++) { s ^= s << 13; s >>>= 0; s ^= s >>> 17; s ^= s << 5; s >>>= 0; o[i] = s & 255; } return o; };
  const tag = (u8) => { let h = 5381; for (let i = 0; i < u8.length; i++) h = (Math.imul(h, 33) ^ u8[i]) >>> 0; return h; };
  return {
    real: !!subtle,
    async encrypt(text) {
      const iv = window.crypto.getRandomValues(new Uint8Array(12));
      const pt = new TextEncoder().encode(text);
      if (subtle) { const ct = new Uint8Array(await subtle.encrypt({ name: 'AES-GCM', iv }, await getKey(), pt)); return { iv, ct }; }
      const ks = stream(pt.length, iv), body = pt.map((b, i) => b ^ ks[i]), t = tag(body);
      const ct = new Uint8Array(body.length + 4); ct.set(body); ct.set([t >>> 24, (t >>> 16) & 255, (t >>> 8) & 255, t & 255], body.length);
      return { iv, ct };
    },
    async decrypt(iv, ct) {
      if (subtle) return new TextDecoder().decode(await subtle.decrypt({ name: 'AES-GCM', iv }, await getKey(), ct));
      const body = ct.slice(0, ct.length - 4), t = tag(body), want = ((ct[ct.length - 4] << 24) | (ct[ct.length - 3] << 16) | (ct[ct.length - 2] << 8) | ct[ct.length - 1]) >>> 0;
      if (t !== want) throw new Error('tag');
      const ks = stream(body.length, iv); return new TextDecoder().decode(body.map((b, i) => b ^ ks[i]));
    },
  };
})();

const CIPHERS = ['Serpent', 'Twofish', 'ChaCha20', 'ChaChaPoly1305'];
const fp = () => Array.from(crypto.getRandomValues(new Uint8Array(8))).map((b) => b.toString(16).padStart(2, '0')).join(':').toUpperCase();
const clock = () => new Date().toTimeString().slice(0, 8);

function MacDemo({ sector }) {
  const sample = MAC_SAMPLES[sector] || MAC_SAMPLES.infra;
  const [tab, setTab] = RM.useState('msg');
  const [text, setText] = RM.useState(sample.text);
  const [sent, setSent] = RM.useState(null);      // { iv, ct, tampered }
  const [out, setOut] = RM.useState(null);        // { ok, text }
  const [busy, setBusy] = RM.useState(false);
  const [log, setLog] = RM.useState([{ t: clock(), k: 'info', m: 'Session started. Key loaded from the QDI key.' }]);
  const [fprint, setFprint] = RM.useState(() => fp());
  const [rot, setRot] = RM.useState(0);
  const [on, setOn] = RM.useState([true, true, true, false]);
  const add = (k, m) => setLog((l) => [{ t: clock(), k, m }, ...l].slice(0, 40));
  RM.useEffect(() => { setText(sample.text); setSent(null); setOut(null); add('info', 'Switched to ' + sample.app + '.'); }, [sector]);

  const stepNow = out ? 4 : sent ? 3 : 1;
  const steps = ['Write', 'Encrypt', 'Intercept', 'Decrypt'];
  const done = sent ? (out ? 4 : 3) : 0;
  const stack = CIPHERS.filter((c, i) => on[i]);

  const send = async () => {
    if (busy || !text.trim()) return;
    setBusy(true); setOut(null);
    try { const r = await macCrypto.encrypt(text); setSent({ iv: r.iv, ct: r.ct, tampered: false }); add('ok', 'Encrypted ' + text.length + ' characters for ' + sample.to + ' (' + (stack.length) + ' cipher layer' + (stack.length === 1 ? '' : 's') + ').'); } catch (e) { setSent(null); }
    setBusy(false);
  };
  const tamper = () => {
    if (!sent || sent.tampered) return;
    const ct = sent.ct.slice(); ct[0] ^= 1; setSent({ iv: sent.iv, ct, tampered: true }); setOut(null); add('warn', 'One bit changed in transit.');
  };
  const open = async () => {
    if (!sent || busy) return;
    setBusy(true);
    try { const t = await macCrypto.decrypt(sent.iv, sent.ct); setOut({ ok: true, text: t }); add('ok', 'Opened at ' + sample.to + '. Integrity check passed.'); }
    catch (e) { setOut({ ok: false }); add('bad', 'Rejected at ' + sample.to + '. Message was altered.'); }
    setBusy(false);
  };
  const reset = () => { setText(sample.text); setSent(null); setOut(null); };
  const rotate = () => { setFprint(fp()); setRot((n) => n + 1); setSent(null); setOut(null); add('info', 'New session key exchanged (Kyber1024).'); };
  const toggle = (i) => setOn((a) => { const n = a.slice(); if (n[i] && n.filter(Boolean).length === 1) return a; n[i] = !n[i]; return n; });

  const TABS = [
    { id: 'msg', label: 'Messages', ic: 'M4 5h16v11H8l-4 4V5z' },
    { id: 'keys', label: 'Keys', ic: 'M14 10a4 4 0 11-3.9-4A4 4 0 0114 10zm-3.5 3.5L4 20h3v-2h2v-2h2.2' },
    { id: 'log', label: 'Audit log', ic: 'M6 4h12v16H6zM9 9h6M9 13h6M9 17h4' },
  ];

  return (
    <div className="mac-wrap">
      <div className="mac">
        <div className="mac-lid">
          <i className="mac-cam" />
          <div className="mac-screen">
            <div className="app">
              <div className="app-top">
                <span className="app-dots"><i /><i /><i /></span>
                <span className="app-title">QDI {sample.app}</span>
                <span className="app-tag">Sample data</span>
              </div>
              <div className="app-body">
                <aside className="app-side">
                  <div className="app-brand"><span className="app-logo">Q</span><b>QDI Secure</b></div>
                  <nav className="app-nav" aria-label="App sections">
                    {TABS.map((t) => (
                      <button key={t.id} className={tab === t.id ? 'on' : ''} onClick={() => setTab(t.id)}>
                        <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true"><path d={t.ic} /></svg>
                        <span>{t.label}</span>
                        {t.id === 'log' && <em>{log.length}</em>}
                      </button>
                    ))}
                  </nav>
                  <div className="app-user"><span className="app-av">OP</span><span><b>Operations desk</b><i>Key active</i></span></div>
                </aside>

                <div className="app-main">
                  {tab === 'msg' && (
                    <>
                      <div className="app-head">
                        <h5>New message</h5>
                        <ol className="app-steps" aria-label="Progress">
                          {steps.map((s, i) => <li key={s} className={i < done ? 'done' : i === stepNow - 1 ? 'on' : ''}><i>{i + 1}</i>{s}</li>)}
                        </ol>
                      </div>
                      <div className="app-grid">
                        <section className="app-card">
                          <h4>Your message</h4>
                          <div className="app-to"><span>To</span><b>{sample.to}</b><em>{stack.length} layer{stack.length === 1 ? '' : 's'}</em></div>
                          <textarea className="app-ta" value={text} onChange={(e) => { setText(e.target.value); if (sent) { setSent(null); setOut(null); } }} aria-label="Message to send" maxLength={240} />
                          <div className="app-row">
                            <button className="app-btn solid" onClick={send} disabled={busy || !text.trim()}>Encrypt and send</button>
                            <button className="app-btn" onClick={reset}>Reset</button>
                          </div>
                        </section>
                        <section className="app-card">
                          <h4>What anyone in between sees</h4>
                          <div className={'app-cipher' + (sent ? '' : ' empty') + (sent && sent.tampered ? ' warn' : '')} aria-live="polite">
                            {sent ? hex(sent.ct) : 'Nothing sent yet.'}
                          </div>
                          <div className="app-row">
                            <button className="app-btn" onClick={tamper} disabled={!sent || sent.tampered}>Change one bit</button>
                            <button className="app-btn solid" onClick={open} disabled={!sent || busy}>Decrypt at {sample.to}</button>
                          </div>
                          <div className={'app-out' + (out ? (out.ok ? ' ok' : ' bad') : '')} aria-live="polite">
                            {!out && <span>The result appears here.</span>}
                            {out && out.ok && <><b>Opened</b><span>{out.text}</span></>}
                            {out && !out.ok && <><b>Rejected</b><span>The message was altered on the way, so it was refused.</span></>}
                          </div>
                        </section>
                      </div>
                    </>
                  )}
                  {tab === 'keys' && (
                    <>
                      <div className="app-head"><h5>Keys</h5><span className="app-pill">Kyber1024</span></div>
                      <div className="app-grid">
                        <section className="app-card">
                          <h4>Session key</h4>
                          <div className="app-fp" key={rot}>{fprint}</div>
                          <p className="app-note">Fingerprint of the key shared with {sample.to}. Rotating it runs a fresh key exchange.</p>
                          <div className="app-row"><button className="app-btn solid" onClick={rotate}>Rotate key</button></div>
                        </section>
                        <section className="app-card">
                          <h4>Cipher stack</h4>
                          <div className="app-ciphers">
                            {CIPHERS.map((c, i) => (
                              <button key={c} className={'app-cp' + (on[i] ? ' on' : '')} onClick={() => toggle(i)} aria-pressed={on[i]}>
                                <span className="app-sw"><i /></span>{c}
                              </button>
                            ))}
                          </div>
                          <p className="app-note">{stack.length} of 4 layers active. An attacker has to break every one.</p>
                        </section>
                      </div>
                    </>
                  )}
                  {tab === 'log' && (
                    <>
                      <div className="app-head"><h5>Audit log</h5><button className="app-btn" onClick={() => setLog([{ t: clock(), k: 'info', m: 'Log cleared.' }])}>Clear</button></div>
                      <ul className="app-log">
                        {log.map((l, i) => <li key={l.t + i + l.m} className={l.k}><time>{l.t}</time><i /><span>{l.m}</span></li>)}
                      </ul>
                    </>
                  )}
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
      <p className="mac-note">
        Working demo. {macCrypto.real ? 'Your browser’s AES 256 encryption' : 'A simulated cipher'} stands in for QDI&#39;s Kyber1024 key exchange and stacked ciphers, so you can see the idea: what travels is unreadable and tampering is caught.
      </p>
    </div>
  );
}

Object.assign(window, { MacDemo, MAC_SAMPLES });
