// ─────────────────────────────────────────────────────────────────────────
// Meridian — Research / Knowledge library (wired to papers + calls + RAG)
// Library list, semantic search over the vault, markdown reader, upload.
// ─────────────────────────────────────────────────────────────────────────
const TYPE_META = {
  analysis: { label: "Analysis", icon: "file-text", color: "var(--sage-600)" },
  call:     { label: "Weekly call", icon: "headphones", color: "var(--sky-accent)" },
};

// Ledger processing status → friendly badge (German). 'processed' renders no
// badge (the content speaks for itself). Shared by list cards + reader.
function statusBadge(status) {
  if (status === "processing") return { cls: "warn", icon: "loader", spin: true, label: "Aufnahme/Analyse läuft…" };
  if (status === "failed") return { cls: "err", icon: "alert-triangle", label: "Fehlgeschlagen" };
  if (status === "partial") return { cls: "warn", icon: "alert-circle", label: "Teilweise verarbeitet" };
  if (status && status !== "processed") return { cls: "plain", icon: "clock", label: "In Warteschlange" };
  return null;
}

// Status-aware empty state for the Summary / Transcript panes. `kind` is
// 'summary' | 'transcript' so a genuinely-new item gets the right wording.
function StatusNotice({ status, kind }) {
  if (status === "processing") {
    return <EmptyState icon="loader" title="Aufnahme/Analyse läuft…"
      body="Die Aufnahme wird transkribiert und zusammengefasst — das dauert ein paar Minuten." />;
  }
  if (status === "failed") {
    return <EmptyState icon="alert-triangle" title="Fehlgeschlagen – wird automatisch erneut versucht"
      body="Die Verarbeitung ist fehlgeschlagen. Der nächste geplante Lauf versucht es automatisch erneut." />;
  }
  if (kind === "transcript") {
    return <EmptyState icon="captions" title="Transkript wird noch erstellt…"
      body="Sobald die Aufnahme transkribiert ist, erscheint sie hier." />;
  }
  return <EmptyState icon="file-x" title="Noch keine Zusammenfassung"
    body="Dieser Eintrag wurde noch nicht verarbeitet." />;
}

function Research({ theme, onOpenStock, store }) {
  const [q, setQ] = useState("");
  const [type, setType] = useState("all");
  const [mine, setMine] = useState(false);
  const [open, setOpen] = useState(null);
  const [semantic, setSemantic] = useState(null); // {loading, hits, q}

  const recBySym = store.data && store.data.recBySym;
  const lib = useAsync(async () => {
    const [pp, cc] = await Promise.all([API.papers(150).catch(() => ({ papers: [] })), API.calls(150).catch(() => ({ calls: [] }))]);
    return [
      ...(pp.papers || []).map((p) => API.map.mapResearch(p, "analysis", recBySym)),
      ...(cc.calls || []).map((c) => API.map.mapResearch(c, "call", recBySym)),
    ].sort((a, b) => new Date(b.date) - new Date(a.date));
  }, [!!store.data]);

  const items = useMemo(() => {
    const all = lib.data || [];
    return all.filter((r) =>
      (type === "all" || r.type === type) &&
      (!mine || r.inPortfolio) &&
      (q === "" || (r.title + " " + r.summary + " " + (r.ticker || "")).toLowerCase().includes(q.toLowerCase()))
    );
  }, [q, type, mine, lib.data]);

  const runSemantic = async () => {
    if (!q.trim()) { setSemantic(null); return; }
    setSemantic({ loading: true, hits: [], q });
    try {
      const r = await API.search(q.trim(), 8);
      setSemantic({ loading: false, hits: r.hits || [], q });
    } catch (e) {
      setSemantic({ loading: false, hits: [], q, error: e.message });
    }
  };

  if (open) return <Reader item={open} onBack={() => setOpen(null)} onOpenStock={onOpenStock} />;

  const counts = {
    all: (lib.data || []).length,
    analysis: (lib.data || []).filter(r=>r.type==="analysis").length,
    call: (lib.data || []).filter(r=>r.type==="call").length,
  };

  return (
    <div className="rise">
      <PageHeader title="Research" subtitle="Search across every analysis and weekly call in your library."
        actions={<button className="btn btn-sm" onClick={lib.reload}><Icon name="refresh-cw" size={13} /> Reload</button>} />

      {/* semantic search */}
      <form onSubmit={(e) => { e.preventDefault(); runSemantic(); }} style={{ position: "relative", marginBottom: 16 }}>
        <Icon name="sparkles" size={17} style={{ position: "absolute", left: 16, top: "50%", transform: "translateY(-50%)", color: "var(--sage-500)", zIndex: 1 }} />
        <input className="input input-lg" value={q} onChange={(e) => setQ(e.target.value)}
          placeholder="Semantic search — e.g. “which of my holdings are below fair value?”"
          style={{ paddingLeft: 46, paddingRight: 16, boxShadow: "var(--elev-2)" }} />
      </form>

      {/* semantic results */}
      {semantic && (
        <Card style={{ marginBottom: 16 }}>
          <SectionTitle icon="search" action={<button className="btn btn-xs btn-ghost" onClick={() => setSemantic(null)}>Clear</button>}>Knowledge-base matches</SectionTitle>
          {semantic.loading ? <Col gap={8}><Skel h={40} /><Skel h={40} /></Col>
            : semantic.error ? <EmptyState icon="cloud-off" title="Search failed" body={semantic.error} />
            : semantic.hits.length === 0 ? <EmptyState icon="search-x" title="No matches in the vault" body="Index more notes or try broader terms." />
            : <Col gap={2}>
                {semantic.hits.map((h, i) => (
                  <div key={i} style={{ padding: "11px 8px", borderTop: i ? "1px solid var(--border-1)" : 0 }}>
                    <Row justify="space-between" gap={10}>
                      <span className="mono" style={{ fontSize: 12, color: "var(--sage-700)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{h.path}</span>
                      <span className="mono muted-2" style={{ fontSize: 11, flexShrink: 0 }}>{(h.score * 100).toFixed(0)}%</span>
                    </Row>
                    <p className="muted" style={{ fontSize: 12.5, lineHeight: 1.5, margin: "4px 0 0", display: "-webkit-box", WebkitLineClamp: 2, WebkitBoxOrient: "vertical", overflow: "hidden" }}>{h.text}</p>
                  </div>
                ))}
              </Col>}
        </Card>
      )}

      <Row justify="space-between" wrap style={{ gap: 12, marginBottom: 16 }}>
        <div className="segmented">
          {[["all", "All"], ["analysis", "Analyses"], ["call", "Calls"]].map(([k, l]) => (
            <button key={k} className={type === k ? "active" : ""} onClick={() => setType(k)}>{l} <span className="mono" style={{ opacity: 0.6 }}>{counts[k]}</span></button>
          ))}
        </div>
        <button onClick={() => setMine(!mine)} className={"chip" + (mine ? " chip-selected" : "")} style={{ padding: "6px 13px", cursor: "pointer" }}>
          <Icon name="wallet" size={13} /> In my portfolio
        </button>
      </Row>

      <div style={{ display: "grid", gridTemplateColumns: "minmax(0, 1fr) 300px", gap: 16, alignItems: "start" }} className="dash-grid">
        {/* list */}
        <Col gap={12} style={{ minWidth: 0 }}>
          {lib.loading ? <>{[0,1,2].map(i => <SkelCard key={i} lines={2} />)}</>
            : lib.error ? <Card><EmptyState icon="cloud-off" title="Couldn't load research" body={lib.error.message} action={<button className="btn btn-sm" onClick={lib.reload}>Retry</button>} /></Card>
            : items.length === 0 ? <Card><EmptyState icon="library" title={(lib.data||[]).length === 0 ? "Your library is empty" : "Nothing found"} body={(lib.data||[]).length === 0 ? "Ingest analyses & calls, or upload a transcript on the right." : "No documents match that search. Try broader terms or clear the filters."} /></Card>
            : items.map((r) => {
              const tm = TYPE_META[r.type] || TYPE_META.analysis;
              return (
                <button key={r.id} onClick={() => setOpen(r)} className="card card-hover" style={{ textAlign: "left", cursor: "pointer", display: "block", width: "100%" }}>
                  <Row gap={14} align="flex-start">
                    <div style={{ width: 40, height: 40, borderRadius: 11, background: "var(--bg-tint)", display: "grid", placeItems: "center", flexShrink: 0, color: tm.color }}><Icon name={tm.icon} size={18} /></div>
                    <div style={{ flex: 1, minWidth: 0 }}>
                      <Row justify="space-between" align="flex-start" gap={12}>
                        <div style={{ fontWeight: 600, fontSize: 15.5, letterSpacing: "-0.01em", lineHeight: 1.3 }}>{r.title}</div>
                        {r.recommendation && <span className="badge badge-plain" style={{ flexShrink: 0 }}>{r.recommendation}</span>}
                      </Row>
                      <Row gap={8} wrap style={{ marginTop: 5, fontSize: 12, color: "var(--fg-3)" }}>
                        <span style={{ color: tm.color, fontWeight: 500 }}>{tm.label}</span><span>·</span>
                        <span>{r.source}</span><span>·</span><span>{fmt.date(r.date)}</span>
                        {r.ticker && <><span>·</span><span className="mono">{r.ticker}</span></>}
                        {r.inPortfolio && <span className="badge badge-signal" style={{ marginLeft: 2 }}>In portfolio</span>}
                        {(() => { const sb = statusBadge(r.status); return sb ? <span className={"badge badge-" + sb.cls} style={{ marginLeft: 2 }}><Icon name={sb.icon} size={11} className={sb.spin ? "spin" : ""} /> {sb.label}</span> : null; })()}
                      </Row>
                      {r.summary && <p className="muted" style={{ fontSize: 13.5, lineHeight: 1.55, margin: "10px 0 0", display: "-webkit-box", WebkitLineClamp: 2, WebkitBoxOrient: "vertical", overflow: "hidden" }}>{r.summary}</p>}
                    </div>
                  </Row>
                </button>
              );
            })}
        </Col>

        {/* right rail: upload */}
        <Col gap={16} style={{ minWidth: 0 }}>
          <UploadCard onDone={lib.reload} />
          <Card>
            <SectionTitle icon="info">About</SectionTitle>
            <p className="muted" style={{ fontSize: 12.5, lineHeight: 1.6, margin: 0 }}>
              Analyses and calls are ingested automatically by the background jobs. Drop a PDF or transcript above to add it to the library — it’s summarised and embedded for semantic search.
            </p>
          </Card>
        </Col>
      </div>
    </div>
  );
}

/* ── Upload / ingest (real POST /api/ingest/upload) ───────────────────────── */
function UploadCard({ onDone }) {
  const [drag, setDrag] = useState(false);
  const [file, setFile] = useState(null);
  const [kind, setKind] = useState("auto");
  const [stage, setStage] = useState("idle"); // idle | uploading | processing | done | error
  const [pct, setPct] = useState(0);
  const [msg, setMsg] = useState("");
  const inputRef = useRef(null);

  const upload = async (f) => {
    if (!f) return;
    setFile(f.name); setStage("uploading"); setPct(0); setMsg("");
    try {
      const r = await API.ingestUpload(f, kind, (p) => { setPct(p); if (p >= 100) setStage("processing"); });
      setStage("done");
      setMsg(r && (r.title || r.id) ? `${r.title || r.id} · added` : "Added to your library");
      if (onDone) onDone();
    } catch (e) {
      setStage("error");
      setMsg(e.message || "Upload failed");
    }
  };

  return (
    <div
      onDragOver={(e) => { e.preventDefault(); setDrag(true); }}
      onDragLeave={() => setDrag(false)}
      onDrop={(e) => { e.preventDefault(); setDrag(false); if (e.dataTransfer.files[0]) upload(e.dataTransfer.files[0]); }}
      className="card"
      style={{ borderStyle: stage === "idle" ? "dashed" : "solid", borderColor: drag ? "var(--sage-500)" : "var(--border-2)", borderWidth: stage === "idle" ? 2 : 1, background: drag ? "var(--signal-soft)" : "var(--bg-surface)", textAlign: "center", transition: "border-color .15s, background .15s" }}
    >
      <input ref={inputRef} type="file" accept=".pdf,.txt,.md,.vtt,.srt" style={{ display: "none" }} onChange={(e) => e.target.files[0] && upload(e.target.files[0])} />
      {(stage === "idle" || stage === "error") && (
        <Col gap={8} align="center" style={{ padding: "8px 4px" }}>
          <div style={{ width: 42, height: 42, borderRadius: 12, background: "var(--bg-tint)", display: "grid", placeItems: "center", color: "var(--sage-600)" }}><Icon name="upload-cloud" size={20} /></div>
          <div style={{ fontWeight: 600, fontSize: 14 }}>Drop a transcript or PDF</div>
          <div className="muted" style={{ fontSize: 12.5, lineHeight: 1.5 }}>It’s transcribed, summarised and embedded for search.</div>
          <div className="segmented" style={{ marginTop: 4 }}>
            {[["auto","Auto"],["pdf","PDF"],["transcript","Transcript"]].map(([k,l]) => <button key={k} className={kind===k?"active":""} onClick={() => setKind(k)}>{l}</button>)}
          </div>
          <button className="btn btn-sm" style={{ marginTop: 4 }} onClick={() => inputRef.current?.click()}><Icon name="file-up" size={13} /> Choose file</button>
          {stage === "error" && <Row gap={7} style={{ color: "var(--danger-fg)", fontSize: 12, marginTop: 4 }}><Icon name="alert-circle" size={13} /> {msg}</Row>}
        </Col>
      )}
      {(stage === "uploading" || stage === "processing") && (
        <Col gap={10} style={{ padding: "6px 2px", textAlign: "left" }}>
          <Row gap={9}><Icon name="loader" size={15} className="spin" style={{ color: "var(--sage-600)" }} /><span style={{ fontWeight: 600, fontSize: 13.5, flex: 1, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{file}</span></Row>
          <div className="progress"><div style={{ width: (stage === "processing" ? 100 : pct) + "%" }} /></div>
          <div className="muted mono" style={{ fontSize: 11.5 }}>{stage === "processing" ? "Processing & embedding…" : "Uploading · " + pct + "%"}</div>
        </Col>
      )}
      {stage === "done" && (
        <Col gap={9} align="center" style={{ padding: "6px 2px" }}>
          <div style={{ width: 40, height: 40, borderRadius: 999, background: "var(--signal-bg)", display: "grid", placeItems: "center", color: "var(--signal-fg)" }}><Icon name="check" size={20} /></div>
          <div style={{ fontWeight: 600, fontSize: 13.5 }}>Added to your library</div>
          <div className="muted" style={{ fontSize: 12 }}>{msg}</div>
          <button className="btn btn-sm" onClick={() => { setStage("idle"); setFile(null); setPct(0); }}>Add another</button>
        </Col>
      )}
    </div>
  );
}

/* ── Reader — renders real markdown from the API ──────────────────────────── */
function Reader({ item, onBack, onOpenStock }) {
  const tm = TYPE_META[item.type] || TYPE_META.analysis;
  const [tab, setTab] = useState("summary"); // for calls: summary | transcript

  const doc = useAsync(async () => {
    if (item.type === "analysis") return await API.paper(item.id);
    return await API.callSummary(item.id);
  }, [item.id]);

  const transcript = useAsync(async () => {
    if (item.type !== "call" || tab !== "transcript") return null;
    return await API.callTranscript(item.id).catch(() => null);
  }, [item.id, tab]);

  return (
    <div className="rise" style={{ maxWidth: 880, margin: "0 auto" }}>
      <Row gap={10} style={{ marginBottom: 18 }}><button className="btn btn-sm btn-ghost" onClick={onBack}><Icon name="arrow-left" size={14} /> Research</button></Row>

      <Row gap={8} wrap style={{ marginBottom: 12, fontSize: 12.5, color: "var(--fg-3)" }}>
        <span style={{ color: tm.color, fontWeight: 600, display: "inline-flex", alignItems: "center", gap: 5 }}><Icon name={tm.icon} size={13} /> {tm.label}</span>
        <span>·</span><span>{item.source}</span><span>·</span><span>{fmt.date(item.date)}</span>
        {item.ticker && <><span>·</span><span className="mono">{item.ticker}</span></>}
      </Row>
      <h1 style={{ fontSize: 30, fontWeight: 600, letterSpacing: "var(--tracking-tighter)", margin: "0 0 14px", lineHeight: 1.15 }}>{item.title}</h1>

      {/* stat strip */}
      <Row gap={10} wrap style={{ marginBottom: 24 }}>
        {item.recommendation && <div className="card" style={{ padding: "10px 16px", flex: "1 1 120px" }}><div className="kpi-label">Recommendation</div><div style={{ marginTop: 6 }}><span className="badge badge-plain">{item.recommendation}</span></div></div>}
        {item.renditeerwartung != null && <div className="card" style={{ padding: "10px 16px", flex: "1 1 120px" }}><div className="kpi-label">Rendite-Erwartung</div><div className="mono" style={{ fontSize: 18, fontWeight: 600, color: "var(--sage-700)", marginTop: 2 }}>{fmt.pct(item.renditeerwartung)}</div></div>}
        {item.inPortfolio && item.ticker && (
          <button className="card card-hover" style={{ padding: "10px 16px", flex: "1 1 140px", textAlign: "left", cursor: "pointer" }} onClick={() => onOpenStock(item.ticker)}>
            <div className="kpi-label">In portfolio</div>
            <Row gap={5} style={{ marginTop: 5, fontWeight: 600, fontSize: 13 }}>Open position <Icon name="arrow-right" size={13} /></Row>
          </button>
        )}
      </Row>

      {item.type === "call" && (
        <Row gap={4} style={{ marginBottom: 16 }}>
          <div className="segmented">
            <button className={tab === "summary" ? "active" : ""} onClick={() => setTab("summary")}>Summary</button>
            <button className={tab === "transcript" ? "active" : ""} onClick={() => setTab("transcript")}>Transcript</button>
          </div>
        </Row>
      )}

      {item.type === "call" && tab === "transcript" ? (
        <Card style={{ padding: "28px 32px" }}>
          {transcript.loading ? <Col gap={8}><Skel h={14} /><Skel h={14} /><Skel w="80%" h={14} /></Col>
            : (transcript.data && typeof transcript.data.transcript === "string" && transcript.data.transcript.trim())
              ? <pre className="scroll-nice" style={{ whiteSpace: "pre-wrap", overflowWrap: "anywhere", fontFamily: "var(--font-sans)", fontSize: 14, lineHeight: 1.65, color: "var(--fg-1)", margin: 0 }}>{String(transcript.data.transcript)}</pre>
              : <StatusNotice status={item.status} kind="transcript" />}
        </Card>
      ) : (
        <Card style={{ padding: "28px 32px" }}>
          {doc.loading ? <Col gap={10}><Skel h={16} w="40%" /><Skel h={14} /><Skel h={14} /><Skel w="70%" h={14} /></Col>
            : doc.error ? <EmptyState icon="cloud-off" title="Couldn't load document" body={doc.error.message} />
            : (doc.data && doc.data.markdown) ? <MarkdownView text={doc.data.markdown} />
            : <StatusNotice status={item.status} kind="summary" />}
        </Card>
      )}
    </div>
  );
}

Object.assign(window, { Research, Reader, UploadCard });
