/* eslint-disable */
// ============================================================
// Personality-First Positioning Auditor — single-tool chat app
// Login → paste your messaging → get audited. Nothing else.
// ============================================================

const { useState, useRef, useEffect, useCallback } = React;

// ------------------------------------------------------------
// Config
// ------------------------------------------------------------
const CFG     = typeof STUDIO_CONFIG !== 'undefined' ? STUDIO_CONFIG : {};
const APP     = CFG.appName || "Personality-First Positioning Auditor";
const TAGLINE = CFG.tagline || "Audit your headline, bio, posts. No fluff, no filler.";
const UPSELL  = CFG.upsell || {
  text: "Want me to actually rewrite this with you?",
  linkText: "Book a power hour with Magali →",
  linkUrl: "https://docs.google.com/forms/d/e/1FAIpQLScrJky9cCQvimXMMGmait0PWmMyL_scvRmTiPLEmkXmR4nSXw/viewform",
};
const PLUGIN_ID = "gameplan-launchpad";

const OPENING_MESSAGE =
  "Paste a LinkedIn headline, bio, post, one-liner, or chunk of profile text — anything you want audited as buyer-facing messaging. I'll tell you what's working, what's weakening it, and what to fix first. (Minimum 15 words, otherwise I'll ask one question before auditing.)";

// ------------------------------------------------------------
// Small helpers
// ------------------------------------------------------------
function showToast(message, type = 'info') {
  const container = document.getElementById('toast-container');
  if (!container) return;
  const el = document.createElement('div');
  el.className = `toast ${type}`;
  el.textContent = message;
  container.appendChild(el);
  setTimeout(() => {
    el.style.opacity = '0';
    el.style.transition = 'opacity 0.25s';
    setTimeout(() => el.remove(), 260);
  }, 3000);
}

// Lightweight markdown → HTML for assistant messages (no external lib).
function renderMarkdown(text) {
  if (!text) return '';
  // Escape HTML
  let s = text
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;');

  // Code fences ```...```
  s = s.replace(/```([\s\S]*?)```/g, (_, code) => `<pre><code>${code.trim()}</code></pre>`);

  // Inline code `...`
  s = s.replace(/`([^`\n]+)`/g, '<code>$1</code>');

  // Headers
  s = s.replace(/^### (.+)$/gm, '<h3>$1</h3>')
       .replace(/^## (.+)$/gm,  '<h2>$1</h2>')
       .replace(/^# (.+)$/gm,   '<h1>$1</h1>');

  // Bold + italic
  s = s.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>')
       .replace(/(?<!\*)\*([^*\n]+)\*(?!\*)/g, '<em>$1</em>');

  // Links [text](url)
  s = s.replace(/\[([^\]]+)\]\((https?:[^)]+)\)/g,
                '<a href="$2" target="_blank" rel="noopener">$1</a>');

  // Horizontal rule
  s = s.replace(/^---$/gm, '<hr>');

  // Lists (simple)
  s = s.replace(/(?:^|\n)((?:[-*] .+(?:\n|$))+)/g, (_, block) => {
    const items = block.trim().split(/\n/).map(line =>
      `<li>${line.replace(/^[-*]\s+/, '')}</li>`
    ).join('');
    return `\n<ul>${items}</ul>`;
  });
  s = s.replace(/(?:^|\n)((?:\d+\. .+(?:\n|$))+)/g, (_, block) => {
    const items = block.trim().split(/\n/).map(line =>
      `<li>${line.replace(/^\d+\.\s+/, '')}</li>`
    ).join('');
    return `\n<ol>${items}</ol>`;
  });

  return s;
}

// ------------------------------------------------------------
// Theme (light / dark) — persisted to localStorage
// ------------------------------------------------------------
const THEME_KEY = 'gp_theme';
function getInitialTheme() {
  try {
    const stored = localStorage.getItem(THEME_KEY);
    if (stored === 'light' || stored === 'dark') return stored;
  } catch (_) {}
  return 'dark';
}
function useTheme() {
  const [theme, setTheme] = useState(getInitialTheme);
  useEffect(() => {
    document.documentElement.setAttribute('data-theme', theme);
    try { localStorage.setItem(THEME_KEY, theme); } catch (_) {}
  }, [theme]);
  const toggle = () => setTheme(t => (t === 'dark' ? 'light' : 'dark'));
  return [theme, toggle];
}

function ThemeToggle({ theme, onToggle }) {
  const isDark = theme === 'dark';
  return (
    <button
      className="theme-toggle"
      onClick={onToggle}
      aria-label={isDark ? 'Switch to light mode' : 'Switch to dark mode'}
      title={isDark ? 'Light mode' : 'Dark mode'}
    >
      {isDark ? (
        // Sun
        <svg width="16" height="16" viewBox="0 0 24 24" fill="none"
             stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
          <circle cx="12" cy="12" r="4" />
          <path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41" />
        </svg>
      ) : (
        // Moon
        <svg width="16" height="16" viewBox="0 0 24 24" fill="none"
             stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
          <path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" />
        </svg>
      )}
    </button>
  );
}

// ------------------------------------------------------------
// Logo (orange rounded square + white ⊙ mark)
// ------------------------------------------------------------
function LogoGlyph({ size = 14 }) {
  // Circle-with-centered-dot rendition of "⊙"
  return (
    <svg width={size} height={size} viewBox="0 0 32 32" fill="none">
      <circle cx="16" cy="16" r="9" stroke="#FFFFFF" strokeWidth="2.4" fill="none" />
      <circle cx="16" cy="16" r="2.4" fill="#FFFFFF" />
    </svg>
  );
}

function LogoMark({ size = 24 }) {
  return (
    <span className="gp-logo" style={{ width: size, height: size }}>
      <LogoGlyph size={size * 0.62} />
    </span>
  );
}

// ------------------------------------------------------------
// Reusable form bits
// ------------------------------------------------------------
function Field({ label, type = "text", value, onChange, placeholder, autoFocus, name, autoComplete }) {
  return (
    <div className="field">
      {label && <label className="field-label">{label}</label>}
      <input
        className="field-input"
        type={type}
        value={value}
        onChange={onChange}
        placeholder={placeholder}
        autoFocus={autoFocus}
        name={name}
        autoComplete={autoComplete}
      />
    </div>
  );
}

function Btn({ children, onClick, type = "button", disabled, variant = "primary", block, className }) {
  const cls = ["btn", `btn-${variant}`, block ? "btn-block" : "", className || ""].join(" ").trim();
  return (
    <button type={type} className={cls} onClick={onClick} disabled={disabled}>
      {children}
    </button>
  );
}

// ------------------------------------------------------------
// Auth screen wrapper
// ------------------------------------------------------------
function AuthShell({ children }) {
  return (
    <div className="auth-shell">
      <div className="auth-card">
        <div className="auth-brand">
          <LogoMark size={44} />
          <div>
            <div className="auth-brand-name">{APP}</div>
            <div className="auth-brand-tag">{TAGLINE}</div>
          </div>
        </div>
        {children}
      </div>
    </div>
  );
}

// ------------------------------------------------------------
// Login screen
// ------------------------------------------------------------
function LoginScreen({ onNavigate, onAuth }) {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [busy, setBusy] = useState(false);
  const [error, setError] = useState('');

  const submit = async (e) => {
    e.preventDefault();
    setError('');
    if (!email || !password) { setError('Email and password are required.'); return; }
    setBusy(true);
    try {
      const res = await fetch('/api/auth/login', {
        method: 'POST', credentials: 'include',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ email, password }),
      });
      const data = await res.json().catch(() => ({}));
      if (!res.ok) { setError(data.error || 'Could not sign in.'); setBusy(false); return; }
      onAuth();
    } catch {
      setError('Network error. Please try again.');
      setBusy(false);
    }
  };

  return (
    <AuthShell>
      <div className="auth-title">Sign in</div>
      <form className="auth-form" onSubmit={submit}>
        {error && <div className="auth-error">{error}</div>}
        <Field
          label="Email"
          type="email"
          value={email}
          onChange={(e) => setEmail(e.target.value)}
          placeholder="you@example.com"
          autoComplete="email"
          autoFocus
        />
        <Field
          label="Password"
          type="password"
          value={password}
          onChange={(e) => setPassword(e.target.value)}
          placeholder="••••••••"
          autoComplete="current-password"
        />
        <Btn type="submit" block disabled={busy}>{busy ? 'Signing in…' : 'Sign in'}</Btn>
      </form>
      <div className="auth-meta">
        <button className="auth-link-btn" onClick={() => onNavigate('forgot')}>Forgot password?</button>
        <button className="auth-link-btn" onClick={() => onNavigate('signup')}>Create account</button>
      </div>
    </AuthShell>
  );
}

// ------------------------------------------------------------
// Signup screen
// ------------------------------------------------------------
function SignupScreen({ onNavigate, onAuth }) {
  const [fullName, setFullName] = useState('');
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [busy, setBusy] = useState(false);
  const [error, setError] = useState('');

  const submit = async (e) => {
    e.preventDefault();
    setError('');
    if (!fullName.trim()) { setError('Please enter your name.'); return; }
    if (!email || !password) { setError('Email and password are required.'); return; }
    if (password.length < 8) { setError('Password must be at least 8 characters.'); return; }
    setBusy(true);
    try {
      const res = await fetch('/api/auth/signup', {
        method: 'POST', credentials: 'include',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ fullName, email, password }),
      });
      const data = await res.json().catch(() => ({}));
      if (!res.ok) { setError(data.error || 'Could not create account.'); setBusy(false); return; }
      onAuth();
    } catch {
      setError('Network error. Please try again.');
      setBusy(false);
    }
  };

  return (
    <AuthShell>
      <div className="auth-title">Create your account</div>
      <form className="auth-form" onSubmit={submit}>
        {error && <div className="auth-error">{error}</div>}
        <Field
          label="Name"
          value={fullName}
          onChange={(e) => setFullName(e.target.value)}
          placeholder="Your name"
          autoComplete="name"
          autoFocus
        />
        <Field
          label="Email"
          type="email"
          value={email}
          onChange={(e) => setEmail(e.target.value)}
          placeholder="you@example.com"
          autoComplete="email"
        />
        <Field
          label="Password"
          type="password"
          value={password}
          onChange={(e) => setPassword(e.target.value)}
          placeholder="At least 8 characters"
          autoComplete="new-password"
        />
        <Btn type="submit" block disabled={busy}>{busy ? 'Creating…' : 'Create account'}</Btn>
      </form>
      <div className="auth-footnote">
        Already have an account?{' '}
        <button className="auth-link-btn" onClick={() => onNavigate('login')}>Sign in</button>
      </div>
    </AuthShell>
  );
}

// ------------------------------------------------------------
// Forgot password
// ------------------------------------------------------------
function ForgotScreen({ onNavigate }) {
  const [email, setEmail] = useState('');
  const [busy, setBusy] = useState(false);
  const [sent, setSent] = useState(false);
  const [error, setError] = useState('');

  const submit = async (e) => {
    e.preventDefault();
    setError('');
    if (!email) { setError('Enter the email on your account.'); return; }
    setBusy(true);
    try {
      const res = await fetch('/api/auth/forgot', {
        method: 'POST', credentials: 'include',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ email }),
      });
      if (!res.ok) {
        const data = await res.json().catch(() => ({}));
        setError(data.error || 'Could not send reset email.');
        setBusy(false);
        return;
      }
      setSent(true);
      setBusy(false);
    } catch {
      setError('Network error. Please try again.');
      setBusy(false);
    }
  };

  return (
    <AuthShell>
      <div className="auth-title">Reset your password</div>
      {sent ? (
        <>
          <div className="auth-success">
            If that email is on file, we've sent a reset link. It expires in 1 hour.
          </div>
          <div className="auth-footnote">
            <button className="auth-link-btn" onClick={() => onNavigate('login')}>← Back to sign in</button>
          </div>
        </>
      ) : (
        <>
          <form className="auth-form" onSubmit={submit}>
            {error && <div className="auth-error">{error}</div>}
            <Field
              label="Email"
              type="email"
              value={email}
              onChange={(e) => setEmail(e.target.value)}
              placeholder="you@example.com"
              autoComplete="email"
              autoFocus
            />
            <Btn type="submit" block disabled={busy}>{busy ? 'Sending…' : 'Send reset link'}</Btn>
          </form>
          <div className="auth-footnote">
            <button className="auth-link-btn" onClick={() => onNavigate('login')}>← Back to sign in</button>
          </div>
        </>
      )}
    </AuthShell>
  );
}

// ------------------------------------------------------------
// Reset password (from email link with ?reset=TOKEN)
// ------------------------------------------------------------
function ResetScreen({ token, onNavigate }) {
  const [password, setPassword] = useState('');
  const [confirm, setConfirm] = useState('');
  const [busy, setBusy] = useState(false);
  const [error, setError] = useState('');
  const [done, setDone] = useState(false);

  const submit = async (e) => {
    e.preventDefault();
    setError('');
    if (!password || password.length < 8) { setError('Password must be at least 8 characters.'); return; }
    if (password !== confirm) { setError('Passwords don\'t match.'); return; }
    setBusy(true);
    try {
      const res = await fetch('/api/auth/reset', {
        method: 'POST', credentials: 'include',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ token, password }),
      });
      const data = await res.json().catch(() => ({}));
      if (!res.ok) { setError(data.error || 'Could not reset password.'); setBusy(false); return; }
      setDone(true);
      setBusy(false);
    } catch {
      setError('Network error. Please try again.');
      setBusy(false);
    }
  };

  return (
    <AuthShell>
      <div className="auth-title">Choose a new password</div>
      {done ? (
        <>
          <div className="auth-success">Password reset. You can sign in now.</div>
          <div className="auth-footnote">
            <button className="auth-link-btn" onClick={() => {
              window.history.replaceState({}, '', '/');
              onNavigate('login');
            }}>← Back to sign in</button>
          </div>
        </>
      ) : (
        <>
          <form className="auth-form" onSubmit={submit}>
            {error && <div className="auth-error">{error}</div>}
            <Field
              label="New password"
              type="password"
              value={password}
              onChange={(e) => setPassword(e.target.value)}
              placeholder="At least 8 characters"
              autoComplete="new-password"
              autoFocus
            />
            <Field
              label="Confirm password"
              type="password"
              value={confirm}
              onChange={(e) => setConfirm(e.target.value)}
              placeholder="Type it again"
              autoComplete="new-password"
            />
            <Btn type="submit" block disabled={busy}>{busy ? 'Saving…' : 'Reset password'}</Btn>
          </form>
          <div className="auth-footnote">
            <button className="auth-link-btn" onClick={() => {
              window.history.replaceState({}, '', '/');
              onNavigate('login');
            }}>← Back to sign in</button>
          </div>
        </>
      )}
    </AuthShell>
  );
}

// ------------------------------------------------------------
// Sidebar — saved conversations per user
// ------------------------------------------------------------
function Sidebar({ open, conversations, currentId, onSelect, onNew, onDelete, onRename }) {
  const [renamingId, setRenamingId] = useState(null);
  const [renameDraft, setRenameDraft] = useState('');
  const renameInputRef = useRef(null);

  useEffect(() => {
    if (renamingId && renameInputRef.current) {
      renameInputRef.current.focus();
      renameInputRef.current.select();
    }
  }, [renamingId]);

  const startRename = (c, e) => {
    if (e) e.stopPropagation();
    setRenamingId(c.id);
    setRenameDraft(c.title || '');
  };

  const commitRename = async () => {
    const id = renamingId;
    const title = renameDraft.trim();
    setRenamingId(null);
    setRenameDraft('');
    if (!id || !title) return;
    const original = conversations.find(c => c.id === id);
    if (original && (original.title || '') === title) return;
    try { await onRename(id, title); } catch { /* handled upstream */ }
  };

  const cancelRename = () => {
    setRenamingId(null);
    setRenameDraft('');
  };

  return (
    <aside className={`sidebar ${open ? '' : 'collapsed'}`}>
      <div className="sidebar-header">
        <button className="sidebar-new" onClick={onNew}>
          <svg width="14" height="14" viewBox="0 0 24 24" fill="none"
               stroke="currentColor" strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round">
            <path d="M12 5v14M5 12h14" />
          </svg>
          <span>New chat</span>
        </button>
      </div>
      <div className="sidebar-list">
        {conversations.length === 0 ? (
          <div className="sidebar-empty">No conversations yet.</div>
        ) : (
          conversations.map(c => {
            const isRenaming = renamingId === c.id;
            return (
              <div
                key={c.id}
                className={`convo-item ${c.id === currentId ? 'active' : ''} ${isRenaming ? 'renaming' : ''}`}
                onClick={() => { if (!isRenaming) onSelect(c.id); }}
                onDoubleClick={(e) => startRename(c, e)}
                role="button"
                tabIndex={0}
                onKeyDown={(e) => {
                  if (isRenaming) return;
                  if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); onSelect(c.id); }
                }}
              >
                {isRenaming ? (
                  <input
                    ref={renameInputRef}
                    className="convo-rename-input"
                    value={renameDraft}
                    onChange={(e) => setRenameDraft(e.target.value)}
                    onClick={(e) => e.stopPropagation()}
                    onBlur={commitRename}
                    onKeyDown={(e) => {
                      e.stopPropagation();
                      if (e.key === 'Enter') { e.preventDefault(); commitRename(); }
                      else if (e.key === 'Escape') { e.preventDefault(); cancelRename(); }
                    }}
                    maxLength={100}
                  />
                ) : (
                  <span className="convo-title" title={c.title || 'Untitled'}>
                    {c.title || 'Untitled'}
                  </span>
                )}
                {!isRenaming && (
                  <span className="convo-actions">
                    <button
                      className="convo-action"
                      onClick={(e) => startRename(c, e)}
                      aria-label="Rename conversation"
                      title="Rename"
                    >
                      <svg width="13" height="13" viewBox="0 0 24 24" fill="none"
                           stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
                        <path d="M12 20h9" />
                        <path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4z" />
                      </svg>
                    </button>
                    <button
                      className="convo-action convo-delete"
                      onClick={(e) => onDelete(c.id, e)}
                      aria-label="Delete conversation"
                      title="Delete"
                    >
                      <svg width="13" height="13" viewBox="0 0 24 24" fill="none"
                           stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
                        <path d="M3 6h18M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2M6 6l1 14a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2l1-14" />
                      </svg>
                    </button>
                  </span>
                )}
              </div>
            );
          })
        )}
      </div>
    </aside>
  );
}

// ------------------------------------------------------------
// Chat screen
// ------------------------------------------------------------
function ChatScreen({ user, onLogout, onResendVerify, theme, onToggleTheme }) {
  const [messages, setMessages] = useState([{ role: 'assistant', content: OPENING_MESSAGE }]);
  const [input, setInput] = useState('');
  const [conversationId, setConversationId] = useState(null);
  const [sending, setSending] = useState(false);
  const [waitingForFirstToken, setWaitingForFirstToken] = useState(false);
  const [focused, setFocused] = useState(false);
  const [verifyBannerDismissed, setVerifyBannerDismissed] = useState(() => {
    try { return localStorage.getItem('gp_verify_dismissed') === '1'; }
    catch { return false; }
  });
  const dismissVerifyBanner = () => {
    setVerifyBannerDismissed(true);
    try { localStorage.setItem('gp_verify_dismissed', '1'); } catch {}
  };
  const [conversations, setConversations] = useState([]);
  const [sidebarOpen, setSidebarOpen] = useState(true);
  const inputRef = useRef(null);
  const scrollRef = useRef(null);
  const endRef = useRef(null);

  // Load this user's conversations for this plugin
  const loadConversations = useCallback(async () => {
    try {
      const res = await fetch(
        `/api/conversations?plugin_id=${encodeURIComponent(PLUGIN_ID)}`,
        { credentials: 'include' }
      );
      if (!res.ok) return;
      const data = await res.json();
      setConversations(data.conversations || []);
    } catch { /* offline — ignore */ }
  }, []);

  useEffect(() => { loadConversations(); }, [loadConversations]);

  // Start a fresh conversation (in-memory only; gets persisted on first send)
  const newChat = () => {
    if (sending) return;
    setMessages([{ role: 'assistant', content: OPENING_MESSAGE }]);
    setConversationId(null);
    setInput('');
    if (inputRef.current) {
      inputRef.current.style.height = 'auto';
      inputRef.current.focus();
    }
  };

  // Load an existing conversation by id
  const openConversation = async (id) => {
    if (sending || id === conversationId) return;
    try {
      const res = await fetch(`/api/conversations/${encodeURIComponent(id)}`, { credentials: 'include' });
      if (!res.ok) {
        showToast('Could not open conversation.', 'error');
        return;
      }
      const data = await res.json();
      const msgs = (data.messages || []).map(m => ({ role: m.role, content: m.content }));
      setMessages(msgs.length > 0
        ? [{ role: 'assistant', content: OPENING_MESSAGE }, ...msgs]
        : [{ role: 'assistant', content: OPENING_MESSAGE }]);
      setConversationId(id);
    } catch {
      showToast('Could not open conversation.', 'error');
    }
  };

  // Rename a conversation
  const renameConversation = async (id, title) => {
    // Optimistic update so the sidebar reflects the change immediately
    setConversations(prev =>
      prev.map(c => (c.id === id ? { ...c, title } : c))
    );
    try {
      const res = await fetch(`/api/conversations/${encodeURIComponent(id)}`, {
        method: 'PATCH',
        credentials: 'include',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ title }),
      });
      if (!res.ok) {
        showToast('Could not rename conversation.', 'error');
        loadConversations(); // resync from server
        return;
      }
    } catch {
      showToast('Could not rename conversation.', 'error');
      loadConversations();
    }
  };

  // Delete a conversation (soft delete on the server)
  const deleteConversation = async (id, e) => {
    if (e) e.stopPropagation();
    if (!confirm('Delete this conversation? This cannot be undone.')) return;
    try {
      const res = await fetch(`/api/conversations/${encodeURIComponent(id)}`, {
        method: 'DELETE',
        credentials: 'include',
      });
      if (!res.ok) { showToast('Could not delete conversation.', 'error'); return; }
      if (id === conversationId) {
        setMessages([{ role: 'assistant', content: OPENING_MESSAGE }]);
        setConversationId(null);
      }
      loadConversations();
    } catch {
      showToast('Could not delete conversation.', 'error');
    }
  };

  // Autosize textarea
  const autoSize = () => {
    const el = inputRef.current;
    if (!el) return;
    el.style.height = 'auto';
    el.style.height = Math.min(el.scrollHeight, 200) + 'px';
  };

  // Scroll to bottom when messages grow
  useEffect(() => {
    endRef.current?.scrollIntoView({ behavior: 'smooth', block: 'end' });
  }, [messages, waitingForFirstToken]);

  // Focus input on mount
  useEffect(() => { inputRef.current?.focus(); }, []);

  const sendMessage = async () => {
    const text = input.trim();
    if (!text || sending) return;

    setMessages(prev => [...prev, { role: 'user', content: text }]);
    setInput('');
    setSending(true);
    setWaitingForFirstToken(true);
    if (inputRef.current) inputRef.current.style.height = 'auto';

    try {
      const res = await fetch('/api/chat', {
        method: 'POST',
        credentials: 'include',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ pluginId: PLUGIN_ID, conversationId, message: text }),
      });

      if (!res.ok) {
        const err = await res.json().catch(() => ({}));
        showToast(err.error || 'Something went wrong.', 'error');
        setSending(false);
        setWaitingForFirstToken(false);
        return;
      }

      const newConvoId = res.headers.get('X-Conversation-Id');
      const wasNewConvo = !conversationId && !!newConvoId;
      if (newConvoId) setConversationId(newConvoId);

      // Streamed SSE
      const reader = res.body.getReader();
      const decoder = new TextDecoder();
      let buffer = '';
      let acc = '';
      let started = false;

      while (true) {
        const { done, value } = await reader.read();
        if (done) break;
        buffer += decoder.decode(value, { stream: true });
        const lines = buffer.split('\n');
        buffer = lines.pop() || '';
        for (const line of lines) {
          if (!line.startsWith('data: ')) continue;
          const data = line.slice(6).trim();
          if (data === '[DONE]') continue;
          try {
            const event = JSON.parse(data);
            if (event.type === 'content_block_delta' && event.delta?.text) {
              acc += event.delta.text;
              if (!started) {
                started = true;
                setWaitingForFirstToken(false);
                setMessages(prev => [...prev, { role: 'assistant', content: acc }]);
              } else {
                setMessages(prev => {
                  const next = [...prev];
                  next[next.length - 1] = { role: 'assistant', content: acc };
                  return next;
                });
              }
            }
          } catch {
            /* skip malformed JSON line */
          }
        }
      }

      if (!started) {
        // No tokens streamed back — show a fallback
        setWaitingForFirstToken(false);
        setMessages(prev => [...prev, { role: 'assistant', content: '…' }]);
      }

      // Refresh sidebar so the new convo appears (or moves to the top)
      loadConversations();
    } catch (err) {
      showToast('Connection lost. Try again.', 'error');
      setWaitingForFirstToken(false);
    } finally {
      setSending(false);
    }
  };

  const onKeyDown = (e) => {
    if (e.key === 'Enter' && !e.shiftKey) {
      e.preventDefault();
      sendMessage();
    }
  };

  const canSend = input.trim().length > 0 && !sending;

  return (
    <div className="app-shell">
      <Sidebar
        open={sidebarOpen}
        conversations={conversations}
        currentId={conversationId}
        onSelect={openConversation}
        onNew={newChat}
        onDelete={deleteConversation}
        onRename={renameConversation}
      />
      <div className="chat-shell">
      {/* Header */}
      <header className="chat-header">
        <div className="chat-header-left">
          <button
            className="sidebar-toggle"
            onClick={() => setSidebarOpen(o => !o)}
            aria-label={sidebarOpen ? 'Hide sidebar' : 'Show sidebar'}
            title={sidebarOpen ? 'Hide sidebar' : 'Show sidebar'}
          >
            <svg width="16" height="16" viewBox="0 0 24 24" fill="none"
                 stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
              <rect x="3" y="4" width="18" height="16" rx="2" />
              <path d="M9 4v16" />
            </svg>
          </button>
          <LogoMark size={24} />
          <span className="chat-header-name">{APP}</span>
          <ThemeToggle theme={theme} onToggle={onToggleTheme} />
        </div>
        <div className="chat-header-right">
          <button className="btn btn-ghost" onClick={onLogout}>Sign out</button>
        </div>
      </header>

      {/* Email verify banner */}
      {user && !user.email_verified && !verifyBannerDismissed && (
        <div className="verify-banner">
          <span className="verify-banner-text">
            Please verify your email.{' '}
            <button onClick={onResendVerify}>Resend verification</button>
          </span>
          <button
            className="verify-banner-close"
            onClick={dismissVerifyBanner}
            aria-label="Dismiss"
            title="Dismiss"
          >
            <svg width="14" height="14" viewBox="0 0 24 24" fill="none"
                 stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round">
              <path d="M6 6 L18 18 M18 6 L6 18" />
            </svg>
          </button>
        </div>
      )}

      {/* Messages */}
      <div className="chat-scroll" ref={scrollRef}>
        <div className="chat-inner">
          {messages.map((m, i) => (
            <div key={i} className={`msg-row ${m.role}`}>
              {m.role === 'assistant' ? (
                <>
                  <span className="msg-assistant-mark">
                    <LogoGlyph size={14} />
                  </span>
                  <div
                    className="msg-assistant-text"
                    dangerouslySetInnerHTML={{ __html: renderMarkdown(m.content) }}
                  />
                </>
              ) : (
                <div className="msg-bubble-user">{m.content}</div>
              )}
            </div>
          ))}

          {waitingForFirstToken && (
            <div className="msg-row assistant">
              <span className="msg-assistant-mark">
                <LogoGlyph size={14} />
              </span>
              <div className="msg-assistant-text">
                <span className="typing-dots"><span></span><span></span><span></span></span>
              </div>
            </div>
          )}

          <div ref={endRef} />
        </div>
      </div>

      {/* Input bar */}
      <div className="input-wrap">
        <div className="input-inner">
          <div className={`input-box ${focused ? 'focused' : ''}`}>
            <textarea
              ref={inputRef}
              className="input-textarea"
              placeholder={`Message ${APP}…`}
              rows={1}
              value={input}
              onChange={(e) => { setInput(e.target.value); autoSize(); }}
              onKeyDown={onKeyDown}
              onFocus={() => setFocused(true)}
              onBlur={() => setFocused(false)}
              disabled={sending}
            />
            <button
              className="send-btn"
              onClick={sendMessage}
              disabled={!canSend}
              aria-label="Send message"
            >
              <svg width="14" height="14" viewBox="0 0 24 24" fill="none">
                <path d="M5 12 L19 12 M13 6 L19 12 L13 18"
                      stroke="currentColor" strokeWidth="2.4"
                      strokeLinecap="round" strokeLinejoin="round" />
              </svg>
            </button>
          </div>
          <div className="input-hint">Enter to send · Shift+Enter for new line</div>
        </div>
      </div>

      {/* Pinned upsell strip */}
      <div className="upsell-strip">
        <span>{UPSELL.text}</span>
        <a href={UPSELL.linkUrl} target="_blank" rel="noopener noreferrer">{UPSELL.linkText}</a>
      </div>
      </div>
    </div>
  );
}

// ------------------------------------------------------------
// Root
// ------------------------------------------------------------
function App() {
  const [screen, setScreen] = useState('loading'); // loading | login | signup | forgot | reset | chat
  const [user, setUser] = useState(null);
  const [resetToken, setResetToken] = useState(null);
  const [theme, toggleTheme] = useTheme();

  const loadUser = async () => {
    try {
      const res = await fetch('/api/auth/me', { credentials: 'include' });
      if (!res.ok) { setScreen('login'); return; }
      const data = await res.json();
      setUser(data.user || null);
      setScreen('chat');
    } catch {
      setScreen('login');
    }
  };

  // Initial boot — handle reset token, verified flag, then check session
  useEffect(() => {
    document.title = APP;

    const params = new URLSearchParams(window.location.search);
    const reset = params.get('reset');
    const verified = params.get('verified');

    if (reset) {
      setResetToken(reset);
      setScreen('reset');
      return;
    }

    if (verified) {
      showToast('Email verified successfully!', 'success');
      window.history.replaceState({}, '', '/');
    }

    (async () => {
      try {
        const res = await fetch('/api/auth/verify', { credentials: 'include' });
        const data = await res.json().catch(() => ({}));
        if (data.authenticated) {
          await loadUser();
        } else {
          setScreen('login');
        }
      } catch {
        setScreen('login');
      }
    })();
  }, []);

  const handleAuth = () => loadUser();

  const handleLogout = async () => {
    try {
      await fetch('/api/auth/logout', { method: 'POST', credentials: 'include' });
    } catch {}
    setUser(null);
    setScreen('login');
  };

  const handleResendVerify = async () => {
    try {
      const res = await fetch('/api/auth/resend-verify', { method: 'POST', credentials: 'include' });
      if (res.ok) showToast('Verification email sent!', 'success');
      else showToast('Could not send verification email.', 'error');
    } catch {
      showToast('Could not send verification email.', 'error');
    }
  };

  if (screen === 'loading') {
    return <div className="boot">Loading…</div>;
  }
  if (screen === 'login')   return <LoginScreen   onNavigate={setScreen} onAuth={handleAuth} />;
  if (screen === 'signup')  return <SignupScreen  onNavigate={setScreen} onAuth={handleAuth} />;
  if (screen === 'forgot')  return <ForgotScreen  onNavigate={setScreen} />;
  if (screen === 'reset')   return <ResetScreen   token={resetToken} onNavigate={setScreen} />;
  return (
    <ChatScreen
      user={user}
      onLogout={handleLogout}
      onResendVerify={handleResendVerify}
      theme={theme}
      onToggleTheme={toggleTheme}
    />
  );
}

// ------------------------------------------------------------
// Render
// ------------------------------------------------------------
ReactDOM.createRoot(document.getElementById('app')).render(React.createElement(App));
