// AAP — Components: Sidebar, Welcome, Messages, Composer
const { useState, useEffect, useRef, useCallback } = React;
const Icon = window.AAP_Icon;

// ============ Sidebar ============

// Helpers pour grouper les conversations par date
function _sidebarDateLabel(isoStr) {
  if (!isoStr) return 'Plus ancien';
  const d = new Date(isoStr);
  const now = new Date();
  const diffMs = now - d;
  const diffDays = Math.floor(diffMs / 86400000);
  if (diffDays === 0) return "Aujourd'hui";
  if (diffDays === 1) return 'Hier';
  if (diffDays <= 7) return '7 derniers jours';
  const months = ['Janvier','Février','Mars','Avril','Mai','Juin',
                  'Juillet','Août','Septembre','Octobre','Novembre','Décembre'];
  return d.getFullYear() === now.getFullYear()
    ? months[d.getMonth()]
    : `${months[d.getMonth()]} ${d.getFullYear()}`;
}

function _truncate(str, n) {
  if (!str) return '(sans titre)';
  return str.length > n ? str.slice(0, n) + '…' : str;
}

function _formatHour(isoStr) {
  if (!isoStr) return '';
  const d = new Date(isoStr);
  return d.toLocaleTimeString('fr-FR', { hour: '2-digit', minute: '2-digit' });
}

function Sidebar({ activeId, onNew, onSelect, conversations = [], testerName = 'Jeremy' }) {
  // Grouper les conversations réelles par date
  const groups = {};
  conversations.forEach(c => {
    const label = _sidebarDateLabel(c.last_query_at);
    (groups[label] = groups[label] || []).push(c);
  });

  // Ordre des groupes
  const GROUP_ORDER = ["Aujourd'hui", 'Hier', '7 derniers jours'];
  const sortedGroups = [
    ...GROUP_ORDER.filter(g => groups[g]),
    ...Object.keys(groups).filter(g => !GROUP_ORDER.includes(g)),
  ];

  // Initiales pour l'avatar
  const initials = testerName.split(' ').map(w => w[0]).join('').slice(0, 2).toUpperCase();

  return (
    <aside className="sidebar">
      <div className="sidebar-header">
        <div className="logo-mark"><img src="calipso-logo.png" alt="Calipso" /></div>
        <div className="logo-text">
          <div className="brand-name">AAP</div>
          <div className="brand-sub">Calipso Assurances</div>
        </div>
      </div>
      <button className="new-chat-btn" onClick={onNew}>
        <Icon.Plus s={14} />
        <span>Nouvelle conversation</span>
      </button>
      <div className="conv-list">
        {conversations.length === 0 ? (
          <div className="sidebar-empty">
            <span>Aucune conversation</span>
          </div>
        ) : (
          sortedGroups.map(group => (
            <div key={group}>
              <div className="sidebar-section">{group}</div>
              {groups[group].map(c => (
                <div
                  key={c.session_id}
                  className={"conv-item" + (activeId === c.session_id ? " active" : "")}
                  onClick={() => onSelect(c.session_id)}
                  title={c.first_query || ''}
                >
                  <span className="dot" />
                  <span className="conv-text">{_truncate(c.first_query, 42)}</span>
                  <span className="conv-time">{_formatHour(c.last_query_at)}</span>
                </div>
              ))}
            </div>
          ))
        )}
      </div>
      <div className="sidebar-footer">
        <div className="avatar">{initials || 'QA'}</div>
        <div className="user-info">
          <div className="name">{testerName}</div>
          <div className="role">Banc de test · AAP v3.4</div>
        </div>
        <button className="composer-icon-btn" title="Réglages"><Icon.Settings s={15} /></button>
      </div>
    </aside>
  );
}

// ============ Particles bg ============
function Particles() {
  const dots = Array.from({ length: 14 }, (_, i) => ({
    left: `${i * 7.3 % 100}%`,
    size: 4 + i % 5 * 2,
    delay: -(i * 0.9),
    duration: 12 + i % 4 * 2
  }));
  return (
    <div className="particles">
      {dots.map((d, i) =>
      <div key={i} className="particle" style={{
        left: d.left, width: d.size, height: d.size,
        animationDelay: `${d.delay}s`, animationDuration: `${d.duration}s`
      }} />
      )}
    </div>);

}

// ============ Welcome ============
function Welcome({ onPick }) {
  const sugg = window.AAP_SUGGESTIONS;
  const hour = new Date().getHours();
  const greet = hour < 12 ? 'Bonjour' : hour < 18 ? 'Bon après-midi' : 'Bonsoir';
  return (
    <div className="welcome">
      <Particles />
      <div className="logo-stage">
        <div className="logo-glow" />
        <div className="logo-beam" />
        <div className="logo-beam b2" />
        <div className="logo-beam b3" />
        <div className="logo-rings" />
        <div className="logo-orbit r1"><span className="dot d1" /><span className="dot d2" /></div>
        <div className="logo-orbit r2"><span className="dot b d1" /><span className="dot b d2" /></div>
        <div className="logo-orbit r3"><span className="dot g d1" /><span className="dot g d2" /></div>
        <span className="logo-spark s1" />
        <span className="logo-spark s2" />
        <span className="logo-spark s3" />
        <span className="logo-spark s4" />
        <span className="logo-spark s5" />
        <span className="logo-spark s6" />
        <img className="logo-img" src="calipso-logo.png" alt="Calipso Assurances" />
        <div className="logo-shine" />
      </div>
      <h1>{greet} Marc, je suis <em>AAP</em></h1>
      <p className="greeting">Votre assistant commercial Calipso. analyser votre pipeline, comparer nos garanties Decenal.</p>
    </div>);

}

// ============ Tool Call ============
function ToolCall({ event, running }) {
  const [expanded, setExpanded] = useState(true);
  useEffect(() => {
    if (!running) {
      // collapse after a beat
      const t = setTimeout(() => setExpanded(false), 1800);
      return () => clearTimeout(t);
    }
  }, [running]);
  return (
    <div className={"tool-call" + (running ? " running" : "") + (expanded ? " expanded" : "")}>
      <div className="tool-header" onClick={() => setExpanded((e) => !e)}>
        <div className={"tool-icon " + (event.icon || '')}>
          {event.icon === 'crm' && <Icon.CRM s={13} />}
          {event.icon === 'calc' && <Icon.Calc s={13} />}
          {event.icon === 'search' && <Icon.Search s={13} />}
        </div>
        <div style={{ flex: 1, minWidth: 0 }}>
          <div className="tool-name">{event.name}</div>
          <div className="tool-status">{event.sub}</div>
        </div>
        <div className={"tool-status " + (running ? '' : 'done')}>
          {running ? 'en cours…' : <span style={{ display: 'inline-flex', alignItems: 'center', gap: 4 }}><Icon.Check s={11} /> terminé</span>}
        </div>
        <div className="tool-chevron"><Icon.ChevronRight s={13} /></div>
      </div>
      <div className="tool-progress" />
      <div className="tool-body">
        {event.params && event.params.map((p, i) =>
        <div key={i} className="tool-line">
            <span className="k">{p.k}</span>
            <span className="v">{p.v}</span>
          </div>
        )}
        {!running && event.result &&
        <div className="tool-line" style={{ marginTop: 8, paddingTop: 8, borderTop: '1px dashed var(--line)' }}>
            <span className="k">→ résultat</span>
            <span className="v">{event.result}</span>
          </div>
        }
      </div>
    </div>);

}

// ============ Cards ============
function QuoteCard({ data }) {
  return (
    <div className="result-card">
      <div className="rc-label">{data.title}</div>
      <table className="quote-table">
        <thead><tr><th>Garantie</th><th style={{ textAlign: 'right' }}>Prime</th></tr></thead>
        <tbody>
          {data.rows.map((r, i) =>
          <tr key={i}>
              <td>{r.label}</td>
              <td className="num">{r.value}</td>
            </tr>
          )}
        </tbody>
        <tfoot>
          <tr>
            <td>{data.totalLabel}</td>
            <td className="num quote-total">{data.total}</td>
          </tr>
        </tfoot>
      </table>
    </div>);

}

function LeadsCard({ data }) {
  return (
    <div className="result-card">
      <div className="rc-label">Top 5 · pipeline chaud</div>
      {data.map((l, i) =>
      <div key={i} className="lead-row" style={{ animation: `msgIn 0.4s ${i * 0.08}s both` }}>
          <div className="avatar" style={{ width: 28, height: 28, fontSize: 11 }}>{l.name.split(' ').map((n) => n[0]).slice(0, 2).join('')}</div>
          <div style={{ flex: 1, minWidth: 0 }}>
            <div className="lead-name">{l.name}</div>
            <div className="lead-co">{l.co} · <em style={{ color: 'var(--accent)' }}>{l.signal}</em></div>
          </div>
          <span className={"lead-score " + (l.tier === 'hot' ? '' : l.tier)}>{l.score}</span>
        </div>
      )}
    </div>);

}

function CompareCard({ data }) {
  return (
    <div className="result-card">
      <div className="rc-label">Comparatif RC Pro · benchmarks</div>
      <table className="quote-table">
        <thead>
          <tr>
            <th>Critère</th>
            <th style={{ color: 'var(--accent)' }}>Calipso</th>
            <th>AXA Pro</th>
            <th>Generali</th>
          </tr>
        </thead>
        <tbody>
          {data.map((r, i) =>
          <tr key={i}>
              <td>{r.critere}</td>
              <td style={{ fontWeight: 500, color: 'var(--accent)' }}>{r.calipso}</td>
              <td>{r.axa}</td>
              <td>{r.gen}</td>
            </tr>
          )}
        </tbody>
      </table>
    </div>);

}

function EmailCard({ data }) {
  return (
    <div className="result-card" style={{ background: 'var(--bg-soft)' }}>
      <div className="rc-label" style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
        <Icon.File s={12} /> Brouillon · email
      </div>
      <div style={{ fontSize: 13, color: 'var(--ink-3)', marginBottom: 6 }}>
        <strong style={{ color: 'var(--ink)' }}>Objet :</strong> {data.subject}
      </div>
      <pre style={{
        margin: 0, fontFamily: 'inherit', fontSize: 13.5, lineHeight: 1.6,
        whiteSpace: 'pre-wrap', color: 'var(--ink)'
      }}>{data.body}</pre>
    </div>);

}

// ============ Markdown-ish text ============
function MdText({ text }) {
  // Inline **bold**, then split by newlines
  const lines = text.split('\n').map((line, li) => {
    const parts = [];
    let last = 0;
    const re = /\*\*(.+?)\*\*/g;
    let m;
    while ((m = re.exec(line)) !== null) {
      if (m.index > last) parts.push(line.slice(last, m.index));
      parts.push(<strong key={li + '-' + m.index}>{m[1]}</strong>);
      last = m.index + m[0].length;
    }
    if (last < line.length) parts.push(line.slice(last));
    if (line.trim().startsWith('•')) {
      return <li key={li} style={{ listStyle: 'none', paddingLeft: 8 }}>{parts.map((p, i) => <span key={i}>{i === 0 ? String(p).replace('•', '▸ ') : p}</span>)}</li>;
    }
    return <p key={li}>{parts}</p>;
  });
  return <>{lines}</>;
}

// ============ Streaming Message ============
function StreamingMessage({ scenario, onDone, onCancelRef, onFlag }) {
  const [renderedEvents, setRenderedEvents] = useState([]);
  const [textBuf, setTextBuf] = useState('');
  const [streamingTextIndex, setStreamingTextIndex] = useState(null);
  const [thinking, setThinking] = useState(null);
  const [runningToolIdx, setRunningToolIdx] = useState(null);
  const [finished, setFinished] = useState(false);
  const [flagged, setFlagged] = useState(false);
  const cancelledRef = useRef(false);

  useEffect(() => {
    if (onCancelRef) onCancelRef.current = () => {cancelledRef.current = true;};
    let mounted = true;

    async function run() {
      const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
      const events = scenario.events;

      for (let i = 0; i < events.length; i++) {
        if (cancelledRef.current || !mounted) break;
        const ev = events[i];

        if (ev.type === 'thinking') {
          setThinking(ev.text);
          await sleep(ev.ms);
          setThinking(null);
        } else if (ev.type === 'tool') {
          // push tool, mark running
          setRenderedEvents((p) => [...p, { ...ev, _key: i }]);
          setRunningToolIdx(i);
          await sleep(ev.durationMs);
          if (cancelledRef.current) break;
          setRunningToolIdx(null);
        } else if (ev.type === 'text') {
          // push placeholder text event, then stream tokens
          const idx = i;
          setRenderedEvents((p) => [...p, { ...ev, _key: i, _content: '' }]);
          setStreamingTextIndex(idx);
          const tokens = ev.content.split(/(\s+)/);
          let acc = '';
          for (let t = 0; t < tokens.length; t++) {
            if (cancelledRef.current) break;
            acc += tokens[t];
            setRenderedEvents((p) => p.map((e) => e._key === idx ? { ...e, _content: acc } : e));
            // randomize delay slightly
            await sleep(18 + Math.random() * 35);
          }
          setStreamingTextIndex(null);
        } else if (ev.type === 'card') {
          setRenderedEvents((p) => [...p, { ...ev, _key: i }]);
          await sleep(250);
        }
      }
      if (mounted && !cancelledRef.current) { setFinished(true); onDone(); }
    }
    run();
    return () => {mounted = false;};
    // eslint-disable-next-line
  }, []);

  return (
    <div className="message-row">
      <div className="msg-avatar ai"><Icon.Sparkle s={13} /></div>
      <div className="msg-content">
        <div className="msg-name">AAP <span className="timestamp">à l'instant</span></div>
        <div className="msg-body">
          {renderedEvents.map((ev, i) => {
            if (ev.type === 'tool') {
              return <ToolCall key={ev._key} event={ev} running={runningToolIdx === ev._key} />;
            }
            if (ev.type === 'text') {
              const isStreaming = streamingTextIndex === ev._key;
              return (
                <div key={ev._key}>
                  <MdText text={ev._content} />
                  {isStreaming && <span className="caret" />}
                </div>);

            }
            if (ev.type === 'card') {
              if (ev.kind === 'quote') return <QuoteCard key={ev._key} data={ev.data} />;
              if (ev.kind === 'leads') return <LeadsCard key={ev._key} data={ev.data} />;
              if (ev.kind === 'compare') return <CompareCard key={ev._key} data={ev.data} />;
              if (ev.kind === 'email') return <EmailCard key={ev._key} data={ev.data} />;
            }
            return null;
          })}
          {thinking &&
          <div className="thinking">
              <div className="thinking-dots"><span /><span /><span /></div>
              <span className="thinking-shimmer">{thinking}</span>
            </div>
          }
        </div>
      </div>
    </div>);

}

// ============ User Message ============
function UserMessage({ text }) {
  return (
    <div className="message-row">
      <div className="msg-avatar user">QA</div>
      <div className="msg-content">
        <div className="msg-name">Testeur QA <span className="timestamp">à l'instant</span></div>
        <div className="msg-body"><p>{text}</p></div>
      </div>
    </div>);

}

// ============ Composer ============
function Composer({ onSend, streaming, onStop }) {
  const [val, setVal] = useState('');
  const [focused, setFocused] = useState(false);
  const ref = useRef(null);

  const autosize = useCallback(() => {
    if (!ref.current) return;
    ref.current.style.height = 'auto';
    ref.current.style.height = Math.min(ref.current.scrollHeight, 200) + 'px';
  }, []);

  const submit = () => {
    if (!val.trim() || streaming) return;
    onSend(val.trim());
    setVal('');
    setTimeout(() => {if (ref.current) ref.current.style.height = 'auto';}, 0);
  };

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

  return (
    <div className="composer-wrap">
      <div className={"composer" + (focused ? " focused" : "") + (streaming ? " streaming" : "")}>
        <textarea
          ref={ref}
          rows={1}
          value={val}
          placeholder="Demandez à AAP — devis, leads, comparatif, email…"
          onChange={(e) => {setVal(e.target.value);autosize();}}
          onFocus={() => setFocused(true)}
          onBlur={() => setFocused(false)}
          onKeyDown={handleKey} />
        
        <div className="composer-row">
          <div className="composer-tools">
            <button className="composer-icon-btn" title="Joindre"><Icon.Attach s={15} /></button>
            <button className="composer-icon-btn" title="Voix"><Icon.Mic s={15} /></button>
            <button className="composer-icon-btn" title="Outils"><Icon.Bolt s={14} /></button>
          </div>
          {streaming ?
          <button className="send-btn stop" onClick={onStop} title="Arrêter">
              <Icon.Stop s={11} />
            </button> :

          <button className="send-btn" disabled={!val.trim()} onClick={submit}>
              <Icon.Send s={13} />
            </button>
          }
        </div>
      </div>
      <div className="composer-hint">
        AAP peut faire des erreurs. Vérifiez les informations sensibles. Appuyez sur <kbd>↵</kbd> pour envoyer · <kbd>⇧↵</kbd> nouvelle ligne
      </div>
    </div>);

}

// ============ Login Gate ============
function LoginGate({ onUnlock }) {
  const [code, setCode] = useState('');
  const [error, setError] = useState(false);
  const [shake, setShake] = useState(false);

  const handleSubmit = (e) => {
    e.preventDefault();
    if (code.trim() === 'QA-aap-v8@2026') {
      setError(false);
      onUnlock();
    } else {
      setError(true);
      setShake(true);
      setTimeout(() => setShake(false), 600);
      setCode('');
    }
  };

  return (
    <div style={{
      position: 'fixed', inset: 0,
      background: 'var(--bg)',
      display: 'flex', flexDirection: 'column',
      alignItems: 'center', justifyContent: 'center',
      gap: 24, padding: 24,
    }}>
      <style>{`
        @keyframes qa-shake {
          0%,100%{transform:translateX(0)}
          20%{transform:translateX(-8px)}
          40%{transform:translateX(8px)}
          60%{transform:translateX(-5px)}
          80%{transform:translateX(5px)}
        }
      `}</style>
      <div style={{ textAlign: 'center', marginBottom: 8 }}>
        <div style={{
          width: 56, height: 56, borderRadius: 14,
          background: 'linear-gradient(135deg,#1A1916 0%,#2A2823 100%)',
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          margin: '0 auto 16px', padding: 10,
        }}>
          <img src="calipso-logo.png" alt="Calipso"
               style={{ width: '100%', height: '100%', objectFit: 'contain' }}/>
        </div>
        <h2 style={{ margin: '0 0 6px', fontSize: 22, fontWeight: 600, color: 'var(--ink)' }}>
          Interface QA · AAP
        </h2>
        <p style={{ margin: 0, fontSize: 13.5, color: 'var(--ink-3)' }}>
          Banc de test — accès restreint
        </p>
      </div>

      <form
        onSubmit={handleSubmit}
        style={{ width: '100%', maxWidth: 340, animation: shake ? 'qa-shake 0.5s ease' : 'none' }}
      >
        <label style={{ display: 'block', fontSize: 12, fontWeight: 600,
          color: 'var(--ink-3)', marginBottom: 8, letterSpacing: '.04em',
          textTransform: 'uppercase' }}>
          Code d'accès QA
        </label>
        <input
          type="password"
          value={code}
          onChange={e => { setCode(e.target.value); setError(false); }}
          placeholder="Entrez votre code d'accès…"
          autoFocus
          style={{
            width: '100%', height: 44, padding: '0 14px',
            fontSize: 15, borderRadius: 10,
            border: `1.5px solid ${error ? '#e53935' : 'var(--line-2)'}`,
            background: 'var(--bg-elev)', color: 'var(--ink)', outline: 'none',
            boxSizing: 'border-box', transition: 'border-color .15s',
          }}
        />
        {error && (
          <p style={{ margin: '8px 0 0', fontSize: 12.5, color: '#e53935' }}>
            Code incorrect. Vérifiez le code transmis par l'équipe.
          </p>
        )}
        <button
          type="submit"
          style={{
            width: '100%', height: 44, marginTop: 12,
            background: 'var(--accent)', color: '#fff',
            border: 'none', borderRadius: 10,
            fontSize: 14.5, fontWeight: 600,
            cursor: code.trim() ? 'pointer' : 'default',
            opacity: code.trim() ? 1 : 0.5,
            transition: 'opacity .15s',
          }}
        >
          Accéder à l'interface
        </button>
      </form>
      <p style={{ fontSize: 12, color: 'var(--ink-4)', margin: 0, textAlign: 'center' }}>
        Interface de test interne — Calipso Assurances
      </p>
    </div>
  );
}

// ============ AAPResponse — vraie réponse AAP avec animation typewriter ============
function AAPResponse({ text, status, latencyMs, score, streaming, onFlag, animate = false }) {
  const [displayed, setDisplayed]         = useState(() => animate ? '' : (text || ''));
  const [typewriterDone, setTypewriterDone]= useState(() => !animate);
  const [flagged, setFlagged]             = useState(false);
  const indexRef = useRef(0);

  useEffect(() => {
    if (!text) return;
    // Historique chargé → affichage immédiat, sans animation
    if (!animate) {
      setDisplayed(text);
      setTypewriterDone(true);
      return;
    }
    // Nouvelle réponse → typewriter
    setDisplayed('');
    setTypewriterDone(false);
    indexRef.current = 0;
    const tokens = text.split(/(\s+)/);
    let cancelled = false;
    const step = () => {
      if (cancelled) return;
      if (indexRef.current >= tokens.length) { setTypewriterDone(true); return; }
      indexRef.current++;
      setDisplayed(tokens.slice(0, indexRef.current).join(''));
      setTimeout(step, 14 + Math.random() * 22);
    };
    step();
    return () => { cancelled = true; };
  }, [text, animate]);

  const handleFlag = () => {
    setFlagged(true);
    if (onFlag) onFlag(displayed.slice(0, 140));
  };

  // Indicateur de chargement pendant l'appel AAP
  if (streaming && !text) {
    return (
      <div className="message-row">
        <div className="msg-avatar ai"><Icon.Sparkle s={13}/></div>
        <div className="msg-content">
          <div className="msg-name">AAP <span className="timestamp">à l'instant</span></div>
          <div className="msg-body">
            <div className="thinking">
              <div className="thinking-dots"><span/><span/><span/></div>
              <span className="thinking-shimmer">Interrogation de l'AAP en cours…</span>
            </div>
          </div>
        </div>
      </div>
    );
  }

  return (
    <div className="message-row">
      <div className="msg-avatar ai"><Icon.Sparkle s={13}/></div>
      <div className="msg-content">
        <div className="msg-name">
          AAP
          {latencyMs != null && (
            <span className="timestamp" style={{ marginLeft: 6 }}>
              {(latencyMs / 1000).toFixed(1)}s
              {score != null && ` · score ${score}`}
              {status && status !== 'SUCCESS' && (
                <span style={{
                  color: status === 'NEED_INFO' ? '#f59e0b' : '#e53935',
                  marginLeft: 6, fontWeight: 600,
                }}>
                  {status}
                </span>
              )}
            </span>
          )}
        </div>
        <div className="msg-body">
          <MdText text={displayed}/>
          {!typewriterDone && text && <span className="caret"/>}
          {typewriterDone && text && (
            <div style={{ marginTop: 10 }}>
              <button
                className={"flag-btn" + (flagged ? ' flagged' : '')}
                onClick={handleFlag}
                title="Signaler une anomalie sur cette réponse"
              >
                <svg width="11" height="11" viewBox="0 0 24 24"
                     fill={flagged ? 'currentColor' : 'none'}
                     stroke="currentColor" strokeWidth="2"
                     strokeLinecap="round" strokeLinejoin="round">
                  <path d="M4 15s1-1 4-1 5 2 8 2 4-1 4-1V3s-1 1-4 1-5-2-8-2-4 1-4 1z"/>
                  <line x1="4" y1="22" x2="4" y2="15"/>
                </svg>
                {flagged ? 'Anomalie signalée' : 'Signaler une anomalie'}
              </button>
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

Object.assign(window, {
  LoginGate, AAPResponse,
  Sidebar, Welcome, StreamingMessage, UserMessage, Composer,
  ToolCall, QuoteCard, LeadsCard, CompareCard, EmailCard, MdText,
});