/**
 * ChatPage — multi-turn chat with streamed encyclopedic responses and citations.
 *
 * Uses SSE streaming via api/chat.ts to display tokens in real-time.
 * Citation markers [N] in the streamed text become superscript links to
 * the source list rendered below the response. Conversations are threaded
 * via conversation_id backed by Redis history on the server.
 */

import React, { useState, useRef, useCallback, useEffect } from "react";
import { Link } from "react-router-dom";
import { streamChat, type ChatSource } from "../api/chat";
import { useDocumentTitle } from "../hooks/useDocumentTitle";
import styles from "./ChatPage.module.css";

// Matches [1], [2,3], [1,2,3] etc. — same regex as utils/citations.tsx
const CITATION_RE = /\[(\d+(?:,\s*\d+)*)\]/g;

interface Message {
  role: "user" | "assistant";
  text: string;
  sources: ChatSource[];
  done: boolean;
  error?: string;
}

/**
 * Parse text containing [N] markers into React nodes with citation links.
 */
function parseChatCitations(
  text: string,
  sources: ChatSource[],
): React.ReactNode[] {
  const nodes: React.ReactNode[] = [];
  let lastIndex = 0;

  for (const match of text.matchAll(CITATION_RE)) {
    const matchStart = match.index ?? 0;
    if (matchStart > lastIndex) {
      nodes.push(text.slice(lastIndex, matchStart));
    }

    const rawGroup = match[1];
    if (!rawGroup) continue;
    const indices = rawGroup.split(",").map((s) => parseInt(s.trim(), 10));
    const links: React.ReactNode[] = [];

    for (let i = 0; i < indices.length; i++) {
      const idx = indices[i]!;
      const source = sources[idx - 1]; // 1-based
      if (source) {
        if (i > 0) links.push(", ");
        const href = source.section_anchor
          ? `/techniques/${source.slug}#${source.section_anchor}`
          : `/techniques/${source.slug}`;
        links.push(
          <Link
            key={`${matchStart}-${idx}`}
            to={href}
            className={styles.citationLink}
            title={`${source.title}${source.section_heading ? ` — ${source.section_heading}` : ""}`}
          >
            {idx}
          </Link>,
        );
      } else {
        if (i > 0) links.push(", ");
        links.push(String(idx));
      }
    }

    nodes.push(
      <sup key={`cite-${matchStart}`} className={styles.citationGroup}>
        [{links}]
      </sup>,
    );

    lastIndex = matchStart + match[0].length;
  }

  if (lastIndex < text.length) {
    nodes.push(text.slice(lastIndex));
  }

  return nodes.length > 0 ? nodes : [text];
}

export default function ChatPage() {
  useDocumentTitle("Chat — Chrysopedia");

  const [messages, setMessages] = useState<Message[]>([]);
  const [input, setInput] = useState("");
  const [streaming, setStreaming] = useState(false);
  const [conversationId, setConversationId] = useState<string | undefined>(
    undefined,
  );

  const abortRef = useRef<AbortController | null>(null);
  const inputRef = useRef<HTMLInputElement>(null);
  const messagesEndRef = useRef<HTMLDivElement>(null);

  // Cleanup on unmount
  useEffect(() => {
    return () => {
      abortRef.current?.abort();
    };
  }, []);

  // Scroll to bottom when messages update
  useEffect(() => {
    messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
  }, [messages]);

  const sendMessage = useCallback(
    (text: string) => {
      const q = text.trim();
      if (!q || streaming) return;

      abortRef.current?.abort();

      // Generate conversation_id on first message
      const cid = conversationId ?? crypto.randomUUID();
      if (!conversationId) setConversationId(cid);

      const userMsg: Message = {
        role: "user",
        text: q,
        sources: [],
        done: true,
      };
      const assistantMsg: Message = {
        role: "assistant",
        text: "",
        sources: [],
        done: false,
      };
      const newMessages = [...messages, userMsg, assistantMsg];
      setMessages(newMessages);
      setInput("");
      setStreaming(true);

      const assistantIndex = newMessages.length - 1;

      abortRef.current = streamChat(
        q,
        {
          onSources: (sources) => {
            setMessages((prev) =>
              prev.map((m, i) =>
                i === assistantIndex ? { ...m, sources } : m,
              ),
            );
          },
          onToken: (token) => {
            setMessages((prev) =>
              prev.map((m, i) =>
                i === assistantIndex ? { ...m, text: m.text + token } : m,
              ),
            );
          },
          onDone: (meta) => {
            setConversationId(meta.conversation_id);
            setMessages((prev) =>
              prev.map((m, i) =>
                i === assistantIndex ? { ...m, done: true } : m,
              ),
            );
            setStreaming(false);
          },
          onError: (msg) => {
            setMessages((prev) =>
              prev.map((m, i) =>
                i === assistantIndex ? { ...m, done: true, error: msg } : m,
              ),
            );
            setStreaming(false);
          },
        },
        undefined, // no creator scope on the main chat page
        cid,
      );
    },
    [messages, streaming, conversationId],
  );

  const handleSubmit = useCallback(
    (e: React.FormEvent) => {
      e.preventDefault();
      sendMessage(input);
    },
    [input, sendMessage],
  );

  const handleNewConversation = useCallback(() => {
    abortRef.current?.abort();
    setMessages([]);
    setInput("");
    setStreaming(false);
    setConversationId(undefined);
    inputRef.current?.focus();
  }, []);

  const hasMessages = messages.length > 0;

  return (
    <div className={styles.container}>
      <div className={styles.headerRow}>
        <h1 className={styles.title}>Ask Chrysopedia</h1>
        {hasMessages && (
          <button
            className={styles.newConversationBtn}
            onClick={handleNewConversation}
            title="Start a new conversation"
          >
            + New conversation
          </button>
        )}
      </div>

      {!hasMessages && !streaming && (
        <div className={styles.placeholder}>
          <span className={styles.placeholderIcon}>💬</span>
          <p>Ask any question about music production techniques.</p>
          <p>Responses include citations linking to source pages and videos.</p>
        </div>
      )}

      {hasMessages && (
        <div className={styles.messagesArea}>
          {messages.map((msg, i) => {
            if (msg.role === "user") {
              return (
                <div key={i} className={styles.userMsg}>
                  {msg.text}
                </div>
              );
            }

            // Assistant message
            return (
              <div key={i} className={styles.assistantMsg}>
                {msg.error ? (
                  <div className={styles.error}>{msg.error}</div>
                ) : (
                  <>
                    <div className={styles.responseText}>
                      {parseChatCitations(msg.text, msg.sources)}
                      {!msg.done && <span className={styles.cursor} />}
                    </div>
                    {msg.done && msg.sources.length > 0 && (
                      <div className={styles.sourcesSection}>
                        <h3 className={styles.sourcesTitle}>Sources</h3>
                        <ol className={styles.sourceList}>
                          {msg.sources.map((src) => {
                            const href = src.section_anchor
                              ? `/techniques/${src.slug}#${src.section_anchor}`
                              : `/techniques/${src.slug}`;
                            return (
                              <li
                                key={src.number}
                                className={styles.sourceItem}
                              >
                                <span className={styles.sourceNumber}>
                                  {src.number}
                                </span>
                                <div>
                                  <Link
                                    to={href}
                                    className={styles.sourceLink}
                                  >
                                    {src.title}
                                    {src.section_heading
                                      ? ` — ${src.section_heading}`
                                      : ""}
                                  </Link>
                                  {src.creator_name && (
                                    <span className={styles.sourceMeta}>
                                      {" "}
                                      · {src.creator_name}
                                    </span>
                                  )}
                                </div>
                              </li>
                            );
                          })}
                        </ol>
                      </div>
                    )}
                  </>
                )}
              </div>
            );
          })}

          {/* Typing indicator — before first token arrives */}
          {streaming &&
            messages.length > 0 &&
            messages[messages.length - 1]?.role === "assistant" &&
            messages[messages.length - 1]?.text === "" &&
            !messages[messages.length - 1]?.error && (
              <div className={styles.loading}>
                <div className={styles.spinner} />
                Thinking…
              </div>
            )}

          <div ref={messagesEndRef} />
        </div>
      )}

      <form onSubmit={handleSubmit} className={styles.inputRow}>
        <input
          ref={inputRef}
          type="text"
          className={styles.input}
          value={input}
          onChange={(e) => setInput(e.target.value)}
          placeholder="Ask about music production techniques…"
          disabled={streaming}
          autoFocus
        />
        <button
          type="submit"
          className={styles.submitBtn}
          disabled={!input.trim() || streaming}
        >
          {streaming ? "Streaming…" : "Send"}
        </button>
      </form>
    </div>
  );
}
