// chat.jsx - інтерфейс бесіди з Azoth AI
const { useState, useRef, useEffect, useCallback } = React;

/* ---- typewriter ---- */
function Typewriter({ text, speed = 22, onTick, onDone }) {
  const [n, setN] = useState(0);
  useEffect(() => {
    setN(0);
    if (!text) return;
    let i = 0;
    const id = setInterval(() => {
      i += 1; setN(i);
      onTick && onTick();
      if (i >= text.length) { clearInterval(id); onDone && onDone(); }
    }, speed);
    return () => clearInterval(id);
  }, [text, speed]);
  const done = n >= text.length;
  return (
    <span>
      {text.slice(0, n)}
      {!done && <span className="tw-caret">✦</span>}
    </span>
  );
}

/* ---- tarot card with flip ---- */
function TarotCard({ card, delay = 0, flipped }) {
  return (
    <div className="tcard" style={{ animationDelay: `${delay}ms` }}>
      <div className={"tcard__inner" + (flipped ? " is-flipped" : "")}>
        <div className="tcard__face tcard__back">
          <span className="tcard__back-glyph">✦</span>
        </div>
        <div className={"tcard__face tcard__front" + (card.img ? " tcard__front--image" : "")}>
          {card.img
            ? <img className="tcard__image" src={card.img} alt={card.name} loading="lazy" />
            : <span className="tcard__glyph">{card.glyph}</span>}
          <span className="tcard__num">{card.n}</span>
          <span className="tcard__meta">
            <span className="tcard__name">{card.name}</span>
            <span className="tcard__key">{card.key}</span>
          </span>
        </div>
      </div>
    </div>
  );
}

function Message({ msg, speed, onTick }) {
  if (msg.role === "user") {
    return (
      <div className="msg msg--user">
        <div className="msg__body msg__body--user">{msg.text}</div>
      </div>
    );
  }
  return (
    <div className="msg msg--agent">
      <div className="msg__avatar"><SigilMark size={32} live={msg.typing} /></div>
      <div className="msg__col">
        <div className="msg__who">Azoth AI</div>
        {msg.cards && <TarotSpread cards={msg.cards} />}
        <div className="msg__body">
          {msg.streaming && !msg.text
            ? <span className="cm__divining"><i /><i /><i /> вдивляюся…</span>
            : msg.typing
            ? <Typewriter text={msg.text} speed={speed} onTick={onTick} onDone={msg.onDone} />
            : msg.text}
        </div>
        {msg.sources && msg.sources.length > 0 && (
          <div className="msg__sources">
            {msg.sources.slice(0, 5).map((s, i) => (
              <a key={i} href={s.url} target="_blank" rel="noreferrer">
                {s.title || s.url}
              </a>
            ))}
          </div>
        )}
      </div>
    </div>
  );
}

function TarotSpread({ cards }) {
  const [flipped, setFlipped] = useState([]);
  useEffect(() => {
    cards.forEach((_, i) => {
      setTimeout(() => setFlipped(f => [...f, i]), 500 + i * 650);
    });
  }, []);
  return (
    <div className="tspread">
      {cards.map((c, i) => (
        <div key={i} className="tspread__slot">
          <TarotCard card={c} delay={i * 120} flipped={flipped.includes(i)} />
          <span className="tspread__label">{SPREAD_LABELS[i] || ""}</span>
        </div>
      ))}
    </div>
  );
}

const SPREAD_LABELS = ["Минуле", "Теперішнє", "Прийдешнє"];

function buildSpreadContext(cards) {
  if (!cards || !cards.length) return "";
  const cardList = cards
    .map((card, i) => `${SPREAD_LABELS[i] || `Позиція ${i + 1}`}: ${card.name} — ${card.key}`)
    .join("; ");
  return `\n\n[Карти розкладу, які вже відкриті в інтерфейсі: ${cardList}. Тлумач саме ці карти й ці позиції; не замінюй їх іншими арканами.]`;
}

function Chat({ seeker, onSignOut, speed = 22 }) {
  const disc = window.DISCIPLINES;
  const REPLIES = window.REPLIES, SUGGESTIONS = window.SUGGESTIONS, drawCards = window.drawCards;
  const [active, setActive] = useState((seeker?.disciplines && seeker.disciplines[0]) || "free");
  const [sidebar, setSidebar] = useState(true);
  const [input, setInput] = useState("");
  const [msgs, setMsgs] = useState([]);
  const [busy, setBusy] = useState(false);
  const [threads, setThreads] = useState([]);
  const [activeThreadId, setActiveThreadId] = useState(null);
  const [loadingThread, setLoadingThread] = useState(false);
  const scrollRef = useRef(null);
  const taRef = useRef(null);

  const activeDisc = disc.find(d => d.id === active);

  const scrollDown = useCallback(() => {
    const el = scrollRef.current; if (el) el.scrollTop = el.scrollHeight;
  }, []);
  useEffect(() => { scrollDown(); }, [msgs, scrollDown]);

  const localGreeting = useCallback(() => ({
    id: "greeting",
    role: "agent",
    text: REPLIES.greeting.replace("{name}", seeker?.name || "мандрівник"),
    typing: false,
  }), [REPLIES, seeker?.name]);

  async function refreshThreads(selectFirst = false) {
    try {
      const data = await window.AzothApi.threads();
      setThreads(data.threads || []);
      if (selectFirst && data.threads && data.threads[0]) loadThread(data.threads[0].id);
      if (selectFirst && (!data.threads || !data.threads[0])) startNew();
    } catch (err) {
      setMsgs([{ id: "auth-error", role: "agent", text: "Сесія згасла. Увійди ще раз.", typing: false }]);
    }
  }

  useEffect(() => { refreshThreads(true); }, []);

  async function loadThread(threadId) {
    setLoadingThread(true);
    setBusy(false);
    setActiveThreadId(threadId);
    try {
      const data = await window.AzothApi.history(threadId);
      setMsgs((data.messages || []).map(m => ({
        id: m.id,
        role: m.role === "assistant" ? "agent" : "user",
        text: m.content,
        typing: false,
        sources: m.meta?.sources || [],
      })));
    } catch (err) {
      setMsgs([{ id: "load-error", role: "agent", text: "Не вдалося відкрити цю бесіду.", typing: false }]);
    } finally {
      setLoadingThread(false);
    }
  }

  function startNew() {
    setActiveThreadId(null);
    setBusy(false);
    setMsgs([localGreeting()]);
  }

  function pushAgent(text, cards) {
    const id = Date.now() + Math.random();
    setBusy(true);
    // brief "divining" pause, then type
    setTimeout(() => {
      setMsgs(m => [...m, {
        id, role: "agent", text, cards, typing: true,
        onDone: () => { setBusy(false); setMsgs(mm => mm.map(x => x.id === id ? { ...x, typing: false } : x)); }
      }]);
    }, cards ? 200 : 650);
  }

  function send(text) {
    const q = (text ?? input).trim();
    if (!q || busy) return;
    setInput("");
    if (taRef.current) taRef.current.style.height = "auto";
    setMsgs(m => [...m, { id: Date.now(), role: "user", text: q }]);

    const lc = q.toLowerCase();
    const wantsSpread = active === "tarot" || /карт|розклад|тар/.test(lc);
    const spreadCards = wantsSpread ? drawCards(3) : null;
    const agentId = Date.now() + Math.random() + "-agent";
    setBusy(true);
    setMsgs(m => [...m, {
      id: agentId,
      role: "agent",
      text: "",
      cards: spreadCards,
      typing: false,
      streaming: true,
      sources: [],
    }]);
    window.AzothApi.streamChat({
      message: `${active === "free" ? q : `[${activeDisc.name}] ${q}`}${buildSpreadContext(spreadCards)}`,
      threadId: activeThreadId,
      search: null,
    }, {
      thread: ({ thread_id }) => setActiveThreadId(thread_id),
      sources: ({ sources }) => setMsgs(m => m.map(x => x.id === agentId ? { ...x, sources: sources || [] } : x)),
      delta: ({ text }) => setMsgs(m => m.map(x => x.id === agentId ? { ...x, text: x.text + (text || "") } : x)),
      error: ({ message }) => {
        setMsgs(m => m.map(x => x.id === agentId ? {
          ...x,
          text: `Завіса затремтіла: ${message || "відповідь обірвалася"}`,
          streaming: false,
        } : x));
        setBusy(false);
      },
      done: () => {
        setMsgs(m => m.map(x => x.id === agentId ? { ...x, streaming: false } : x));
        setBusy(false);
        refreshThreads(false);
      },
    }).catch(err => {
      setMsgs(m => m.map(x => x.id === agentId ? {
        ...x,
        text: `Завіса затремтіла: ${err.message || err}`,
        streaming: false,
      } : x));
      setBusy(false);
    });
  }

  function onKey(e) {
    if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); send(); }
  }
  function grow(e) {
    setInput(e.target.value);
    e.target.style.height = "auto";
    e.target.style.height = Math.min(e.target.scrollHeight, 160) + "px";
  }

  return (
    <div className={"app-screen view-enter chat" + (sidebar ? "" : " chat--collapsed")}>
      {/* ---- sidebar ---- */}
      <aside className="cs">
        <div className="cs__top">
          <div className="ld__brand">
            <SigilMark size={26} live />
            <span className="wordmark" style={{ fontSize: ".9rem" }}>AZOTH</span>
          </div>
          <button className="cs__collapse" onClick={() => setSidebar(false)} title="Сховати">‹</button>
        </div>

        <button className="cs__new" onClick={startNew}>
          <span>✦</span> Нова бесіда
        </button>

        <div className="cs__group-label">Дисципліни</div>
        <nav className="cs__disc">
          {disc.map(d => (
            <button key={d.id}
              className={"cs__disc-item" + (active === d.id ? " is-on" : "")}
              onClick={() => setActive(d.id)}>
              <span className="cs__disc-glyph">{d.glyph}</span>
              <span>{d.name}</span>
            </button>
          ))}
        </nav>

        <div className="cs__group-label">Недавнє</div>
        <div className="cs__history">
          {threads.length === 0 && <div className="cs__empty">Ще немає збережених бесід</div>}
          {threads.map((h) => (
            <button key={h.id}
              className={"cs__hist-item" + (h.id === activeThreadId ? " is-on" : "")}
              onClick={() => loadThread(h.id)}>
              {h.title}
            </button>
          ))}
        </div>

        <div className="cs__user">
          <span className="cs__avatar">{(seeker?.name || "С")[0].toUpperCase()}</span>
          <span className="cs__user-name">{seeker?.name || "мандрівник"}</span>
          <button className="cs__signout" onClick={onSignOut} title="Покинути коло">⎋</button>
        </div>
      </aside>

      {/* ---- main ---- */}
      <main className="cm">
        <header className="cm__head">
          {!sidebar && <button className="cm__expand" onClick={() => setSidebar(true)}>☰</button>}
          <div className="cm__head-title">
            <span className="cm__head-glyph">{activeDisc.glyph}</span>
            <div>
              <div className="cm__head-name">{activeDisc.name}</div>
              <div className="cm__head-lat">{activeDisc.lat}</div>
            </div>
          </div>
          <div className="cm__head-status"><i /> Завіса тонка</div>
        </header>

        <div className="cm__scroll" ref={scrollRef}>
          <div className="cm__thread">
            {msgs.map(m => (
              <Message key={m.id} msg={m} speed={speed} onTick={scrollDown} />
            ))}
            {(busy || loadingThread) && !msgs.some(m => m.typing || m.streaming) && (
              <div className="msg msg--agent">
                <div className="msg__avatar"><SigilMark size={32} live /></div>
                <div className="msg__col">
                  <div className="msg__who">Azoth AI</div>
                  <div className="cm__divining"><i /><i /><i /> вдивляюся…</div>
                </div>
              </div>
            )}
          </div>
        </div>

        <div className="cm__compose-wrap">
          {msgs.length <= 1 && (
            <div className="cm__suggest">
              {(SUGGESTIONS[active] || []).map((s, i) => (
                <button key={i} className="cm__chip" onClick={() => send(s)}>{s}</button>
              ))}
            </div>
          )}
          <div className="cm__compose">
            <textarea ref={taRef} rows={1} value={input}
              onChange={grow} onKeyDown={onKey}
              placeholder={`Звернися до Azoth AI · ${activeDisc.name.toLowerCase()}…`} />
            <button className="cm__send" disabled={!input.trim() || busy} onClick={() => send()}>
              <span>✦</span>
            </button>
          </div>
          <div className="cm__fineprint">Azoth AI допомагає практику бачити структуру, джерела й ризики. Рішення сесії лишаються за тобою.</div>
        </div>
      </main>
    </div>
  );
}
Object.assign(window, { Chat });
