// app.jsx — main router for the grippay app mockups
// Wires the screen picker, side annotations, tweaks panel, and the iOS device.

const SCREENS = /*EDITMODE-BEGIN*/{
  "default_screen": "01-onboarding",
  "primary_color": "#3FCA8C",
  "show_annotations": true,
  "device_glow": true,
  "agent_name": "Bia"
}/*EDITMODE-END*/;

const SCREEN_LIST = [
  {
    id:'01-onboarding', num:'01', label:'onboarding · zero-touch',
    component:'ScreenOnboardingChat',
    note:{
      lbl:'killer moment · install',
      ttl:<>"Bia, instalá grip" — <em>30 segundos</em> después, wallet ready.</>,
      body:<>Cero forms. Cero KYC. El agente provisiona la principal, deploya smart wallet en Base, y devuelve <code>0xA4F2…91Bc</code> + 4 funding endpoints. El humano nunca instaló nada.</>,
    },
    notes:[
      { lbl:'why this works', ttl:'message-first onboarding', body:'En lugar de pedirle al humano que descargue una app, abra una wallet, copie una seed phrase. El agente lo hace por él, en el thread donde ya conversa.' },
      { lbl:'four funding paths', ttl:'meet the user where the money is', body:'USDC native (instant, 0 fee) · Pix (LatAm settle 30s) · card (3DS) · bank wire. Decide el agente — usuario solo confirma.' },
    ],
  },
  {
    id:'02-funding', num:'02', label:'funding · QR live',
    component:'ScreenFundingCard',
    note:{
      lbl:'inline experience',
      ttl:<>QR <em>vivo</em> en el chat — sin app, sin redirección.</>,
      body:<>Card nativa de Grip Pay con address copiable, monto, fee, ETA. Estado <code>awaiting → detected → confirmed</code> se actualiza en tiempo real. Termina con link a Basescan.</>,
    },
    notes:[
      { lbl:'state machine', ttl:'three states, zero ambiguity', body:'awaiting (ochre, pulsing) → detected (blue, mempool seen) → confirmed (green, 1 conf). Cada estado emite mensaje contextual del agente.' },
      { lbl:'no chargebacks', ttl:'finality is the feature', body:'USDC en Base settla en 2s con finality. No hay reversibilidad. Para refunds, agente emite tx inversa.' },
    ],
  },
  {
    id:'03-approval', num:'03', label:'approval · face id',
    component:'ScreenApprovalCard',
    note:{
      lbl:'moment of truth',
      ttl:<>el <em>cap exceeded</em> → push → face id loop, in 15s.</>,
      body:<>Apple Pay vibes para agentes. Cuando el agente excede su cap, dispara una card inline con monto, recipient, ref, policy check (✓/✗). Un tap a Face ID y onchain en 2s.</>,
    },
    notes:[
      { lbl:'policy as readable text', ttl:'the rule is the receipt', body:'Cada decisión muestra qué reglas se chequearon — recipient en allowlist, monto vs cap, scope match. El humano ve POR QUÉ se le pide aprobar.' },
      { lbl:'always reversible', ttl:'deny means no', body:'Deny es un sign también. El agente recibe la negación, ajusta plan. No hay state oculto, no hay retry sneaky.' },
    ],
  },
  {
    id:'04-home', num:'04', label:'home dashboard',
    component:'ScreenHome',
    note:{
      lbl:'standalone app',
      ttl:<>el <em>cockpit</em> de tu identidad onchain.</>,
      body:<>Principal pill arriba (lea.base ✓). Balance grande. 4 acciones rápidas. Strip de wads abiertos con barra de uso. Recent activity con agent attribution. Tab bar fija.</>,
    },
    notes:[
      { lbl:'one hand on the money', ttl:'principal is sovereign', body:'El balance es del humano, no del agente. Los wads son delegations limitadas. Revocar un wad no toca los fondos — los fondos nunca salieron del principal.' },
      { lbl:'agent attribution', ttl:'every tx has a face', body:'Bia (violet), Pi (blue), Atlas (ochre). El color se mantiene a través de wads, activity, approvals. Forma instant pattern recognition.' },
    ],
  },
  {
    id:'05-wad-detail', num:'05', label:'wad · policy',
    component:'ScreenWadDetail',
    note:{
      lbl:'policy container',
      ttl:<>la <em>policy</em> es legible — JSON, no callbacks.</>,
      body:<>Scope, caps, allowlist, expiration, rate limit. Todos onchain, todos editables (excepto agent address y principal — locked). Un tap revoke y el agente pierde poder en &lt;1 block.</>,
    },
    notes:[
      { lbl:'declarative > imperative', ttl:'policy is a contract, not a callback', body:'No hay un servidor que decide. La policy vive onchain. La wallet la enforce. Si Grip muere mañana, la policy sigue funcionando.' },
      { lbl:'revoke is final', ttl:'one tap, one block', body:'Revocar es signed por la principal y effective antes del próximo bloque (~2s). Pending tx fail con error explícito.' },
    ],
  },
  {
    id:'06-activity', num:'06', label:'activity · ledger',
    component:'ScreenActivity',
    note:{
      lbl:'every tx, attributed',
      ttl:<>filtros por <em>agente</em>, scope, dirección — todo exportable.</>,
      body:<>Chips de filtro: in/out + per-agent. Stats 30d. Group by day. Export a CSV (spreadsheet), JSON (raw + signatures), o PDF (tax-ready).</>,
    },
    notes:[
      { lbl:'audit by default', ttl:'no need for separate book-keeping', body:'Cada tx tiene agent + scope + tag + onchain ref. La policy decision se firma — el ledger es la prueba.' },
      { lbl:'three export targets', ttl:'taxes, devs, ops', body:'PDF para impuestos. JSON para devs que quieren reproducir decisions. CSV para el contador del studio.' },
    ],
  },
  {
    id:'07-settings', num:'07', label:'settings · recovery',
    component:'ScreenSettings',
    note:{
      lbl:'sovereign defaults',
      ttl:<>identidad, recuperación, time-locks — <em>tuyo</em>, no nuestro.</>,
      body:<>DID copiable. Social recovery con 3 guardians (2-of-3 sigs). Daily cap global cross-wad. Time-lock de 24h para tx grandes. Notificaciones granulares.</>,
    },
    notes:[
      { lbl:'self-custody, made livable', ttl:'two-of-three is the magic', body:'Si perdés el phone, dos de tus tres guardians firman recovery. Ellos nunca ven balances ni mueven plata. Apenas pueden re-asignar la principal a un device nuevo.' },
      { lbl:'time-lock as fuse', ttl:'large = patience', body:'Tx > $2,000 esperan 24h. Suficiente para que un attacker con tu Face ID no vacíe la wallet. Cancellable durante esa ventana.' },
    ],
  },
];

function App() {
  const [tweaks, setTweak] = useTweaks(SCREENS);
  const [active, setActive] = React.useState(tweaks.default_screen || '01-onboarding');

  // resolve screen
  const screen = SCREEN_LIST.find(s => s.id === active) || SCREEN_LIST[0];

  // navigation handler — tabs jump between screens
  const handleNav = (target) => {
    const map = {
      'home':'04-home', 'wads':'05-wad-detail',
      'activity':'06-activity', 'settings':'07-settings',
      'wad-detail':'05-wad-detail',
    };
    if (map[target]) setActive(map[target]);
  };

  // render the active screen component
  const Comp = window[screen.component];
  const screenEl = Comp ? <Comp onNav={handleNav}/> : <div style={{padding:24, color:'#fff'}}>missing: {screen.component}</div>;

  // render picker
  React.useEffect(() => {
    const picker = document.getElementById('picker');
    if (!picker) return;
    picker.innerHTML = '';
    SCREEN_LIST.forEach(s => {
      const btn = document.createElement('button');
      btn.className = 'pick' + (s.id === active ? ' active' : '');
      btn.innerHTML = `<span class="num">${s.num}</span> ${s.label}`;
      btn.onclick = () => setActive(s.id);
      picker.appendChild(btn);
    });
  }, [active]);

  // render annotations
  React.useEffect(() => {
    if (!tweaks.show_annotations) {
      document.getElementById('anno-left').innerHTML = '';
      document.getElementById('anno-right').innerHTML = '';
      return;
    }
    const left = document.getElementById('anno-left');
    const right = document.getElementById('anno-right');
    if (left) {
      left.innerHTML = '';
      // primary annotation goes left
      if (screen.note) {
        const div = document.createElement('div');
        div.className = 'anno';
        const root = ReactDOM.createRoot(div);
        root.render(<AnnotationCard note={screen.note}/>);
        left.appendChild(div);
      }
    }
    if (right) {
      right.innerHTML = '';
      (screen.notes || []).forEach(note => {
        const div = document.createElement('div');
        div.className = 'anno';
        const root = ReactDOM.createRoot(div);
        root.render(<AnnotationCard note={note}/>);
        right.appendChild(div);
      });
    }
  }, [active, tweaks.show_annotations]);

  // device shadow tweak
  React.useEffect(() => {
    const dev = document.querySelector('.device');
    if (!dev) return;
    if (tweaks.device_glow) {
      dev.style.boxShadow = `0 0 0 11px #1a1a1a, 0 0 0 12px #2a2a2a, 0 60px 120px -30px ${tweaks.primary_color}40, 0 30px 60px -20px rgba(0,0,0,0.6), inset 0 0 0 1px rgba(255,255,255,0.05)`;
    } else {
      dev.style.boxShadow = '0 0 0 11px #1a1a1a, 0 0 0 12px #2a2a2a, 0 60px 120px -30px rgba(0,0,0,0.8), 0 30px 60px -20px rgba(0,0,0,0.6), inset 0 0 0 1px rgba(255,255,255,0.05)';
    }
  }, [tweaks.device_glow, tweaks.primary_color, active]);

  // primary color tweak — update CSS var
  React.useEffect(() => {
    document.documentElement.style.setProperty('--green', tweaks.primary_color);
    // recompute related vars
    const c = tweaks.primary_color;
    const rgb = hexToRgb(c);
    if (rgb) {
      document.documentElement.style.setProperty('--green-soft', `rgba(${rgb.r},${rgb.g},${rgb.b},0.14)`);
      document.documentElement.style.setProperty('--green-line', `rgba(${rgb.r},${rgb.g},${rgb.b},0.36)`);
      document.documentElement.style.setProperty('--green-glow', `rgba(${rgb.r},${rgb.g},${rgb.b},0.06)`);
    }
  }, [tweaks.primary_color]);

  return (
    <>
      <div data-screen-label={`${screen.num} ${screen.label}`} style={{position:'absolute',inset:0}}>
        {screenEl}
      </div>
      <TweaksPanelMount tweaks={tweaks} setTweak={setTweak}/>
    </>
  );
}

function AnnotationCard({ note }) {
  return (
    <>
      <div className="lbl">{note.lbl}</div>
      <div className="ttl">{note.ttl}</div>
      <div className="body">{note.body}</div>
    </>
  );
}

function TweaksPanelMount({ tweaks, setTweak }) {
  return (
    <TweaksPanel title="Tweaks">
      <TweakSection label="Screen">
        <TweakSelect
          label="default screen"
          value={tweaks.default_screen}
          onChange={v => setTweak('default_screen', v)}
          options={SCREEN_LIST.map(s => ({ value:s.id, label:`${s.num} · ${s.label}` }))}
        />
      </TweakSection>
      <TweakSection label="Brand">
        <TweakColor
          label="primary color"
          value={tweaks.primary_color}
          onChange={v => setTweak('primary_color', v)}
        />
        <TweakSelect
          label="primary agent"
          value={tweaks.agent_name}
          onChange={v => setTweak('agent_name', v)}
          options={[
            { value:'Bia', label:'Bia · violet' },
            { value:'Pi', label:'Pi · blue' },
            { value:'Atlas', label:'Atlas · ochre' },
          ]}
        />
      </TweakSection>
      <TweakSection label="Display">
        <TweakToggle
          label="annotations"
          value={tweaks.show_annotations}
          onChange={v => setTweak('show_annotations', v)}
        />
        <TweakToggle
          label="device glow"
          value={tweaks.device_glow}
          onChange={v => setTweak('device_glow', v)}
        />
      </TweakSection>
    </TweaksPanel>
  );
}

function hexToRgb(hex) {
  const m = hex.replace('#','').match(/^([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i);
  if (!m) return null;
  return { r: parseInt(m[1],16), g: parseInt(m[2],16), b: parseInt(m[3],16) };
}

// boot
const mount = document.getElementById('screen');
const reactRoot = ReactDOM.createRoot(mount);
reactRoot.render(<App/>);
