// Main Inbox View — the core screen
// Wired to real backend via window.tmApi (see lib/tm-browser.js)

// Default local-part: prefer the deterministic one returned by the backend
// (sticky to this access key, so the customer always lands on the same address
// across sessions). Fall back to a random one only for very old sessions
// without the field.
const Inbox = ({ session, onSignOut }) => {
  const initialLocal = () => {
    if (session?.defaultLocalPart) return session.defaultLocalPart;
    const arr = (typeof RANDOM_ADDRESSES !== 'undefined' && RANDOM_ADDRESSES.length) ? RANDOM_ADDRESSES : ['mailbox'];
    const pick = arr[Math.floor(Math.random() * arr.length)];
    return pick.replace(/\d+$/, '') + Math.floor(10 + Math.random() * 990);
  };

  const [domains, setDomains] = React.useState({ shared: [], custom: [] });
  const [currentDomain, setCurrentDomain] = React.useState('zeromailer.cloud');
  const [localPart, setLocalPart] = React.useState(initialLocal);
  const [emails, setEmails] = React.useState([]);
  const [selectedId, setSelectedId] = React.useState(null);
  const [refreshing, setRefreshing] = React.useState(false);
  const [toast, setToast] = React.useState(null);
  const [loading, setLoading] = React.useState(true);
  // Settings persist to localStorage so they survive a reload.
  const [settings, setSettings] = React.useState(() => {
    const defaults = { refresh: '10', retention: '24', showCodes: true, sound: false, density: 'cozy' };
    try {
      return { ...defaults, ...JSON.parse(localStorage.getItem('tm_settings') || '{}') };
    } catch { return defaults; }
  });
  React.useEffect(() => {
    try { localStorage.setItem('tm_settings', JSON.stringify(settings)); } catch {}
  }, [settings]);

  // Sound on new mail — Web Audio API ding. Stable single AudioContext.
  // settings.sound is read via a ref so the auto-refresh closure picks up
  // toggle changes without re-creating loadInbox.
  const audioRef = React.useRef(null);
  const soundOnRef = React.useRef(settings.sound);
  React.useEffect(() => { soundOnRef.current = settings.sound; }, [settings.sound]);
  const playDing = React.useCallback((force) => {
    if (!force && !soundOnRef.current) return;
    try {
      if (!audioRef.current) {
        const Ctx = window.AudioContext || window.webkitAudioContext;
        if (!Ctx) return;
        audioRef.current = new Ctx();
      }
      const ctx = audioRef.current;
      if (ctx.state === 'suspended') ctx.resume();
      const now = ctx.currentTime;
      const osc = ctx.createOscillator();
      const gain = ctx.createGain();
      osc.connect(gain);
      gain.connect(ctx.destination);
      osc.type = 'sine';
      osc.frequency.setValueAtTime(880, now);             // A5
      osc.frequency.exponentialRampToValueAtTime(1320, now + 0.08); // → E6
      gain.gain.setValueAtTime(0.0001, now);
      gain.gain.exponentialRampToValueAtTime(0.18, now + 0.02);
      gain.gain.exponentialRampToValueAtTime(0.0001, now + 0.4);
      osc.start(now);
      osc.stop(now + 0.42);
    } catch { /* AudioContext may be blocked until user interaction */ }
  }, []);

  // Modals / panels
  const [domainOpen, setDomainOpen] = React.useState(false);
  const [addOpen, setAddOpen] = React.useState(false);
  const [manageOpen, setManageOpen] = React.useState(false);
  const [settingsOpen, setSettingsOpen] = React.useState(false);
  const [shortcutsOpen, setShortcutsOpen] = React.useState(false);
  const [qrOpen, setQrOpen] = React.useState(false);
  const [dnsModal, setDnsModal] = React.useState(null);   // { domain, records } | null

  const addressInputRef = React.useRef(null);
  const selected = emails.find((e) => e.id === selectedId);
  const fullAddress = `${localPart}@${currentDomain}`;

  // Address input focus + selection state.
  //   addressFocused      = input has keyboard focus → "CHANGE" becomes "SAVE"
  //   addressFullySelected = entire local-part is highlighted → @domain mirrors the selection styling
  const [addressFocused, setAddressFocused] = React.useState(false);
  const [addressFullySelected, setAddressFullySelected] = React.useState(false);

  // Draft state: the input is bound to draftLocalPart, NOT localPart.
  // The committed localPart only changes on explicit save (Enter / SAVE click /
  // dice button). Blurring without commit reverts the draft.
  // This also closes a real privacy issue: previously, every keystroke fired
  // GET /api/inbox?address=… which pollutes the DB with claim rows AND leaks
  // address existence (200 vs 403 vs 409) to anyone with a valid code.
  const [draftLocalPart, setDraftLocalPart] = React.useState(localPart);
  React.useEffect(() => {
    if (!addressFocused) setDraftLocalPart(localPart);
  }, [localPart, addressFocused]);

  const showToast = (msg, kind = 'success') => {
    setToast({ msg, kind });
    setTimeout(() => setToast(null), 1800);
  };

  const doCopy = (text, label = 'Copied to clipboard') => {
    navigator.clipboard?.writeText(text);
    showToast(label);
  };

  // Initial domain load
  React.useEffect(() => {
    let cancelled = false;
    tmApi.customer.domains().then((d) => {
      if (cancelled) return;
      setDomains(d);
      // Prefer the relay's own shared domain if present, else first shared
      const preferred = d.shared.find((s) => s.domain === 'zeromailer.cloud') || d.shared[0];
      if (preferred && !d.custom.some((c) => c.domain === currentDomain)) {
        setCurrentDomain(preferred.domain);
      }
    }).catch((e) => showToast('Could not load domains: ' + e.message, 'error'));
    return () => { cancelled = true; };
  }, []);

  // Fetch inbox whenever address changes. Auto-refresh calls this with
  // silent=true. We MUST preserve any previously-fetched body/headers/
  // attachments on existing rows — the list endpoint doesn't return them
  // and overwriting would wipe the open email's content.
  const loadInbox = React.useCallback(async (silent = false) => {
    if (!silent) setLoading(true);
    try {
      const res = await tmApi.customer.inbox(`${localPart}@${currentDomain}`);
      setEmails((prev) => {
        const prevIds = new Set(prev.map((e) => e.id));
        const prevById = new Map(prev.map((e) => [e.id, e]));
        const next = res.emails.map((e) => {
          const old = prevById.get(e.id);
          return {
            ...e,
            body: old?.body,
            headers: old?.headers,
            attachments: old?.attachments,
            toAddress: old?.toAddress ?? e.toAddress,
            isNew: !prevIds.has(e.id) && silent,
          };
        });
        if (silent) {
          const newCount = next.filter((e) => e.isNew).length;
          if (newCount > 0) {
            showToast(`${newCount} new message${newCount > 1 ? 's' : ''}`);
            playDing();
          }
        }
        return next;
      });
    } catch (e) {
      if (e.code === 'address_taken') showToast('Address in use by another key', 'error');
      else if (e.code !== 'http_401') showToast('Inbox load failed: ' + e.message, 'error');
    } finally {
      setLoading(false);
    }
  }, [localPart, currentDomain]);

  React.useEffect(() => {
    setSelectedId(null);
    loadInbox(false);
  }, [localPart, currentDomain]);

  // Auto-refresh — runs every settings.refresh seconds. Browsers throttle
  // setInterval in background tabs (down to 1/min in Chrome), so we ALSO
  // refresh on visibilitychange so the inbox catches up the moment the user
  // tabs back in.
  React.useEffect(() => {
    if (settings.refresh === 'off') return;
    const ms = Number(settings.refresh) * 1000;
    const t = setInterval(() => loadInbox(true), ms);
    const onVis = () => { if (document.visibilityState === 'visible') loadInbox(true); };
    document.addEventListener('visibilitychange', onVis);
    return () => { clearInterval(t); document.removeEventListener('visibilitychange', onVis); };
  }, [settings.refresh, loadInbox]);

  // Also refresh when the window regains focus (covers cmd-tabbing out)
  React.useEffect(() => {
    const onFocus = () => loadInbox(true);
    window.addEventListener('focus', onFocus);
    return () => window.removeEventListener('focus', onFocus);
  }, [loadInbox]);

  const doRefresh = async () => {
    if (refreshing) return;
    setRefreshing(true);
    const before = emails.length;
    await loadInbox(true);
    setRefreshing(false);
    setEmails((curr) => {
      if (curr.length === before) showToast('No new mail');
      return curr;
    });
  };

  const doDelete = async () => {
    if (!selected) return;
    const idx = emails.findIndex((e) => e.id === selectedId);
    try {
      await tmApi.customer.deleteEmail(selectedId);
      const newList = emails.filter((e) => e.id !== selectedId);
      setEmails(newList);
      setSelectedId(newList[Math.min(idx, newList.length - 1)]?.id || null);
      showToast('Email deleted');
    } catch (e) {
      showToast('Delete failed: ' + e.message, 'error');
    }
  };

  const doSave = async () => {
    if (!selected) return;
    try {
      const res = await tmApi.customer.toggleSave(selectedId);
      setEmails((prev) => prev.map((e) => e.id === selectedId ? { ...e, saved: res.saved } : e));
      showToast(res.saved ? 'Saved' : 'Removed from saved');
    } catch (e) {
      showToast('Save failed: ' + e.message, 'error');
    }
  };

  const doRandomAddress = () => {
    const next = (RANDOM_ADDRESSES && RANDOM_ADDRESSES.length)
      ? RANDOM_ADDRESSES[Math.floor(Math.random() * RANDOM_ADDRESSES.length)]
      : 'mb' + Math.floor(Math.random() * 10000);
    setLocalPart(next);
    setDraftLocalPart(next);
    showToast(`Saved · ${next}@${currentDomain}`);
  };

  // Commit whatever's in the draft to the live address. Returns whether
  // something actually changed so callers can decide on toast/feedback.
  const commitDraft = () => {
    const v = (draftLocalPart || '').trim();
    if (!v) {
      // Empty: revert silently
      setDraftLocalPart(localPart);
      return false;
    }
    if (v === localPart) return false;
    setLocalPart(v);
    showToast(`Saved · ${v}@${currentDomain}`);
    return true;
  };

  const addDomain = async (d, status) => {
    try {
      await tmApi.customer.addDomain(d);
      const fresh = await tmApi.customer.domains();
      setDomains(fresh);
      if (status === 'verified') {
        setCurrentDomain(d);
        showToast(`@${d} added and selected`);
      } else {
        showToast(`@${d} saved as pending — verify DNS to activate`);
      }
    } catch (e) {
      showToast('Add domain failed: ' + e.message, 'error');
    }
  };

  const removeDomain = async (id) => {
    try {
      await tmApi.customer.removeDomain(id);
      const fresh = await tmApi.customer.domains();
      setDomains(fresh);
      showToast('Domain removed');
    } catch (e) {
      showToast('Remove failed: ' + e.message, 'error');
    }
  };

  const reverifyDomain = async (id, domain) => {
    try {
      const res = await tmApi.customer.verifyDomain(id);
      const fresh = await tmApi.customer.domains();
      setDomains(fresh);
      if (res.status === 'verified') showToast(`@${domain} verified ✓`);
      else showToast(`@${domain} still not verified — DNS may need more time`, 'error');
    } catch (e) {
      showToast('Re-verify failed: ' + e.message, 'error');
    }
  };

  const showDnsForDomain = (d) => {
    if (!d.verificationRecords) {
      showToast('DNS records not available — re-open this menu after the domain list refreshes', 'error');
      return;
    }
    setDnsModal({ domain: d.domain, records: d.verificationRecords });
  };

  const toggleFav = (id, kind) => {
    setDomains((prev) => ({
      ...prev,
      [kind]: prev[kind].map((d) => d.id === id ? { ...d, favorited: !d.favorited } : d),
    }));
  };

  // When an email is selected, fetch its full body (the list endpoint
  // doesn't include body to keep responses small)
  React.useEffect(() => {
    if (!selectedId) return;
    const item = emails.find((e) => e.id === selectedId);
    if (!item || item.body !== undefined) return;
    tmApi.customer.email(selectedId).then((full) => {
      setEmails((prev) => prev.map((e) => e.id === selectedId ? { ...e, ...full, unread: false } : e));
    }).catch(() => {});
  }, [selectedId]);

  // Keyboard shortcuts
  React.useEffect(() => {
    const onKey = (e) => {
      if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
      if (e.metaKey || e.ctrlKey || e.altKey) return;
      const k = e.key.toLowerCase();
      if (k === 'r') { e.preventDefault(); doRefresh(); }
      else if (k === 'n') { e.preventDefault(); addressInputRef.current?.focus(); addressInputRef.current?.select(); }
      else if (k === 'd') { e.preventDefault(); doDelete(); }
      else if (k === 'j') { e.preventDefault(); navigateEmail(1); }
      else if (k === 'k') { e.preventDefault(); navigateEmail(-1); }
      else if (k === '/') { e.preventDefault(); addressInputRef.current?.focus(); addressInputRef.current?.select(); }
      else if (k === '?') { e.preventDefault(); setShortcutsOpen(true); }
      else if (k === 'c') { e.preventDefault(); doCopy(fullAddress, 'Address copied'); }
      else if (k === 'g') { e.preventDefault(); setDomainOpen(true); }
      else if (k === 'escape') {
        setDomainOpen(false); setAddOpen(false); setManageOpen(false);
        setSettingsOpen(false); setShortcutsOpen(false); setQrOpen(false);
      }
    };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  });

  const navigateEmail = (dir) => {
    const idx = emails.findIndex((e) => e.id === selectedId);
    const next = emails[Math.max(0, Math.min(emails.length - 1, idx + dir))];
    if (next) setSelectedId(next.id);
  };

  const density = settings.density;
  const rowPadY = density === 'compact' ? 8 : density === 'roomy' ? 18 : 12;

  // Resizable split between list and preview. Persist user's preferred ratio
  // to localStorage. Constrained to 20%..80% so neither side collapses.
  const [splitPct, setSplitPct] = React.useState(() => {
    const saved = parseFloat(localStorage.getItem('tm_split_pct') ?? '');
    return Number.isFinite(saved) && saved >= 20 && saved <= 80 ? saved : 40;
  });
  const splitContainerRef = React.useRef(null);
  const draggingRef = React.useRef(false);
  React.useEffect(() => {
    const onMove = (e) => {
      if (!draggingRef.current || !splitContainerRef.current) return;
      const rect = splitContainerRef.current.getBoundingClientRect();
      const pct = ((e.clientX - rect.left) / rect.width) * 100;
      const clamped = Math.max(20, Math.min(80, pct));
      setSplitPct(clamped);
    };
    const onUp = () => {
      if (!draggingRef.current) return;
      draggingRef.current = false;
      document.body.style.cursor = '';
      document.body.style.userSelect = '';
    };
    window.addEventListener('mousemove', onMove);
    window.addEventListener('mouseup', onUp);
    return () => {
      window.removeEventListener('mousemove', onMove);
      window.removeEventListener('mouseup', onUp);
    };
  }, []);
  React.useEffect(() => {
    localStorage.setItem('tm_split_pct', String(splitPct));
  }, [splitPct]);
  const startDrag = (e) => {
    e.preventDefault();
    draggingRef.current = true;
    document.body.style.cursor = 'col-resize';
    document.body.style.userSelect = 'none';
  };

  return (
    <div style={{ height: '100vh', background: '#0A0A0A', color: '#EDEDED', display: 'flex', flexDirection: 'column', fontFamily: "'Geist', system-ui, sans-serif", overflow: 'hidden' }}>
      {/* TOP BAR */}
      <div style={{ borderBottom: '1px solid #1f1f1f', padding: '10px 16px', display: 'grid', gridTemplateColumns: '220px 1fr auto', gap: 16, alignItems: 'center', flexShrink: 0 }}>
        {/* Left — SMTP status */}
        <div style={{ display: 'flex', alignItems: 'center', gap: 8, fontFamily: "'JetBrains Mono', monospace", fontSize: 10, letterSpacing: '0.18em', color: '#8A8A8A' }}>
          <span style={{ width: 6, height: 6, borderRadius: '50%', background: '#10B981', boxShadow: '0 0 6px #10B981', animation: 'pulse 2s ease-in-out infinite' }} />
          <span>SMTP</span>
          <span style={{ color: '#404040' }}>·</span>
          <span style={{ color: '#EDEDED', textTransform: 'uppercase' }}>{currentDomain}</span>
        </div>

        {/* Center — address input. The local-part is editable; the @domain
            visually mirrors the input's selection state and is included in
            copy when the whole input is highlighted. */}
        <div
          onClick={(e) => { if (e.target.tagName !== 'BUTTON') addressInputRef.current?.focus(); }}
          style={{
            display: 'flex', alignItems: 'center',
            background: '#141414',
            border: `1px solid ${addressFocused ? '#2a4a3f' : '#1f1f1f'}`,
            borderRadius: 6, padding: '0 10px', height: 34,
            transition: 'border-color 120ms',
          }}
        >
          <span style={{ fontFamily: "'JetBrains Mono', monospace", fontSize: 12, color: '#565656', marginRight: 6 }}>$</span>
          <input
            ref={addressInputRef}
            value={draftLocalPart}
            onChange={(e) => {
              // Smart input: if a full email like "alice@example.com" is pasted
              // (or typed), split on @ and switch the domain too — instead of
              // silently dropping the @ and producing a broken local-part.
              // Tolerates surrounding angle brackets ("<alice@x.com>"), spaces.
              const raw = e.target.value.trim().replace(/^<+|>+$/g, '');
              const at = raw.indexOf('@');
              if (at > 0 && at < raw.length - 1) {
                const local = raw.substring(0, at).replace(/[^a-z0-9._-]/gi, '').toLowerCase();
                const dom = raw.substring(at + 1).toLowerCase().trim();
                setDraftLocalPart(local);
                if (/^[a-z0-9][a-z0-9.-]*\.[a-z]{2,}$/i.test(dom)) {
                  const all = [...(domains.shared || []), ...(domains.custom || [])];
                  if (all.some((d) => d.domain === dom)) {
                    if (dom !== currentDomain) {
                      setCurrentDomain(dom);
                      showToast(`Switched to @${dom}`);
                    }
                  } else {
                    showToast(`Domain @${dom} not in your list — kept @${currentDomain}`, 'error');
                  }
                }
              } else {
                setDraftLocalPart(raw.replace(/[^a-z0-9._-]/gi, '').toLowerCase());
              }
            }}
            onFocus={() => setAddressFocused(true)}
            onBlur={() => { setAddressFocused(false); setAddressFullySelected(false); }}
            onSelect={(e) => {
              const t = e.target;
              setAddressFullySelected(
                t.selectionStart === 0 &&
                t.selectionEnd === t.value.length &&
                t.value.length > 0,
              );
            }}
            onKeyDown={(e) => {
              if (e.key === 'Enter') {
                e.preventDefault();
                commitDraft();
                e.target.blur();
              } else if (e.key === 'Escape') {
                e.preventDefault();
                setDraftLocalPart(localPart);
                e.target.blur();
              }
            }}
            onCopy={(e) => {
              const t = e.target;
              if (t.selectionStart === 0 && t.selectionEnd === t.value.length && t.value.length > 0) {
                e.preventDefault();
                e.clipboardData.setData('text/plain', `${t.value}@${currentDomain}`);
              }
            }}
            style={{ flex: 1, background: 'none', border: 'none', outline: 'none', color: '#EDEDED', fontFamily: "'JetBrains Mono', monospace", fontSize: 13 }}
          />
          <span
            onMouseDown={(e) => {
              // Click the @domain → focus + select the whole local-part so
              // copy/paste captures the full email.
              e.preventDefault();
              e.stopPropagation();
              addressInputRef.current?.focus();
              addressInputRef.current?.select();
            }}
            style={{
              fontFamily: "'JetBrains Mono', monospace", fontSize: 13,
              padding: '2px 4px', borderRadius: 2,
              background: addressFullySelected ? '#10B981' : 'transparent',
              color: addressFullySelected ? '#0A0A0A' : '#565656',
              cursor: 'text', userSelect: 'none',
              transition: 'background 80ms, color 80ms',
            }}
          >@{currentDomain}</span>
          <button onClick={doRandomAddress} title="Random address" style={{ background: 'none', border: 'none', cursor: 'pointer', color: '#8A8A8A', marginLeft: 8, display: 'flex', padding: 4 }}>
            <IconDice size={13} />
          </button>
        </div>

        {/* Right — domain selector + icons + count */}
        <div style={{ display: 'flex', alignItems: 'center', gap: 6, position: 'relative' }}>
          <button
            onClick={() => setDomainOpen(!domainOpen)}
            style={{
              display: 'flex', alignItems: 'center', gap: 8,
              background: '#141414', border: '1px solid #1f1f1f',
              color: '#EDEDED', padding: '7px 10px', borderRadius: 6,
              fontFamily: "'JetBrains Mono', monospace", fontSize: 12, cursor: 'pointer',
            }}
          >
            @{currentDomain.length > 14 ? currentDomain.slice(0, 12) + '…' : currentDomain}
            <IconChevronDown size={12} stroke="#8A8A8A" />
          </button>
          <IconBtn onClick={() => doCopy(fullAddress, 'Address copied')} title="Copy address (C)"><IconCopy size={13} /></IconBtn>
          <IconBtn onClick={() => setQrOpen(true)} title="QR code"><IconQR size={13} /></IconBtn>
          <IconBtn onClick={() => setSettingsOpen(true)} title="Settings"><IconSettings size={13} /></IconBtn>

          <div style={{ marginLeft: 10, fontFamily: "'JetBrains Mono', monospace", fontSize: 11, color: '#8A8A8A', letterSpacing: '0.05em' }}>
            <span style={{ color: '#EDEDED' }}>{emails.length}</span> {emails.length === 1 ? 'msg' : 'msgs'}
          </div>

          <DomainDropdown
            open={domainOpen}
            onClose={() => setDomainOpen(false)}
            domains={domains}
            currentDomain={currentDomain}
            onSelect={(d) => { setCurrentDomain(d); setDomainOpen(false); showToast(`Switched to @${d}`); }}
            onAddDomain={() => { setDomainOpen(false); setAddOpen(true); }}
            onToggleFav={toggleFav}
            onShowDns={(d) => { setDomainOpen(false); showDnsForDomain(d); }}
            onReverify={(d) => { setDomainOpen(false); reverifyDomain(d.id, d.domain); }}
            onRemove={(d) => { setDomainOpen(false); removeDomain(d.id); }}
          />
        </div>
      </div>

      {/* ACTION BAR — second slot toggles between CHANGE (idle) and SAVE (editing) */}
      <div style={{ borderBottom: '1px solid #1f1f1f', display: 'grid', gridTemplateColumns: '1fr 1fr 1fr 1fr', flexShrink: 0 }}>
        <ActionBtn icon={<IconRefresh size={13} stroke={refreshing ? '#10B981' : '#EDEDED'} />} label="REFRESH" hotkey="R" onClick={doRefresh} active={refreshing} />
        <ActionBtn
          icon={addressFocused ? <IconCheck size={13} stroke="#10B981" /> : <IconDice size={13} />}
          label={addressFocused ? 'SAVE' : 'CHANGE'}
          hotkey={addressFocused ? '↵' : 'N'}
          // Stop the browser's default focus-shift on mousedown when the input
          // is currently focused — otherwise blur fires before our onClick,
          // re-rendering the button as "CHANGE" so the click bounces straight
          // back to focus.
          onMouseDown={(e) => { if (addressFocused) e.preventDefault(); }}
          onClick={() => {
            if (addressFocused) {
              commitDraft();
              addressInputRef.current?.blur();
            } else {
              addressInputRef.current?.focus();
              addressInputRef.current?.select();
            }
          }}
          active={addressFocused}
          divider
        />
        <ActionBtn icon={<IconTrash size={13} />} label="DELETE" hotkey="D" onClick={doDelete} divider />
        <ActionBtn icon={<IconMenu size={13} />} label="MANAGE" onClick={() => setManageOpen(true)} divider />
      </div>

      {/* MAIN SPLIT — drag the divider to resize */}
      <div
        ref={splitContainerRef}
        style={{
          flex: 1, display: 'grid',
          gridTemplateColumns: `${splitPct}% 5px 1fr`,
          overflow: 'hidden',
        }}
      >
        {/* Inbox list */}
        <div style={{ display: 'flex', flexDirection: 'column', overflow: 'hidden' }}>
          {/* Column headers */}
          <div style={{ display: 'grid', gridTemplateColumns: '40px 1fr 70px', gap: 12, padding: '10px 16px', borderBottom: '1px solid #1f1f1f', fontFamily: "'JetBrains Mono', monospace", fontSize: 9, letterSpacing: '0.2em', color: '#565656', flexShrink: 0 }}>
            <span>#</span>
            <span>FROM · SUBJECT</span>
            <span style={{ textAlign: 'right' }}>TIME</span>
          </div>
          <div style={{ flex: 1, overflowY: 'auto' }}>
            {emails.length === 0 ? (
              <EmptyList />
            ) : (
              emails.map((e, i) => (
                <EmailRow
                  key={e.id}
                  idx={i}
                  email={e}
                  selected={selectedId === e.id}
                  onSelect={() => setSelectedId(e.id)}
                  onCopyCode={(code) => doCopy(code, `Code ${code} copied`)}
                  rowPadY={rowPadY}
                  showCodes={settings.showCodes}
                />
              ))
            )}
          </div>
        </div>

        {/* Drag handle to resize the split */}
        <div
          onMouseDown={startDrag}
          onDoubleClick={() => setSplitPct(40)}
          title="Drag to resize · double-click to reset"
          style={{
            cursor: 'col-resize', background: '#1f1f1f',
            position: 'relative', transition: 'background 120ms',
          }}
          onMouseEnter={(e) => (e.currentTarget.style.background = '#2a2a2a')}
          onMouseLeave={(e) => (e.currentTarget.style.background = '#1f1f1f')}
        >
          {/* a tiny grip indicator */}
          <div style={{
            position: 'absolute', top: '50%', left: '50%',
            transform: 'translate(-50%, -50%)',
            width: 1, height: 24, background: '#404040',
          }} />
        </div>

        {/* Preview panel */}
        <div style={{ display: 'flex', flexDirection: 'column', overflow: 'hidden', background: '#0A0A0A' }}>
          {selected ? <EmailPreview email={selected} onCopyCode={(c) => doCopy(c, `Code ${c} copied`)} /> : <EmptyPreview />}
        </div>
      </div>

      {/* Status footer */}
      <div style={{ borderTop: '1px solid #1f1f1f', padding: '5px 16px', display: 'flex', alignItems: 'center', justifyContent: 'space-between', fontFamily: "'JetBrains Mono', monospace", fontSize: 10, color: '#565656', letterSpacing: '0.08em', flexShrink: 0, background: '#0a0a0a' }}>
          <div style={{ display: 'flex', gap: 14 }}>
            <span><span style={{ color: '#10B981' }}>●</span> RELAY ONLINE</span>
            <span>retention {settings.retention}h</span>
            <span>auto-refresh {settings.refresh === 'off' ? 'off' : settings.refresh + 's'}</span>
          </div>
          <div style={{ display: 'flex', gap: 14 }}>
            <button onClick={() => setShortcutsOpen(true)} style={{ background: 'none', border: 'none', color: '#565656', cursor: 'pointer', fontFamily: 'inherit', fontSize: 'inherit', letterSpacing: 'inherit' }}>? SHORTCUTS</button>
            <span>v0.4.2</span>
          </div>
      </div>

      {/* Overlays */}
      <AddDomainModal open={addOpen} onClose={() => setAddOpen(false)} onAdd={addDomain} />
      <ManageInboxesModal
        open={manageOpen}
        onClose={() => setManageOpen(false)}
        currentAddress={fullAddress}
        onSelectAddress={(addr) => {
          const [lp, dom] = addr.split('@');
          if (lp && dom) {
            setLocalPart(lp);
            setDraftLocalPart(lp);
            setCurrentDomain(dom);
            showToast(`Switched to ${addr}`);
          }
        }}
        toast={(msg, kind) => showToast(msg, kind)}
      />
      <SettingsDrawer open={settingsOpen} onClose={() => setSettingsOpen(false)} settings={settings} setSettings={setSettings} onShowShortcuts={() => { setSettingsOpen(false); setShortcutsOpen(true); }} onSignOut={async () => { try { await tmApi.customer.logout(); } catch {} onSignOut(); }} codePreview={session?.codePreview} onTestSound={() => playDing(true)} />
      <ShortcutsModal open={shortcutsOpen} onClose={() => setShortcutsOpen(false)} />
      <QRModal open={qrOpen} onClose={() => setQrOpen(false)} address={fullAddress} />
      <ShowDnsRecordsModal open={!!dnsModal} onClose={() => setDnsModal(null)} domain={dnsModal?.domain} records={dnsModal?.records} />

      {/* Toast */}
      {toast && (
        <div style={{
          position: 'fixed', bottom: 40, left: '50%', transform: 'translateX(-50%)',
          background: '#141414', border: '1px solid #262626', borderRadius: 6,
          padding: '9px 14px', fontSize: 12, color: '#EDEDED', zIndex: 300,
          display: 'flex', alignItems: 'center', gap: 8,
          fontFamily: "'JetBrains Mono', monospace", letterSpacing: '0.04em',
          animation: 'toastIn 200ms ease',
        }}>
          <span style={{ width: 6, height: 6, borderRadius: '50%', background: toast.kind === 'success' ? '#10B981' : '#F59E0B' }} />
          {toast.msg}
        </div>
      )}
    </div>
  );
};

const IconBtn = ({ children, onClick, title }) => (
  <button onClick={onClick} title={title} style={{
    background: '#141414', border: '1px solid #1f1f1f', color: '#8A8A8A',
    width: 30, height: 30, borderRadius: 6, cursor: 'pointer',
    display: 'flex', alignItems: 'center', justifyContent: 'center',
    transition: 'background 120ms, color 120ms, border-color 120ms',
  }}
    onMouseEnter={(e) => { e.currentTarget.style.background = '#1a1a1a'; e.currentTarget.style.color = '#EDEDED'; }}
    onMouseLeave={(e) => { e.currentTarget.style.background = '#141414'; e.currentTarget.style.color = '#8A8A8A'; }}
  >{children}</button>
);

const ActionBtn = ({ icon, label, hotkey, onClick, onMouseDown, active, divider }) => (
  <button onClick={onClick} onMouseDown={onMouseDown} style={{
    background: active ? '#0f1f1a' : 'transparent', border: 'none',
    borderLeft: divider ? '1px solid #1f1f1f' : 'none',
    color: active ? '#10B981' : '#EDEDED',
    padding: '11px 12px', cursor: 'pointer',
    display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 7,
    fontFamily: "'JetBrains Mono', monospace", fontSize: 11, letterSpacing: '0.12em',
    transition: 'background 120ms',
  }}
    onMouseEnter={(e) => !active && (e.currentTarget.style.background = '#141414')}
    onMouseLeave={(e) => !active && (e.currentTarget.style.background = 'transparent')}
  >
    {icon}
    <span>{label}</span>
    {hotkey && <span style={{ color: '#565656', marginLeft: 2 }}>{hotkey}</span>}
  </button>
);

const EmailRow = ({ idx, email, selected, onSelect, onCopyCode, rowPadY, showCodes }) => (
  <div
    onClick={onSelect}
    style={{
      display: 'grid', gridTemplateColumns: '40px 32px 1fr auto 70px', gap: 10,
      alignItems: 'center', padding: `${rowPadY}px 16px`,
      cursor: 'pointer', borderBottom: '1px solid #141414',
      borderLeft: `2px solid ${selected ? '#10B981' : email.unread ? '#2a4a3f' : 'transparent'}`,
      background: selected ? '#141414' : 'transparent',
      transition: 'background 100ms',
      animation: email.isNew ? 'newMail 1.5s ease' : 'none',
    }}
    onMouseEnter={(e) => !selected && (e.currentTarget.style.background = '#101010')}
    onMouseLeave={(e) => !selected && (e.currentTarget.style.background = 'transparent')}
  >
    <span style={{ fontFamily: "'JetBrains Mono', monospace", fontSize: 10, color: '#404040', letterSpacing: '0.05em' }}>
      {String(idx + 1).padStart(2, '0')}
    </span>
    <div style={{ width: 26, height: 26, borderRadius: 4, background: email.avatarColor, display: 'flex', alignItems: 'center', justifyContent: 'center', fontFamily: "'JetBrains Mono', monospace", fontSize: 12, fontWeight: 600, color: '#EDEDED' }}>
      {email.avatar}
    </div>
    <div style={{ minWidth: 0 }}>
      <div style={{ fontSize: 13, color: email.unread ? '#EDEDED' : '#c8c8c8', fontWeight: email.unread ? 500 : 400, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
        {email.from}
      </div>
      <div style={{ fontSize: 12, color: '#8A8A8A', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', marginTop: 1 }}>
        {email.subject}
      </div>
    </div>
    {email.code && showCodes ? (
      <button onClick={(e) => { e.stopPropagation(); onCopyCode(email.code); }} style={{
        display: 'flex', alignItems: 'center', gap: 5,
        background: '#0a1f1a', border: '1px solid #1a3f33', color: '#10B981',
        padding: '3px 8px', borderRadius: 4, cursor: 'pointer',
        fontFamily: "'JetBrains Mono', monospace", fontSize: 11, letterSpacing: '0.04em',
      }}>
        <IconZap size={9} stroke="#10B981" /> {email.code}
        <IconCopy size={9} stroke="#10B981" />
      </button>
    ) : <span />}
    <span style={{ textAlign: 'right', fontFamily: "'JetBrains Mono', monospace", fontSize: 11, color: '#8A8A8A' }}>
      {email.time}
    </span>
  </div>
);

const EmailPreview = ({ email, onCopyCode }) => (
  <>
    {/* Preview header */}
    <div style={{ padding: '16px 20px', borderBottom: '1px solid #1f1f1f', flexShrink: 0 }}>
      <div style={{ display: 'grid', gridTemplateColumns: '72px 1fr', rowGap: 6, alignItems: 'baseline' }}>
        <Label>SUBJECT</Label>
        <div style={{ fontSize: 14, color: '#EDEDED', fontWeight: 500 }}>{email.subject}</div>
        <Label>FROM</Label>
        <div style={{ fontFamily: "'JetBrains Mono', monospace", fontSize: 12, color: '#c8c8c8' }}>{email.fromEmail}</div>
        <Label>DATE</Label>
        <div style={{ fontFamily: "'JetBrains Mono', monospace", fontSize: 12, color: '#8A8A8A' }}>{email.fullDate}</div>
      </div>
      {email.code && (
        <div style={{ marginTop: 14, padding: '10px 12px', background: '#0a1f1a', border: '1px solid #1a3f33', borderRadius: 6, display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
            <IconZap size={14} stroke="#10B981" />
            <span style={{ fontFamily: "'JetBrains Mono', monospace", fontSize: 10, letterSpacing: '0.2em', color: '#10B981' }}>CODE DETECTED</span>
            <span style={{ fontFamily: "'JetBrains Mono', monospace", fontSize: 18, color: '#EDEDED', letterSpacing: '0.15em', fontWeight: 600 }}>{email.code}</span>
          </div>
          <button onClick={() => onCopyCode(email.code)} style={{ background: '#10B981', color: '#0A0A0A', border: 'none', padding: '6px 12px', borderRadius: 4, fontFamily: "'JetBrains Mono', monospace", fontSize: 10, letterSpacing: '0.15em', fontWeight: 600, cursor: 'pointer', display: 'flex', alignItems: 'center', gap: 5 }}>
            <IconCopy size={10} stroke="#0A0A0A" /> COPY
          </button>
        </div>
      )}
    </div>
    <div style={{ flex: 1, overflow: 'auto', padding: 20 }}>
      {/* Set explicit color so plain-text emails (no inline styles) stay readable.
          HTML emails with their own styles override these defaults. */}
      <div style={{
        background: '#1a1a1a', color: '#EDEDED',
        fontFamily: "'Geist', system-ui, sans-serif", fontSize: 14, lineHeight: 1.6,
        borderRadius: 8, overflow: 'hidden', minHeight: '100%',
      }} dangerouslySetInnerHTML={{ __html: email.body }} />
    </div>
  </>
);

const Label = ({ children }) => (
  <span style={{ fontFamily: "'JetBrains Mono', monospace", fontSize: 9, letterSpacing: '0.22em', color: '#565656' }}>{children}</span>
);

const EmptyList = () => (
  <div style={{ padding: 60, textAlign: 'center', color: '#565656', fontFamily: "'JetBrains Mono', monospace", fontSize: 12 }}>
    <div style={{ marginBottom: 8, fontSize: 11, letterSpacing: '0.2em' }}>▒ INBOX EMPTY ▒</div>
    <div>waiting for mail…</div>
  </div>
);

const EmptyPreview = () => (
  <div style={{ flex: 1, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', color: '#565656', fontFamily: "'JetBrains Mono', monospace", fontSize: 12, gap: 10 }}>
    <div style={{ width: 42, height: 42, borderRadius: 8, border: '1px dashed #262626', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
      <IconArrow size={16} stroke="#404040" />
    </div>
    <div style={{ letterSpacing: '0.1em' }}>Select an email to preview</div>
    <div style={{ fontSize: 10, color: '#404040', letterSpacing: '0.15em' }}>J/K to navigate · ENTER to open</div>
  </div>
);

window.Inbox = Inbox;
