/*
  agent-chat.tsx
  spboucher.ai Web
  Author: Simon-Pierre Boucher
  Mail: contact@spboucher.ai
*/

"use client";

import { useCallback, useEffect, useRef, useState } from "react";
import { AnimatePresence, motion, useReducedMotion } from "framer-motion";
import { ArrowUp, Minus, Sparkles, X } from "lucide-react";

interface ChatMessage {
  role: "user" | "assistant";
  content: string;
}

const SUGGESTIONS = [
  "Who is Simon-Pierre Boucher?",
  "What does his research focus on?",
  "Tell me about the Zyquo apps",
  "Qu'est-ce que HF Market Data ?",
];

const GREETING =
  "Hi — I'm SPB Agent, Simon-Pierre Boucher's AI assistant. Ask me anything about his research, teaching, or the software he builds. Je réponds aussi en français.";

/** Floating chat bubble + panel, streaming answers from /api/agent. */
export function AgentChat() {
  const [open, setOpen] = useState(false);
  const [messages, setMessages] = useState<ChatMessage[]>([]);
  const [input, setInput] = useState("");
  const [streaming, setStreaming] = useState(false);
  const scrollRef = useRef<HTMLDivElement>(null);
  const inputRef = useRef<HTMLTextAreaElement>(null);
  const abortRef = useRef<AbortController | null>(null);
  const reducedMotion = useReducedMotion();

  useEffect(() => {
    const el = scrollRef.current;
    if (el) el.scrollTop = el.scrollHeight;
  }, [messages, open]);

  useEffect(() => {
    if (open) inputRef.current?.focus();
  }, [open]);

  useEffect(() => {
    const onKey = (e: KeyboardEvent) => {
      if (e.key === "Escape") setOpen(false);
    };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, []);

  const send = useCallback(
    async (text: string) => {
      const question = text.trim();
      if (!question || streaming) return;

      const history: ChatMessage[] = [
        ...messages,
        { role: "user", content: question },
      ];
      setMessages([...history, { role: "assistant", content: "" }]);
      setInput("");
      setStreaming(true);

      const controller = new AbortController();
      abortRef.current = controller;

      try {
        const res = await fetch("/api/agent", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ messages: history }),
          signal: controller.signal,
        });

        if (!res.ok || !res.body) {
          throw new Error(`HTTP ${res.status}`);
        }

        const reader = res.body.getReader();
        const decoder = new TextDecoder();
        let answer = "";
        for (;;) {
          const { done, value } = await reader.read();
          if (done) break;
          answer += decoder.decode(value, { stream: true });
          const snapshot = answer;
          setMessages([
            ...history,
            { role: "assistant", content: snapshot },
          ]);
        }
      } catch (err) {
        if (!(err instanceof DOMException && err.name === "AbortError")) {
          setMessages([
            ...history,
            {
              role: "assistant",
              content:
                "Sorry — I couldn't reach the agent right now. Please try again in a moment, or email contact@spboucher.ai.",
            },
          ]);
        }
      } finally {
        setStreaming(false);
        abortRef.current = null;
      }
    },
    [messages, streaming],
  );

  const onSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    void send(input);
  };

  return (
    <>
      {/* Panel */}
      <AnimatePresence>
        {open && (
          <motion.div
            initial={reducedMotion ? false : { opacity: 0, y: 16, scale: 0.98 }}
            animate={{ opacity: 1, y: 0, scale: 1 }}
            exit={
              reducedMotion
                ? { opacity: 0 }
                : { opacity: 0, y: 16, scale: 0.98 }
            }
            transition={{ duration: 0.18, ease: "easeOut" }}
            className="fixed inset-x-3 bottom-3 z-50 flex max-h-[min(40rem,calc(100dvh-1.5rem))] flex-col overflow-hidden rounded-[0.9rem] border-2 border-foreground bg-background shadow-[var(--shadow-lift)] sm:inset-x-auto sm:right-5 sm:bottom-5 sm:w-[24rem]"
            role="dialog"
            aria-label="SPB Agent chat"
          >
            {/* Header */}
            <div className="flex items-center justify-between border-b-2 border-foreground bg-card/70 px-4 py-3">
              <div className="flex items-center gap-3">
                <div
                  aria-hidden="true"
                  className="flex h-9 w-9 items-center justify-center rounded-[0.6rem] bg-foreground font-mono text-[0.6rem] font-semibold tracking-[0.08em] text-background"
                >
                  SPB
                </div>
                <div>
                  <p className="font-display text-base font-semibold leading-tight tracking-tight">
                    SPB Agent
                  </p>
                  <p className="font-mono text-[0.58rem] uppercase tracking-[0.14em] text-muted-foreground">
                    <span
                      aria-hidden="true"
                      className="mr-1 inline-block h-1.5 w-1.5 rounded-full bg-emerald-500 align-middle"
                    />
                    Claude Haiku 4.5 · live
                  </p>
                </div>
              </div>
              <button
                type="button"
                onClick={() => setOpen(false)}
                aria-label="Close chat"
                className="flex h-8 w-8 items-center justify-center rounded-[0.5rem] border border-border text-muted-foreground transition-colors hover:border-foreground hover:text-foreground"
              >
                <Minus className="h-4 w-4" aria-hidden="true" />
              </button>
            </div>

            {/* Messages */}
            <div
              ref={scrollRef}
              className="flex-1 space-y-4 overflow-y-auto px-4 py-4"
            >
              <Bubble role="assistant" content={GREETING} />
              {messages.map((m, i) => (
                <Bubble
                  key={i}
                  role={m.role}
                  content={m.content}
                  pending={
                    streaming &&
                    i === messages.length - 1 &&
                    m.role === "assistant" &&
                    m.content.length === 0
                  }
                />
              ))}

              {messages.length === 0 && (
                <div className="space-y-2 pt-1">
                  <p className="font-mono text-[0.58rem] uppercase tracking-[0.14em] text-muted-foreground">
                    Try asking
                  </p>
                  <div className="flex flex-wrap gap-2">
                    {SUGGESTIONS.map((s) => (
                      <button
                        key={s}
                        type="button"
                        onClick={() => void send(s)}
                        className="rounded-full border border-border bg-card px-3 py-1.5 text-left text-xs text-foreground/85 transition-colors hover:border-foreground"
                      >
                        {s}
                      </button>
                    ))}
                  </div>
                </div>
              )}
            </div>

            {/* Input */}
            <form
              onSubmit={onSubmit}
              className="flex items-end gap-2 border-t-2 border-foreground bg-card/70 px-3 py-3"
            >
              <textarea
                ref={inputRef}
                value={input}
                onChange={(e) => setInput(e.target.value)}
                onKeyDown={(e) => {
                  if (e.key === "Enter" && !e.shiftKey) {
                    e.preventDefault();
                    void send(input);
                  }
                }}
                rows={1}
                placeholder="Ask about Simon-Pierre…"
                aria-label="Message for SPB Agent"
                className="max-h-28 min-h-[2.5rem] flex-1 resize-none rounded-[0.6rem] border border-border bg-background px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground/70 focus:border-foreground focus:outline-none"
              />
              <button
                type="submit"
                disabled={streaming || input.trim().length === 0}
                aria-label="Send message"
                className="flex h-10 w-10 shrink-0 items-center justify-center rounded-[0.6rem] bg-foreground text-background transition-opacity disabled:opacity-35"
              >
                <ArrowUp className="h-4 w-4" aria-hidden="true" />
              </button>
            </form>
          </motion.div>
        )}
      </AnimatePresence>

      {/* Bubble */}
      <AnimatePresence>
        {!open && (
          <motion.button
            initial={reducedMotion ? false : { opacity: 0, scale: 0.85 }}
            animate={{ opacity: 1, scale: 1 }}
            exit={reducedMotion ? { opacity: 0 } : { opacity: 0, scale: 0.85 }}
            transition={{ duration: 0.15 }}
            type="button"
            onClick={() => setOpen(true)}
            aria-label="Open SPB Agent chat"
            className="group fixed right-5 bottom-5 z-50 flex items-center gap-2.5 rounded-full border-2 border-foreground bg-foreground py-2.5 pr-5 pl-3.5 text-background shadow-[var(--shadow-lift)] transition-transform hover:-translate-y-0.5"
          >
            <Sparkles
              className="h-4 w-4 text-background/90 transition-transform group-hover:rotate-12"
              aria-hidden="true"
            />
            <span className="font-mono text-[0.66rem] font-semibold uppercase tracking-[0.14em]">
              Ask SPB Agent
            </span>
          </motion.button>
        )}
      </AnimatePresence>
    </>
  );
}

function Bubble({
  role,
  content,
  pending,
}: {
  role: "user" | "assistant";
  content: string;
  pending?: boolean;
}) {
  if (role === "user") {
    return (
      <div className="flex justify-end">
        <div className="max-w-[85%] rounded-[0.8rem] rounded-br-[0.2rem] bg-foreground px-3.5 py-2.5 text-sm leading-relaxed whitespace-pre-wrap text-background">
          {content}
        </div>
      </div>
    );
  }
  return (
    <div className="flex justify-start">
      <div className="max-w-[90%] rounded-[0.8rem] rounded-bl-[0.2rem] border border-border bg-card px-3.5 py-2.5 text-sm leading-relaxed whitespace-pre-wrap text-foreground/90 shadow-[var(--shadow-soft)]">
        {pending ? (
          <span
            className="inline-flex items-center gap-1"
            aria-label="SPB Agent is typing"
          >
            <Dot delay="0s" />
            <Dot delay="0.15s" />
            <Dot delay="0.3s" />
          </span>
        ) : (
          content
        )}
      </div>
    </div>
  );
}

function Dot({ delay }: { delay: string }) {
  return (
    <span
      aria-hidden="true"
      className="h-1.5 w-1.5 animate-bounce rounded-full bg-muted-foreground/70"
      style={{ animationDelay: delay }}
    />
  );
}
