/* Voice / AI Log — conversational entry for all Roundhouse actions.
   Multi-turn with Claude: log a session, add ammo, or set up gear.
   Claude asks follow-up questions until it has everything, then shows
   a typed confirm card → saves to IndexedDB. */

// ── System prompt (gun context injected at call time) ─────────────────
function buildSystemPrompt(guns) {
  var today = new Date().toISOString().slice(0, 10);
  var gunList = guns.length > 0
    ? guns.map(function(g, i) {
        var name = g.name || [g.manufacturer, g.model].filter(Boolean).join(' ') || 'Firearm';
        return (i + 1) + '. ' + name + ' (' + (g.caliber || 'caliber unknown') + ', id:' + g.id + ')';
      }).join('\n')
    : '(no guns in account yet)';

  return 'You are a firearm tracking assistant for Roundhouse, a gun tracking app.\n' +
'You help users do three things via natural language:\n' +
'  1. LOG A SESSION — "I shot 200 rounds of Blazer Brass today, had 1 FTF, all suppressed"\n' +
'  2. ADD AMMO — "I bought 200 rounds of Blazer 9mm for $200, training use"\n' +
'  3. ADD GEAR — "I have a Glock 19 with a Holosun 507C and an Apex trigger"\n\n' +
'For EVERY response return ONLY a valid JSON object — no markdown, no prose outside it:\n' +
'{\n' +
'  "intent": "log_session" | "add_ammo" | "add_gear" | "unclear",\n' +
'  "message": "Brief friendly response (1-2 sentences)",\n' +
'  "questions": ["question 1"],\n' +
'  "options": [["choice A", "choice B"]],\n' +
'  "ready": false,\n' +
'  "data": null\n' +
'}\n\n' +
'"questions" = follow-up questions when critical info is missing (max 2 at a time).\n' +
'"options" = parallel array of answer chips for each question (null for free-text).\n' +
'Both are empty arrays when ready = true.\n\n' +
'When ready = true, include complete typed data:\n\n' +
'log_session:\n' +
'{ "date":"YYYY-MM-DD", "guns":[{"gunId":null_or_number,"gunName":"","rounds":0,"suppressed":false,"ammo":""}], "malfunctions":[{"type":"ftf|fte|other","description":"","count":1}], "location":"", "notes":"", "totalRounds":0 }\n\n' +
'add_ammo:\n' +
'{ "manufacturer":"", "caliber":"", "grain":0, "use":"training|defensive|match", "quantity":0, "pricePaid":0, "notes":"" }\n\n' +
'add_gear:\n' +
'{ "guns":[{"manufacturer":"","model":"","caliber":"","category":"pistol|ar|pcc|shotgun|bolt|revolver","serial":null}], "accessories":[{"brand":"","model":"","category":"OPTIC|LIGHT|MAGAZINE|HOLSTER|OTHER"}], "modifications":[{"part":"","detail":"","gunModel":""}] }\n\n' +
'Rules:\n' +
'- Never ask for serial numbers\n' +
'- Infer caliber: G17/G19/G26/G34→9mm, G20→10mm, G21→.45 ACP, G22/G23→.40 S&W, P365/P320/P226→9mm, AR-15/M4→5.56, AK-47→7.62x39, 10/22→.22 LR, 1911→.45 ACP\n' +
'- Infer grain: Blazer Brass 9mm→115gr, .45 ACP→230gr, 5.56→55gr, .308→168gr\n' +
'- "FTF"/"failure to fire"→ftf, "FTE"/"failure to eject"→fte, "stovepipe"→fte\n' +
'- "suppressed"/"with a can"/"with my suppressor"→suppressed:true\n' +
'- Date defaults to today (' + today + ') when not stated\n' +
'- For log_session: match gunId + gunName to the user\'s existing guns when possible\n' +
'- Be brief and practical\n\n' +
"USER'S EXISTING GUNS:\n" + gunList;
}

// ── Main component ────────────────────────────────────────────────────
function VoiceSetup({ go }) {
  var [phase, setPhase]           = React.useState('idle');   // idle|thinking|confirm|done
  var [messages, setMessages]     = React.useState([]);       // display [{role,text,options}]
  var [apiHistory, setApiHistory] = React.useState([]);       // claude API [{role,content}]
  var [inputText, setInputText]   = React.useState('');
  var [isListening, setIsListening] = React.useState(false);
  var [userGuns, setUserGuns]     = React.useState([]);
  var [intent, setIntent]         = React.useState(null);
  var [pendingData, setPendingData] = React.useState(null);
  var [included, setIncluded]     = React.useState({});
  var [error, setError]           = React.useState(null);
  var [showKeyEntry, setShowKeyEntry] = React.useState(!localStorage.getItem('rh-api-key'));
  var [keyDraft, setKeyDraft]     = React.useState('');
  var [saving, setSaving]         = React.useState(false);
  var recogRef  = React.useRef(null);
  var bottomRef = React.useRef(null);

  var hasSpeech = !!(window.SpeechRecognition || window.webkitSpeechRecognition);
  var inConvo   = messages.length > 0;

  React.useEffect(function() {
    window.rhDB.getAllGuns().then(function(gs) { setUserGuns(gs || []); });
  }, []);

  React.useEffect(function() {
    if (bottomRef.current) bottomRef.current.scrollIntoView({ behavior: 'smooth' });
  }, [messages, phase]);

  // ── Send a message + get Claude's reply ──────────────────────────
  async function sendMessage(text) {
    if (!text || !text.trim()) return;
    var key = localStorage.getItem('rh-api-key');
    if (!key) { setShowKeyEntry(true); return; }

    var userApiMsg = { role: 'user', content: text.trim() };
    var newHistory = apiHistory.concat([userApiMsg]);

    setMessages(function(prev) { return prev.concat([{ role: 'user', text: text.trim() }]); });
    setApiHistory(newHistory);
    setInputText('');
    setPhase('thinking');
    setError(null);

    try {
      var res = await fetch('https://api.anthropic.com/v1/messages', {
        method: 'POST',
        headers: {
          'x-api-key': key,
          'anthropic-version': '2023-06-01',
          'anthropic-dangerous-allow-browser': 'true',
          'content-type': 'application/json',
        },
        body: JSON.stringify({
          model: 'claude-3-5-haiku-20241022',
          max_tokens: 1024,
          system: buildSystemPrompt(userGuns),
          messages: newHistory,
        }),
      });

      if (!res.ok) {
        var eb = await res.json().catch(function() { return {}; });
        throw new Error((eb.error && eb.error.message) || 'API error ' + res.status);
      }

      var resData = await res.json();
      var raw = resData.content && resData.content[0] && resData.content[0].text;
      if (!raw) throw new Error('Empty response from Claude');
      raw = raw.replace(/^```[a-z]*\n?/i, '').replace(/\n?```$/i, '').trim();
      var parsed = JSON.parse(raw);

      // Keep full raw JSON in API history so Claude sees its own prior responses
      setApiHistory(function(prev) {
        return prev.concat([{ role: 'assistant', content: raw }]);
      });

      // Show the friendly message text + any option chips
      setMessages(function(prev) {
        return prev.concat([{
          role: 'assistant',
          text: parsed.message || 'Got it.',
          options: (parsed.options && parsed.options[0]) || [],
        }]);
      });

      if (parsed.ready && parsed.data) {
        setIntent(parsed.intent);
        setPendingData(parsed.data);
        if (parsed.intent === 'add_gear') {
          var inc = {};
          (parsed.data.guns || []).forEach(function(g, i) { inc['gun-' + i] = true; });
          setIncluded(inc);
        }
        setPhase('confirm');
      } else {
        setPhase('chat');
      }
    } catch(e) {
      setError('Something went wrong: ' + e.message);
      setPhase(inConvo ? 'chat' : 'idle');
    }
  }

  // ── Speech recognition ────────────────────────────────────────────
  function startListening() {
    var SR = window.SpeechRecognition || window.webkitSpeechRecognition;
    if (!SR) return;
    var r = new SR();
    r.continuous = false;
    r.interimResults = true;
    r.lang = 'en-US';
    r.onresult = function(e) {
      var t = Array.from(e.results).map(function(x) { return x[0].transcript; }).join('');
      setInputText(t);
      if (e.results[e.results.length - 1].isFinal) {
        r.stop();
        sendMessage(t);
      }
    };
    r.onerror = function(e) {
      setError('Mic error: ' + e.error + '. Try typing instead.');
      setIsListening(false);
    };
    r.onend = function() { setIsListening(false); };
    r.start();
    recogRef.current = r;
    setIsListening(true);
    setError(null);
  }

  function stopListening() {
    if (recogRef.current) recogRef.current.stop();
    setIsListening(false);
    if (inputText.trim()) sendMessage(inputText);
  }

  // ── API key ───────────────────────────────────────────────────────
  function saveKey() {
    var k = keyDraft.trim();
    if (!k) return;
    localStorage.setItem('rh-api-key', k);
    setShowKeyEntry(false);
  }

  // ── Save to DB ────────────────────────────────────────────────────
  async function handleConfirm() {
    if (saving) return;
    setSaving(true);
    setError(null);
    try {
      if (intent === 'add_gear') {
        var guns = (pendingData.guns || []).filter(function(g, i) { return included['gun-' + i]; });
        for (var i = 0; i < guns.length; i++) {
          await window.rhDB.saveGun({
            manufacturer:   guns[i].manufacturer || '',
            model:          guns[i].model        || '',
            caliber:        guns[i].caliber      || '',
            category:       guns[i].category     || 'pistol',
            serial:         guns[i].serial       || '',
            startingRounds: 0,
            createdAt:      new Date().toISOString(),
          });
        }
        if (window.__rhCache) window.__rhCache.guns = null;

      } else if (intent === 'log_session') {
        var d = pendingData;
        var total = (d.guns || []).reduce(function(s, g) { return s + (g.rounds || 0); }, 0);
        await window.rhDB.saveSession({
          date:         d.date || new Date().toISOString().slice(0, 10),
          guns:         d.guns || [],
          malfunctions: d.malfunctions || [],
          location:     d.location || '',
          notes:        d.notes || '',
          totalRounds:  total,
          createdAt:    new Date().toISOString(),
        });
        if (window.__rhCache) window.__rhCache.sessions = null;

      } else if (intent === 'add_ammo') {
        var d = pendingData;
        await window.rhDB.saveLot({
          manufacturer: d.manufacturer || '',
          caliber:      d.caliber      || '',
          grain:        d.grain        || 0,
          use:          d.use          || 'training',
          remaining:    d.quantity     || 0,
          purchasedQty: d.quantity     || 0,
          pricePaid:    d.pricePaid    || 0,
          purchasedAt:  new Date().toISOString().slice(0, 10),
          notes:        d.notes        || '',
          createdAt:    new Date().toISOString(),
        });
        if (window.__rhCache) window.__rhCache.lots = null;
      }

      setPhase('done');
    } catch(e) {
      setError('Save failed: ' + e.message);
    }
    setSaving(false);
  }

  // ── DONE ─────────────────────────────────────────────────────────
  if (phase === 'done') {
    var doneLabel = intent === 'add_gear'    ? 'Gear added to Roundhouse.'
                  : intent === 'log_session' ? 'Session logged.'
                  :                            'Ammo added to Supply.';
    var doneRoute = intent === 'add_gear'    ? 'gear'
                  : intent === 'log_session' ? 'home'
                  :                            'supply';
    setTimeout(function() { go(doneRoute); }, 1800);
    return (
      <>
        <TopBar title="Saved"/>
        <div className="screen" style={{display:'flex', flexDirection:'column', alignItems:'center', justifyContent:'center', minHeight:'80%', textAlign:'center'}}>
          <div style={{width:80, height:80, borderRadius:'50%', border:'2px solid #A6C293', display:'flex', alignItems:'center', justifyContent:'center', marginBottom:20}}>
            <svg width="36" height="36" viewBox="0 0 24 24" fill="none" stroke="#A6C293" strokeWidth="2" strokeLinecap="round">
              <path d="M20 6L9 17l-5-5"/>
            </svg>
          </div>
          <div style={{fontFamily:'var(--font-display)', fontSize:32, color:'var(--fg-1)', letterSpacing:'0.02em', lineHeight:1}}>Saved.</div>
          <div className="rh-body-sm" style={{marginTop:10, color:'var(--rh-bone-500)'}}>{doneLabel}</div>
          <div className="label-md" style={{marginTop:6, color:'var(--rh-bone-700)'}}>Taking you there…</div>
        </div>
      </>
    );
  }

  // ── CONFIRM ───────────────────────────────────────────────────────
  if (phase === 'confirm' && pendingData) {
    return (
      <>
        <TopBar title="Confirm"/>
        <div className="screen">
          <BackRow to="back" label="Back" go={function() { setPhase('chat'); setPendingData(null); }}/>
          {intent === 'add_gear'    && <ConfirmGear    data={pendingData} included={included} setIncluded={setIncluded} onSave={handleConfirm} saving={saving}/>}
          {intent === 'log_session' && <ConfirmSession data={pendingData} onSave={handleConfirm} saving={saving}/>}
          {intent === 'add_ammo'   && <ConfirmAmmo    data={pendingData} onSave={handleConfirm} saving={saving}/>}
          {error && <VoiceErrorCard msg={error}/>}
        </div>
      </>
    );
  }

  // ── CHAT (idle + active conversation) ────────────────────────────
  var isThinking = phase === 'thinking';

  return (
    <>
      <TopBar title="Voice Log"/>
      <div className="screen" style={{display:'flex', flexDirection:'column', paddingBottom:4, minHeight:'92%'}}>
        <BackRow to="home" label="Back" go={go}/>

        {/* API key prompt */}
        {showKeyEntry && (
          <div className="card" style={{borderLeft:'3px solid var(--rh-brass)', marginBottom:16}}>
            <div className="label-md" style={{color:'var(--rh-brass-400)'}}>Anthropic API key needed</div>
            <div className="rh-body-sm" style={{marginTop:4, color:'var(--rh-bone-300)', marginBottom:12}}>
              Voice log uses Claude AI. Get a key at console.anthropic.com — stored on this device only, never shared.
            </div>
            <input
              type="password"
              value={keyDraft}
              onChange={function(e) { setKeyDraft(e.target.value); }}
              onKeyDown={function(e) { if (e.key === 'Enter') saveKey(); }}
              placeholder="sk-ant-…"
              style={{width:'100%', boxSizing:'border-box', background:'var(--bg-elevated-2)', border:'1px solid var(--border-default)', color:'var(--fg-1)', padding:'10px 12px', borderRadius:2, fontFamily:'var(--font-mono)', fontSize:12, marginBottom:8}}
            />
            <Button variant="primary" block onClick={saveKey}>Save key</Button>
          </div>
        )}

        {/* Welcome + example prompts (first load only) */}
        {!showKeyEntry && !inConvo && (
          <>
            <div className="screen-hero" style={{paddingTop:0, textAlign:'center'}}>
              <div className="eyebrow">Voice · AI log</div>
              <h1 className="hero-title" style={{fontSize:28}}>What happened <span className="red">today?</span></h1>
              <div className="hero-sub" style={{marginTop:4}}>
                Speak or type — Claude will ask follow-up questions before saving anything.
              </div>
            </div>
            <div style={{display:'flex', flexDirection:'column', gap:8, marginBottom:16}}>
              {[
                { label:'Log a session',  ex:'I shot 200 rounds of Blazer Brass today, had 1 FTF, all suppressed' },
                { label:'Add ammo',       ex:'I bought 200 rounds of Blazer 9mm for $200, training use' },
                { label:'Add gear',       ex:'I have a Glock 19 with a Holosun 507C and Apex trigger' },
              ].map(function(p) {
                return (
                  <div key={p.label} onClick={function() { setInputText(p.ex); }}
                    style={{padding:'10px 14px', borderRadius:2, background:'var(--bg-elevated-1)', border:'1px solid var(--border-default)', cursor:'pointer'}}>
                    <div className="label-md" style={{color:'var(--rh-brass-400)', marginBottom:3}}>{p.label}</div>
                    <div className="rh-body-sm" style={{color:'var(--rh-bone-500)', fontStyle:'italic', fontSize:12}}>"{p.ex}"</div>
                  </div>
                );
              })}
            </div>
          </>
        )}

        {/* Conversation messages */}
        {inConvo && (
          <div style={{flex:1, marginBottom:12}}>
            {messages.map(function(m, i) {
              var isUser = m.role === 'user';
              return (
                <div key={i} style={{marginBottom:12, display:'flex', flexDirection:'column', alignItems: isUser ? 'flex-end' : 'flex-start'}}>
                  <div style={{
                    maxWidth:'88%', padding:'10px 14px',
                    background: isUser ? 'rgba(184,154,86,0.10)' : 'var(--bg-elevated-1)',
                    border:'1px solid ' + (isUser ? 'var(--rh-brass-tint-32)' : 'var(--border-default)'),
                    borderRadius: isUser ? '10px 10px 2px 10px' : '2px 10px 10px 10px',
                    fontFamily:'var(--font-body)', fontSize:14, lineHeight:1.6,
                    color: isUser ? 'var(--rh-brass-400)' : 'var(--fg-1)',
                  }}>
                    {m.text}
                  </div>
                  {/* Quick-reply chips from Claude's options */}
                  {!isUser && m.options && m.options.length > 0 && (
                    <div style={{marginTop:8, display:'flex', flexWrap:'wrap', gap:6, maxWidth:'88%'}}>
                      {m.options.map(function(opt) {
                        return (
                          <button key={opt} onClick={function() { sendMessage(opt); }}
                            style={{padding:'6px 14px', borderRadius:99, background:'var(--bg-elevated-2)', border:'1px solid var(--rh-brass-tint-32)', color:'var(--rh-brass-400)', fontFamily:'var(--font-ui)', fontSize:11, letterSpacing:'0.08em', fontWeight:600, cursor:'pointer'}}>
                            {opt}
                          </button>
                        );
                      })}
                    </div>
                  )}
                </div>
              );
            })}

            {/* Typing indicator */}
            {isThinking && (
              <div style={{display:'flex', alignItems:'center', gap:5, padding:'10px 14px', background:'var(--bg-elevated-1)', border:'1px solid var(--border-default)', borderRadius:'2px 10px 10px 10px', width:'fit-content'}}>
                {[0, 1, 2].map(function(i) {
                  return <div key={i} style={{width:6, height:6, borderRadius:'50%', background:'var(--rh-bone-500)', animation:'rh-dot-bounce 1.2s ease-in-out infinite', animationDelay:(i*0.18)+'s'}}/>;
                })}
              </div>
            )}
            <div ref={bottomRef}/>
          </div>
        )}

        {error && !saving && <VoiceErrorCard msg={error}/>}

        {/* Input row */}
        {!showKeyEntry && (
          <div style={{marginTop:'auto', borderTop: inConvo ? '1px solid var(--border-hairline)' : 'none', paddingTop: inConvo ? 12 : 0}}>
            <div style={{display:'flex', gap:8, alignItems:'flex-end'}}>
              {hasSpeech && (
                <button onClick={isListening ? stopListening : startListening}
                  style={{
                    width:44, height:44, borderRadius:'50%', flexShrink:0,
                    background: isListening ? 'rgba(228,138,130,0.10)' : 'var(--bg-elevated-2)',
                    border:'1px solid ' + (isListening ? 'var(--rh-dusty-red)' : 'var(--border-default)'),
                    display:'flex', alignItems:'center', justifyContent:'center', cursor:'pointer',
                    animation: isListening ? 'rh-pulse 1.4s ease-in-out infinite' : 'none',
                  }}>
                  {isListening
                    ? <div style={{width:14, height:14, background:'var(--rh-dusty-red)', borderRadius:'50%'}}/>
                    : <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="var(--rh-brass-400)" strokeWidth="1.5" strokeLinecap="round"><path d="M12 2a3 3 0 0 1 3 3v7a3 3 0 0 1-6 0V5a3 3 0 0 1 3-3z"/><path d="M19 10v2a7 7 0 0 1-14 0v-2"/><path d="M12 19v3M8 22h8"/></svg>
                  }
                </button>
              )}
              <textarea
                value={inputText}
                onChange={function(e) { setInputText(e.target.value); }}
                onKeyDown={function(e) { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendMessage(inputText); } }}
                placeholder={inConvo ? 'Reply…' : 'I shot 200 rounds of Blazer Brass today…'}
                rows={2}
                disabled={isThinking}
                style={{
                  flex:1, boxSizing:'border-box',
                  background:'var(--bg-elevated-1)', border:'1px solid var(--border-default)',
                  color:'var(--fg-1)', padding:'10px 12px',
                  borderRadius:2, fontFamily:'var(--font-body)',
                  fontSize:14, lineHeight:1.5, resize:'none', outline:'none',
                  opacity: isThinking ? 0.5 : 1,
                }}
              />
              <button
                onClick={function() { sendMessage(inputText); }}
                disabled={!inputText.trim() || isThinking}
                style={{
                  width:44, height:44, borderRadius:2, flexShrink:0,
                  background: inputText.trim() && !isThinking ? 'var(--rh-brass)' : 'var(--bg-elevated-2)',
                  border:'1px solid ' + (inputText.trim() && !isThinking ? 'var(--rh-brass)' : 'var(--border-default)'),
                  color: inputText.trim() && !isThinking ? 'var(--rh-obsidian)' : 'var(--rh-bone-700)',
                  display:'flex', alignItems:'center', justifyContent:'center',
                  cursor: inputText.trim() && !isThinking ? 'pointer' : 'default',
                }}>
                <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
                  <path d="M22 2L11 13M22 2L15 22l-4-9-9-4 20-7z"/>
                </svg>
              </button>
            </div>
          </div>
        )}
      </div>
    </>
  );
}

// ── Confirm: Gear Setup ───────────────────────────────────────────────
function ConfirmGear({ data, included, setIncluded, onSave, saving }) {
  var guns  = data.guns || [];
  var accs  = data.accessories || [];
  var mods  = data.modifications || [];
  var checkedCount = guns.filter(function(g, i) { return included['gun-' + i]; }).length;

  return (
    <div>
      <div className="screen-hero" style={{paddingTop:0}}>
        <div className="eyebrow">Gear setup · Review</div>
        <h1 className="hero-title" style={{fontSize:28}}>Does this look <span className="red">right?</span></h1>
        <div className="hero-sub">Uncheck anything you don't want to add.</div>
      </div>

      {guns.length > 0 && (
        <>
          <LabelRule>Firearms · {guns.length}</LabelRule>
          {guns.map(function(g, i) {
            var on    = !!included['gun-' + i];
            var label = [g.manufacturer, g.model].filter(Boolean).join(' ') || 'Firearm';
            var meta  = [g.caliber, g.category].filter(Boolean).join(' · ');
            return (
              <div key={i} onClick={function() {
                setIncluded(function(prev) {
                  var n = Object.assign({}, prev);
                  n['gun-' + i] = !n['gun-' + i];
                  return n;
                });
              }} style={{
                display:'grid', gridTemplateColumns:'auto 1fr', gap:12, alignItems:'center',
                padding:'12px 14px', marginBottom:6,
                background: on ? 'var(--bg-elevated-1)' : 'transparent',
                border:'1px solid ' + (on ? 'var(--rh-brass)' : 'var(--border-default)'),
                borderRadius:2, cursor:'pointer',
              }}>
                <span style={{width:20, height:20, borderRadius:2, border:'1px solid '+(on?'var(--rh-brass)':'var(--border-strong)'), background: on ? 'var(--rh-brass)' : 'transparent', display:'flex', alignItems:'center', justifyContent:'center', flexShrink:0}}>
                  {on && <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="var(--rh-obsidian)" strokeWidth="3" strokeLinecap="round"><path d="M20 6L9 17l-5-5"/></svg>}
                </span>
                <div>
                  <div style={{fontFamily:'var(--font-display)', fontSize:18, color:'var(--fg-1)', letterSpacing:'0.02em', lineHeight:1}}>{label}</div>
                  {meta && <div className="label-md" style={{marginTop:4, color:'var(--rh-bone-500)'}}>{meta}</div>}
                </div>
              </div>
            );
          })}
        </>
      )}

      {accs.length > 0 && (
        <>
          <LabelRule>Accessories · {accs.length}</LabelRule>
          <div className="card" style={{padding:'4px 14px', opacity:0.65, marginBottom:8}}>
            {accs.map(function(a, i) {
              return (
                <div key={i} style={{padding:'10px 0', borderBottom: i < accs.length-1 ? '1px solid var(--border-hairline)' : 'none'}}>
                  <div style={{fontFamily:'var(--font-display)', fontSize:15, color:'var(--fg-1)', letterSpacing:'0.02em'}}>{[a.brand, a.model].filter(Boolean).join(' ')}</div>
                  <div className="label-md" style={{marginTop:3, color:'var(--rh-bone-500)'}}>{a.category} · Add in Gear → Accessories</div>
                </div>
              );
            })}
          </div>
        </>
      )}

      {mods.length > 0 && (
        <>
          <LabelRule>Modifications · {mods.length}</LabelRule>
          <div className="card" style={{padding:'4px 14px', opacity:0.65, marginBottom:8}}>
            {mods.map(function(m, i) {
              return (
                <div key={i} style={{padding:'10px 0', borderBottom: i < mods.length-1 ? '1px solid var(--border-hairline)' : 'none'}}>
                  <div style={{fontFamily:'var(--font-display)', fontSize:15, color:'var(--fg-1)', letterSpacing:'0.02em'}}>{m.detail || m.part}</div>
                  <div className="label-md" style={{marginTop:3, color:'var(--rh-bone-500)'}}>{m.part} · Add from gun's Record page</div>
                </div>
              );
            })}
          </div>
        </>
      )}

      {guns.length === 0 && (
        <div className="card" style={{padding:'20px 16px', textAlign:'center', marginBottom:12}}>
          <div className="label-md" style={{color:'var(--rh-bone-500)'}}>No firearms detected</div>
          <div className="rh-body-sm" style={{marginTop:6, color:'var(--rh-bone-700)'}}>Try "Glock G19", "SIG P365 XL", or "11.5 inch AR in 5.56".</div>
        </div>
      )}

      <div style={{marginTop:16}}>
        <VoiceSaveButton label={'Add ' + checkedCount + ' firearm' + (checkedCount === 1 ? '' : 's')} onClick={onSave} saving={saving} disabled={checkedCount === 0}/>
      </div>
    </div>
  );
}

// ── Confirm: Session Log ──────────────────────────────────────────────
function ConfirmSession({ data, onSave, saving }) {
  var guns  = data.guns || [];
  var malfs = data.malfunctions || [];
  var total = guns.reduce(function(s, g) { return s + (g.rounds || 0); }, 0);

  function fmtDate(d) {
    if (!d) return '—';
    var parts = d.split('-');
    if (parts.length < 3) return d;
    var MONTHS = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
    return MONTHS[parseInt(parts[1], 10) - 1] + ' ' + parseInt(parts[2], 10) + ', ' + parts[0];
  }

  return (
    <div>
      <div className="screen-hero" style={{paddingTop:0}}>
        <div className="eyebrow">Session log · Review</div>
        <h1 className="hero-title" style={{fontSize:28}}>Log this <span className="red">session?</span></h1>
      </div>

      <LabelRule>Overview</LabelRule>
      <div className="card" style={{padding:0}}>
        <VoiceConfirmRow k="Date"         v={fmtDate(data.date)}/>
        <VoiceConfirmRow k="Location"     v={data.location || '—'}/>
        <VoiceConfirmRow k="Total rounds" v={total.toLocaleString() + ' rds'}/>
        <VoiceConfirmRow k="Malfunctions" v={
          malfs.length === 0 ? 'None'
          : malfs.map(function(m) {
              return (m.count || 1) + '× ' + m.type.toUpperCase() + (m.description ? ' (' + m.description + ')' : '');
            }).join(', ')
        } last/>
      </div>

      {guns.length > 0 && (
        <>
          <LabelRule>Firearms</LabelRule>
          {guns.map(function(g, i) {
            return (
              <div key={i} className="card" style={{padding:'12px 14px', marginBottom:6}}>
                <div style={{display:'flex', justifyContent:'space-between', alignItems:'baseline', gap:8}}>
                  <div style={{minWidth:0}}>
                    <div style={{fontFamily:'var(--font-display)', fontSize:17, color:'var(--fg-1)', letterSpacing:'0.02em', lineHeight:1}}>{g.gunName || 'Firearm'}</div>
                    <div className="label-md" style={{marginTop:4, color:'var(--rh-bone-500)'}}>
                      {[g.ammo, g.suppressed ? 'Suppressed' : null].filter(Boolean).join(' · ')}
                    </div>
                  </div>
                  <div style={{textAlign:'right', flexShrink:0}}>
                    <div style={{fontFamily:'var(--font-display)', fontSize:22, color:'var(--fg-1)', letterSpacing:'0.02em', lineHeight:1}}>{(g.rounds || 0).toLocaleString()}</div>
                    <div className="label-md" style={{fontSize:8, color:'var(--rh-bone-500)', marginTop:2}}>ROUNDS</div>
                  </div>
                </div>
              </div>
            );
          })}
        </>
      )}

      {data.notes && (
        <>
          <LabelRule>Notes</LabelRule>
          <div className="card" style={{padding:'12px 14px'}}>
            <div className="rh-body-sm" style={{color:'var(--rh-bone-300)'}}>{data.notes}</div>
          </div>
        </>
      )}

      <div style={{marginTop:16}}>
        <VoiceSaveButton label="Save session" onClick={onSave} saving={saving}/>
      </div>
    </div>
  );
}

// ── Confirm: Ammo Purchase ────────────────────────────────────────────
function ConfirmAmmo({ data, onSave, saving }) {
  var label = [data.manufacturer, data.caliber].filter(Boolean).join(' ');
  if (data.grain) label += ' · ' + data.grain + 'gr';
  var useLabel = { training:'Training', defensive:'Defensive', match:'Match' }[data.use] || (data.use || '—');
  var cpr = data.pricePaid > 0 && data.quantity > 0
    ? '$' + (data.pricePaid / data.quantity).toFixed(3)
    : '—';

  return (
    <div>
      <div className="screen-hero" style={{paddingTop:0}}>
        <div className="eyebrow">Ammo purchase · Review</div>
        <h1 className="hero-title" style={{fontSize:28}}>Add this <span className="red">ammo?</span></h1>
      </div>

      <LabelRule>Purchase</LabelRule>
      <div className="card" style={{padding:0}}>
        <VoiceConfirmRow k="Ammo"       v={label || '—'}/>
        <VoiceConfirmRow k="Use"        v={useLabel}/>
        <VoiceConfirmRow k="Quantity"   v={(data.quantity || 0).toLocaleString() + ' rds'}/>
        <VoiceConfirmRow k="Paid"       v={data.pricePaid > 0 ? '$' + Number(data.pricePaid).toFixed(2) : '—'}/>
        <VoiceConfirmRow k="Cost / rd"  v={cpr} last/>
      </div>

      {data.notes && (
        <>
          <LabelRule>Notes</LabelRule>
          <div className="card" style={{padding:'12px 14px'}}>
            <div className="rh-body-sm" style={{color:'var(--rh-bone-300)'}}>{data.notes}</div>
          </div>
        </>
      )}

      <div style={{marginTop:16}}>
        <VoiceSaveButton label="Add to Supply" onClick={onSave} saving={saving}/>
      </div>
    </div>
  );
}

// ── Shared subcomponents ──────────────────────────────────────────────
function VoiceConfirmRow({ k, v, last }) {
  return (
    <div style={{display:'grid', gridTemplateColumns:'90px 1fr', gap:12, alignItems:'baseline', padding:'12px 14px', borderBottom: last ? 'none' : '1px solid var(--border-hairline)'}}>
      <div className="label-md" style={{color:'var(--rh-bone-500)'}}>{k}</div>
      <div style={{fontFamily:'var(--font-display)', fontSize:15, color:'var(--fg-1)', letterSpacing:'0.02em'}}>{v}</div>
    </div>
  );
}

function VoiceSaveButton({ label, onClick, saving, disabled }) {
  var active = !saving && !disabled;
  return (
    <button onClick={active ? onClick : undefined} style={{
      width:'100%', padding:'14px', borderRadius:2,
      background: active ? 'var(--rh-brass)' : 'var(--rh-obsidian-700)',
      border:'1px solid ' + (active ? 'var(--rh-brass)' : 'var(--border-default)'),
      color: active ? 'var(--rh-obsidian)' : 'var(--rh-bone-700)',
      cursor: active ? 'pointer' : 'default',
      fontFamily:'var(--font-ui)', fontSize:11,
      letterSpacing:'0.14em', textTransform:'uppercase', fontWeight:700,
    }}>
      {saving ? 'Saving…' : label}
    </button>
  );
}

function VoiceErrorCard({ msg }) {
  return (
    <div style={{marginBottom:12, padding:'12px 14px', background:'rgba(228,138,130,0.07)', border:'1px solid rgba(228,138,130,0.28)', borderRadius:2}}>
      <div className="label-md" style={{color:'#E48A82'}}>Something went wrong</div>
      <div className="rh-body-sm" style={{marginTop:4, color:'var(--rh-bone-300)'}}>{msg}</div>
    </div>
  );
}

window.VoiceSetup = VoiceSetup;
