/* global React, ReactDOM */
const { useState, useEffect, useRef, useCallback } = React;

/* ============================================
   Logo
   ============================================ */
function Logo({ size = 22 }) {
  return (
    <span className="logo" style={{ fontSize: size }}>
      Gospel<span className="logo-mark">[1]</span>
    </span>
  );
}
window.Logo = Logo;

/* ============================================
   Icons
   ============================================ */
const I = {
  arrow: (
    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <line x1="5" y1="12" x2="19" y2="12" /><polyline points="12 5 19 12 12 19" />
    </svg>
  ),
  check: (
    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round">
      <polyline points="20 6 9 17 4 12" />
    </svg>
  ),
  x: (
    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <line x1="18" y1="6" x2="6" y2="18" /><line x1="6" y1="6" x2="18" y2="18" />
    </svg>
  ),
  spark: (
    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
      <path d="M12 3v3M12 18v3M3 12h3M18 12h3M5.6 5.6l2.1 2.1M16.3 16.3l2.1 2.1M5.6 18.4l2.1-2.1M16.3 7.7l2.1-2.1" />
    </svg>
  ),
  external: (
    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" />
      <polyline points="15 3 21 3 21 9" /><line x1="10" y1="14" x2="21" y2="3" />
    </svg>
  ),
};
window.I = I;

/* ============================================
   Citation visuals (small SVGs for three-things cards)
   ============================================ */
function VisCite() {
  return (
    <svg viewBox="0 0 220 80" width="200" height="80" fill="none" stroke="currentColor" strokeWidth="1">
      <line x1="0" y1="20" x2="190" y2="20" stroke="var(--border-1)" />
      <line x1="0" y1="40" x2="170" y2="40" stroke="var(--border-1)" />
      <line x1="0" y1="60" x2="200" y2="60" stroke="var(--border-1)" />
      <rect x="80" y="12" width="22" height="14" rx="3" fill="var(--accent-tint)" stroke="none" />
      <text x="91" y="22" textAnchor="middle" fontFamily="var(--font-mono)" fontSize="9" fontWeight="600" fill="var(--accent)" stroke="none">[2]</text>
      <rect x="140" y="32" width="22" height="14" rx="3" fill="var(--accent-tint)" stroke="none" />
      <text x="151" y="42" textAnchor="middle" fontFamily="var(--font-mono)" fontSize="9" fontWeight="600" fill="var(--accent)" stroke="none">[5]</text>
      <rect x="50" y="52" width="22" height="14" rx="3" fill="var(--accent-tint)" stroke="none" />
      <text x="61" y="62" textAnchor="middle" fontFamily="var(--font-mono)" fontSize="9" fontWeight="600" fill="var(--accent)" stroke="none">[1]</text>
    </svg>
  );
}
function VisSource() {
  return (
    <svg viewBox="0 0 220 80" width="200" height="80" fill="none" strokeWidth="1">
      <rect x="0" y="14" width="60" height="22" rx="6" fill="var(--bg-2)" stroke="var(--border-1)" />
      <rect x="68" y="14" width="80" height="22" rx="6" fill="var(--bg-2)" stroke="var(--border-1)" />
      <rect x="156" y="14" width="50" height="22" rx="6" fill="var(--bg-2)" stroke="var(--border-1)" />
      <text x="30" y="29" textAnchor="middle" fontFamily="var(--font-mono)" fontSize="9" fill="var(--fg-2)">arxiv</text>
      <text x="108" y="29" textAnchor="middle" fontFamily="var(--font-mono)" fontSize="9" fill="var(--fg-2)">internal-wiki</text>
      <text x="181" y="29" textAnchor="middle" fontFamily="var(--font-mono)" fontSize="9" fill="var(--fg-2)">jstor</text>
      <line x1="34" y1="40" x2="34" y2="58" stroke="var(--accent)" strokeWidth="1.2" />
      <line x1="108" y1="40" x2="108" y2="58" stroke="var(--accent)" strokeWidth="1.2" />
      <line x1="181" y1="40" x2="181" y2="58" stroke="var(--accent)" strokeWidth="1.2" />
      <line x1="34" y1="58" x2="181" y2="58" stroke="var(--accent)" strokeWidth="1.2" />
      <circle cx="108" cy="58" r="4" fill="var(--accent)" />
      <line x1="108" y1="62" x2="108" y2="74" stroke="var(--accent)" strokeWidth="1.2" strokeDasharray="2 2" />
    </svg>
  );
}
function VisHonest() {
  return (
    <svg viewBox="0 0 220 80" width="200" height="80" fill="none">
      <rect x="0" y="10" width="220" height="60" rx="8" fill="var(--bg-2)" stroke="var(--border-1)" />
      <text x="14" y="36" fontFamily="var(--font-mono)" fontSize="11" fill="var(--fg-2)">→ insufficient sources</text>
      <text x="14" y="58" fontFamily="var(--font-mono)" fontSize="11" fill="var(--fg-1)" fontWeight="600">
        i don't know<tspan fill="var(--accent)">.</tspan>
      </text>
    </svg>
  );
}
window.VisCite = VisCite;
window.VisSource = VisSource;
window.VisHonest = VisHonest;

/* ============================================
   Streaming answer renderer
   - Takes a tokens array (alternating {text} | {cite} | {para})
   - Streams text char-by-char, citations pop in instantly
   ============================================ */
function StreamingAnswer({ qData, onComplete, speed = 8 }) {
  const [renderedTokens, setRenderedTokens] = useState([]);
  const [done, setDone] = useState(false);
  const [hoveredCite, setHoveredCite] = useState(null);
  const [popPos, setPopPos] = useState({ x: 0, y: 0 });
  const cancelRef = useRef(false);

  useEffect(() => {
    cancelRef.current = false;
    setRenderedTokens([]);
    setDone(false);
    let i = 0;
    const tokens = qData.answer;

    async function run() {
      const acc = [];
      for (let ti = 0; ti < tokens.length; ti++) {
        if (cancelRef.current) return;
        const t = tokens[ti];
        if (t.text) {
          // stream char by char
          let partial = "";
          for (let c = 0; c < t.text.length; c++) {
            if (cancelRef.current) return;
            partial += t.text[c];
            const next = [...acc, { text: partial }];
            setRenderedTokens([...next]);
            await new Promise(r => setTimeout(r, speed));
          }
          acc.push({ text: t.text });
        } else if (t.cite !== undefined) {
          acc.push({ cite: t.cite });
          setRenderedTokens([...acc]);
          await new Promise(r => setTimeout(r, 80));
        } else if (t.para) {
          acc.push({ para: true });
          setRenderedTokens([...acc]);
          await new Promise(r => setTimeout(r, 100));
        }
      }
      setDone(true);
      onComplete && onComplete();
    }
    run();
    return () => { cancelRef.current = true; };
  }, [qData, speed]);

  // group into paragraphs
  const paragraphs = [];
  let current = [];
  renderedTokens.forEach((t, i) => {
    if (t.para) {
      paragraphs.push(current);
      current = [];
    } else {
      current.push({ ...t, _i: i });
    }
  });
  paragraphs.push(current);

  const handleCiteHover = (e, num) => {
    const r = e.currentTarget.getBoundingClientRect();
    setPopPos({ x: r.left + r.width / 2, y: r.top - 8 });
    setHoveredCite(num);
  };

  const sourceMap = {};
  qData.sources.forEach(s => { sourceMap[s.num] = s; });

  return (
    <>
      <div className="answer-body">
        {paragraphs.map((para, pi) => (
          <p key={pi}>
            {para.map((t, i) => {
              if (t.cite !== undefined) {
                return (
                  <a
                    key={i}
                    className="cite"
                    href={`#source-${t.cite}`}
                    onMouseEnter={(e) => handleCiteHover(e, t.cite)}
                    onMouseLeave={() => setHoveredCite(null)}
                    onClick={(e) => {
                      e.preventDefault();
                      const el = document.getElementById(`source-${t.cite}`);
                      if (el) {
                        el.style.transition = 'transform 0.2s';
                        el.style.transform = 'translateX(-4px)';
                        setTimeout(() => { el.style.transform = ''; }, 200);
                      }
                    }}
                  >
                    {t.cite}
                  </a>
                );
              }
              return <span key={i}>{t.text}</span>;
            })}
            {pi === paragraphs.length - 1 && !done && <span className="caret" />}
          </p>
        ))}
      </div>

      {hoveredCite !== null && sourceMap[hoveredCite] && (
        <div
          className="cite-popover visible"
          style={{ left: popPos.x - 140, top: popPos.y - 90 }}
        >
          <span className="src-domain">{sourceMap[hoveredCite].domain} · {sourceMap[hoveredCite].year}</span>
          <span className="src-title">{sourceMap[hoveredCite].title}</span>
        </div>
      )}
    </>
  );
}
window.StreamingAnswer = StreamingAnswer;

/* ============================================
   Sources panel — animated reveal
   ============================================ */
function SourcesPanel({ sources, revealCount }) {
  return (
    <div className="sources">
      <div className="sources-title">Citations · {sources.length}</div>
      {sources.map((s, i) => (
        <a
          key={s.num}
          id={`source-${s.num}`}
          className={`source-card ${i < revealCount ? 'visible' : ''}`}
          href="#"
          onClick={(e) => e.preventDefault()}
          style={{ transitionDelay: `${i * 80}ms` }}
        >
          <span className="source-num">{s.num}</span>
          <span className="source-meta">
            <span className="source-domain">{s.domain} · {s.year}</span>
            <span className="source-title">{s.title}</span>
          </span>
        </a>
      ))}
    </div>
  );
}
window.SourcesPanel = SourcesPanel;
