/**
 * ChatWidget — floating chat bubble + expandable conversation panel.
 *
 * Renders a fixed-position bubble (bottom-right). Clicking opens a slide-up
 * panel with message history, streaming responses, citation rendering,
 * suggested questions, and a typing indicator. Scoped to a specific creator.
 */

import React, { useState, useRef, useCallback, useEffect, useMemo } from "react";
import { Link } from "react-router-dom";
import { streamChat, type ChatSource } from "../api/chat";
import { parseChatCitations } from "../utils/chatCitations";
import { formatTime } from "../utils/formatTime";
import styles from "./ChatWidget.module.css";

interface Technique {
  title: string;
  slug: string;
  topic_category: string;
}

interface ChatWidgetProps {
  creatorName: string;
  techniques: Technique[];
}

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

/** Generate 3 suggested questions from the creator's techniques. */
function buildSuggestions(creatorName: string, techniques: Technique[]): string[] {
  if (techniques.length === 0) {
    return [`What techniques does ${creatorName} use?`];
  }

  const suggestions: string[] = [];

  // First technique by name
  if (techniques[0]) {
    suggestions.push(`How does ${creatorName} approach ${techniques[0].title.toLowerCase()}?`);
  }

  // Category-based question if there are multiple categories
  const categories = [...new Set(techniques.map((t) => t.topic_category).filter(Boolean))];
  if (categories.length > 1 && categories[0]) {
    suggestions.push(`What ${categories[0].toLowerCase()} techniques does ${creatorName} teach?`);
  } else if (techniques[1]) {
    suggestions.push(`Tell me about ${techniques[1].title.toLowerCase()}`);
  }

  // General question
  suggestions.push(`What makes ${creatorName}'s production style unique?`);

  return suggestions.slice(0, 3);
}

/** Map personality weight to a human-readable tier label. */
function getTierLabel(weight: number): string {
  if (weight < 0.2) return "Encyclopedic";
  if (weight < 0.4) return "Subtle Reference";
  if (weight < 0.6) return "Creator Tone";
  if (weight < 0.8) return "Creator Voice";
  return "Full Embodiment";
}

export default function ChatWidget({ creatorName, techniques }: ChatWidgetProps) {
  const [open, setOpen] = useState(false);
  const [messages, setMessages] = useState<Message[]>([]);
  const [input, setInput] = useState("");
  const [streaming, setStreaming] = useState(false);
  const [conversationId, setConversationId] = useState<string | undefined>(undefined);
  const [personalityWeight, setPersonalityWeight] = useState(0);

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

  const suggestions = useMemo(
    () => buildSuggestions(creatorName, techniques),
    [creatorName, techniques],
  );

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

  // Focus input when panel opens
  useEffect(() => {
    if (open) {
      // Small delay to let animation settle
      const t = setTimeout(() => inputRef.current?.focus(), 100);
      return () => clearTimeout(t);
    }
  }, [open]);

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

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

  // Escape key closes panel
  useEffect(() => {
    if (!open) return;
    const handleKey = (e: KeyboardEvent) => {
      if (e.key === "Escape") handleClose();
    };
    document.addEventListener("keydown", handleKey);
    return () => document.removeEventListener("keydown", handleKey);
  }, [open, handleClose]);

  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);
          },
        },
        creatorName,
        cid,
        personalityWeight,
      );
    },
    [messages, streaming, creatorName, conversationId, personalityWeight],
  );

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

  const handleSuggestion = (q: string) => {
    sendMessage(q);
  };

  if (!open) {
    return (
      <button
        className={styles.bubble}
        onClick={() => setOpen(true)}
        aria-label={`Chat with ${creatorName}`}
        title={`Ask about ${creatorName}'s techniques`}
      >
        💬
      </button>
    );
  }

  const showSuggestions = messages.length === 0;

  return (
    <>
      {/* Keep bubble underneath for z-index stacking, hidden visually */}
      <div className={styles.panel}>
        {/* Header */}
        <div className={styles.header}>
          <div>
            <span className={styles.headerLabel}>Chat with</span>
            <h3 className={styles.headerTitle}>{creatorName}</h3>
          </div>
          <button
            className={styles.closeBtn}
            onClick={handleClose}
            aria-label="Close chat"
          >
            ✕
          </button>
        </div>

        {/* Personality slider */}
        <div className={styles.sliderSection}>
          <div className={styles.sliderRow}>
            <span className={styles.sliderLabel}>Encyclopedic</span>
            <input
              type="range"
              className={styles.slider}
              min={0}
              max={1}
              step={0.1}
              value={personalityWeight}
              onChange={(e) => setPersonalityWeight(parseFloat(e.target.value))}
              aria-label="Personality weight"
              style={{ '--slider-fill': `${personalityWeight * 100}%` } as React.CSSProperties}
            />
            <span className={styles.sliderLabel}>Creator Voice</span>
          </div>
          <div className={styles.tierInfo}>
            <span className={styles.tierLabel}>{getTierLabel(personalityWeight)}</span>
            <span className={styles.tierValue}>{personalityWeight.toFixed(1)}</span>
          </div>
        </div>

        {/* Messages */}
        <div className={styles.messages}>
          {showSuggestions && (
            <div className={styles.suggestions}>
              <p className={styles.suggestionsLabel}>Suggested questions</p>
              {suggestions.map((q) => (
                <button
                  key={q}
                  className={styles.suggestionBtn}
                  onClick={() => handleSuggestion(q)}
                >
                  {q}
                </button>
              ))}
            </div>
          )}

          {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.errorMsg}>{msg.error}</div>
                ) : (
                  <>
                    {parseChatCitations(msg.text, msg.sources, styles)}
                    {!msg.done && <span className={styles.cursor} />}
                    {msg.done && msg.sources.length > 0 && (
                      <div className={styles.sources}>
                        <p className={styles.sourcesLabel}>Sources</p>
                        <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.sourceNum}>{src.number}</span>
                                <div className={styles.sourceContent}>
                                  <Link to={href} className={styles.sourceLink}>
                                    {src.title}
                                    {src.section_heading ? ` — ${src.section_heading}` : ""}
                                  </Link>
                                  {src.start_time != null && src.source_video_id && (
                                    <Link
                                      to={`/watch/${src.source_video_id}?t=${Math.floor(src.start_time)}`}
                                      className={styles.timestampBadge}
                                      title="Jump to video timestamp"
                                    >
                                      ▶ {formatTime(src.start_time)}
                                      {src.end_time != null && `–${formatTime(src.end_time)}`}
                                    </Link>
                                  )}
                                  {src.video_filename && (
                                    <span className={styles.videoMeta}>{src.video_filename}</span>
                                  )}
                                </div>
                              </li>
                            );
                          })}
                        </ol>
                      </div>
                    )}
                  </>
                )}
              </div>
            );
          })}

          {/* Typing indicator during loading before first token */}
          {streaming &&
            messages.length > 0 &&
            messages[messages.length - 1]?.role === "assistant" &&
            messages[messages.length - 1]?.text === "" &&
            !messages[messages.length - 1]?.error && (
              <div className={styles.typing}>
                <span className={styles.typingDot} />
                <span className={styles.typingDot} />
                <span className={styles.typingDot} />
              </div>
            )}

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

        {/* Input */}
        <form className={styles.inputRow} onSubmit={handleSubmit}>
          <input
            ref={inputRef}
            type="text"
            className={styles.input}
            value={input}
            onChange={(e) => setInput(e.target.value)}
            placeholder="Ask a question…"
            disabled={streaming}
          />
          <button
            type="submit"
            className={styles.sendBtn}
            disabled={!input.trim() || streaming}
          >
            Send
          </button>
        </form>
      </div>
    </>
  );
}
