import { createFileRoute } from "@tanstack/react-router";
import { useState, useRef, useEffect } from "react";
import { motion, AnimatePresence } from "framer-motion";
import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm";
import {
  MessageSquare,
  Send,
  Sparkles,
  Plus,
  StopCircle,
  RefreshCcw,
  Copy,
  Check,
  User,
} from "lucide-react";
import { Button } from "@/components/ui/button";
import { Textarea } from "@/components/ui/textarea";
import { ScrollArea } from "@/components/ui/scroll-area";
import { MOCK_CHATS } from "@/lib/mock-data";
import { SUGGESTED_PROMPTS } from "@/constants";
import { chatsService } from "@/services";
import { cn } from "@/lib/utils";
import type { ChatMessage, ChatThread } from "@/types";
import { toast } from "sonner";
import { formatDistanceToNow } from "date-fns";

export const Route = createFileRoute("/_app/chat")({
  head: () => ({ meta: [{ title: "AI Chat — Study Unique" }] }),
  component: ChatPage,
});

function ChatPage() {
  const [threads, setThreads] = useState<ChatThread[]>(() =>
    MOCK_CHATS.map((c) => ({ ...c, messages: [...c.messages] })),
  );
  const [activeId, setActiveId] = useState(threads[0]?.id ?? "");
  const [input, setInput] = useState("");
  const [streaming, setStreaming] = useState(false);
  const abortRef = useRef<AbortController | null>(null);
  const scrollRef = useRef<HTMLDivElement>(null);

  const active = threads.find((t) => t.id === activeId);

  useEffect(() => {
    scrollRef.current?.scrollTo({ top: scrollRef.current.scrollHeight, behavior: "smooth" });
  }, [active?.messages.length, streaming]);

  const newThread = () => {
    const id = "c" + Math.random().toString(36).slice(2, 7);
    const t: ChatThread = {
      id,
      title: "New conversation",
      updatedAt: new Date().toISOString(),
      messages: [],
    };
    setThreads((p) => [t, ...p]);
    setActiveId(id);
  };

  const send = async (text: string) => {
    if (!text.trim() || !active) return;
    const userMsg: ChatMessage = {
      id: "m" + Math.random().toString(36).slice(2),
      role: "user",
      content: text,
      createdAt: new Date().toISOString(),
    };
    const aiMsg: ChatMessage = {
      id: "m" + Math.random().toString(36).slice(2),
      role: "assistant",
      content: "",
      createdAt: new Date().toISOString(),
    };
    setThreads((p) =>
      p.map((t) =>
        t.id === activeId
          ? {
              ...t,
              title: t.messages.length === 0 ? text.slice(0, 36) : t.title,
              messages: [...t.messages, userMsg, aiMsg],
              updatedAt: new Date().toISOString(),
            }
          : t,
      ),
    );
    setInput("");
    setStreaming(true);
    const ctrl = new AbortController();
    abortRef.current = ctrl;
    try {
      await chatsService.streamResponse(
        text,
        (tok) => {
          setThreads((p) =>
            p.map((t) =>
              t.id === activeId
                ? {
                    ...t,
                    messages: t.messages.map((m) =>
                      m.id === aiMsg.id ? { ...m, content: m.content + tok } : m,
                    ),
                  }
                : t,
            ),
          );
        },
        ctrl.signal,
      );
    } finally {
      setStreaming(false);
      abortRef.current = null;
    }
  };

  const stop = () => abortRef.current?.abort();

  const regenerate = () => {
    if (!active || active.messages.length < 2) return;
    const lastUser = [...active.messages].reverse().find((m) => m.role === "user");
    if (!lastUser) return;
    setThreads((p) =>
      p.map((t) =>
        t.id === activeId
          ? { ...t, messages: t.messages.slice(0, -1) }
          : t,
      ),
    );
    send(lastUser.content);
  };

  return (
    <div className="grid h-[calc(100vh-4rem)] grid-cols-1 md:grid-cols-[260px_1fr]">
      {/* Thread sidebar */}
      <aside className="hidden flex-col border-r border-border bg-sidebar md:flex">
        <div className="p-3">
          <Button
            onClick={newThread}
            className="w-full gradient-brand text-primary-foreground hover:opacity-90"
          >
            <Plus className="mr-1.5 h-4 w-4" /> New chat
          </Button>
        </div>
        <ScrollArea className="flex-1 px-2 pb-3">
          {threads.map((t) => (
            <button
              key={t.id}
              onClick={() => setActiveId(t.id)}
              className={cn(
                "mb-1 flex w-full flex-col items-start rounded-md px-3 py-2 text-left text-xs transition-colors",
                activeId === t.id
                  ? "bg-sidebar-accent text-foreground"
                  : "text-muted-foreground hover:bg-sidebar-accent/60 hover:text-foreground",
              )}
            >
              <span className="line-clamp-1 text-sm font-medium">{t.title}</span>
              <span className="mt-0.5 text-[10px]">
                {formatDistanceToNow(new Date(t.updatedAt), { addSuffix: true })}
              </span>
            </button>
          ))}
        </ScrollArea>
      </aside>

      {/* Conversation */}
      <section className="flex min-w-0 flex-col">
        <div className="flex h-12 items-center justify-between border-b border-border px-4">
          <div className="flex items-center gap-2 text-sm">
            <Sparkles className="h-4 w-4 text-primary" />
            <span className="font-medium">{active?.title ?? "New conversation"}</span>
          </div>
          <div className="text-xs text-muted-foreground">Powered by Study Unique AI</div>
        </div>

        <div ref={scrollRef} className="flex-1 overflow-y-auto scrollbar-thin">
          {!active || active.messages.length === 0 ? (
            <EmptyChat onPick={(p) => send(p)} />
          ) : (
            <div className="mx-auto w-full max-w-3xl space-y-6 px-4 py-6">
              <AnimatePresence initial={false}>
                {active.messages.map((m, i) => (
                  <MessageBubble
                    key={m.id}
                    message={m}
                    isLast={i === active.messages.length - 1}
                    streaming={streaming && i === active.messages.length - 1 && m.role === "assistant"}
                  />
                ))}
              </AnimatePresence>
            </div>
          )}
        </div>

        {/* Composer */}
        <div className="border-t border-border bg-background/80 p-4 backdrop-blur-xl">
          <div className="mx-auto max-w-3xl">
            {streaming && (
              <div className="mb-2 flex justify-center">
                <Button size="sm" variant="outline" onClick={stop}>
                  <StopCircle className="mr-1.5 h-4 w-4" /> Stop generating
                </Button>
              </div>
            )}
            {active && active.messages.length > 0 && !streaming && (
              <div className="mb-2 flex justify-center">
                <Button size="sm" variant="ghost" onClick={regenerate}>
                  <RefreshCcw className="mr-1.5 h-3.5 w-3.5" /> Regenerate
                </Button>
              </div>
            )}
            <div className="flex items-end gap-2 rounded-2xl border border-border bg-card p-2 focus-within:border-primary/50">
              <Textarea
                value={input}
                onChange={(e) => setInput(e.target.value)}
                onKeyDown={(e) => {
                  if (e.key === "Enter" && !e.shiftKey) {
                    e.preventDefault();
                    send(input);
                  }
                }}
                placeholder="Ask anything about your documents…"
                className="min-h-[40px] resize-none border-0 bg-transparent focus-visible:ring-0"
                rows={1}
              />
              <Button
                onClick={() => send(input)}
                disabled={!input.trim() || streaming}
                size="icon"
                className="gradient-brand text-primary-foreground hover:opacity-90"
              >
                <Send className="h-4 w-4" />
              </Button>
            </div>
            <p className="mt-2 text-center text-[10px] text-muted-foreground">
              Press <kbd className="rounded border border-border bg-muted px-1">Enter</kbd> to send,
              <kbd className="ml-1 rounded border border-border bg-muted px-1">Shift+Enter</kbd> for newline
            </p>
          </div>
        </div>
      </section>
    </div>
  );
}

function EmptyChat({ onPick }: { onPick: (p: string) => void }) {
  return (
    <div className="mx-auto flex h-full max-w-3xl flex-col items-center justify-center px-4 py-10 text-center">
      <div className="flex h-14 w-14 items-center justify-center rounded-2xl gradient-brand shadow-2xl shadow-primary/40">
        <MessageSquare className="h-6 w-6 text-primary-foreground" />
      </div>
      <h2 className="mt-5 text-2xl font-semibold tracking-tight">
        How can I help you <span className="gradient-text">study</span> today?
      </h2>
      <p className="mt-2 text-sm text-muted-foreground">
        Pick a prompt or write your own — I have access to all your documents.
      </p>
      <div className="mt-8 grid w-full gap-2 sm:grid-cols-2">
        {SUGGESTED_PROMPTS.map((p) => (
          <button
            key={p}
            onClick={() => onPick(p)}
            className="rounded-xl border border-border bg-card p-3 text-left text-sm transition-colors hover:border-primary/40 hover:bg-muted"
          >
            <Sparkles className="mr-1.5 inline h-3.5 w-3.5 text-primary" />
            {p}
          </button>
        ))}
      </div>
    </div>
  );
}

function MessageBubble({
  message,
  streaming,
}: {
  message: ChatMessage;
  isLast: boolean;
  streaming: boolean;
}) {
  const [copied, setCopied] = useState(false);
  const isUser = message.role === "user";

  const copy = () => {
    navigator.clipboard.writeText(message.content);
    setCopied(true);
    toast.success("Copied to clipboard");
    setTimeout(() => setCopied(false), 1500);
  };

  return (
    <motion.div
      initial={{ opacity: 0, y: 6 }}
      animate={{ opacity: 1, y: 0 }}
      className={cn("flex gap-3", isUser && "justify-end")}
    >
      {!isUser && (
        <div className="flex h-8 w-8 shrink-0 items-center justify-center rounded-lg gradient-brand">
          <Sparkles className="h-4 w-4 text-primary-foreground" />
        </div>
      )}
      <div className={cn("min-w-0 max-w-[85%]", isUser && "order-2")}>
        {isUser ? (
          <div className="rounded-2xl bg-primary px-4 py-2.5 text-sm text-primary-foreground">
            {message.content}
          </div>
        ) : (
          <div className="group rounded-2xl text-sm leading-relaxed text-foreground/95">
            <div className="prose prose-invert prose-sm max-w-none prose-pre:rounded-lg prose-pre:border prose-pre:border-border prose-pre:bg-muted/50">
              <ReactMarkdown remarkPlugins={[remarkGfm]}>
                {message.content || (streaming ? "…" : "")}
              </ReactMarkdown>
              {streaming && <span className="blink-cursor" />}
            </div>
            {!streaming && message.content && (
              <div className="mt-2 flex items-center gap-1 opacity-0 transition-opacity group-hover:opacity-100">
                <Button size="sm" variant="ghost" className="h-7" onClick={copy}>
                  {copied ? <Check className="h-3.5 w-3.5" /> : <Copy className="h-3.5 w-3.5" />}
                  <span className="ml-1 text-xs">{copied ? "Copied" : "Copy"}</span>
                </Button>
              </div>
            )}
          </div>
        )}
      </div>
      {isUser && (
        <div className="order-3 flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-muted">
          <User className="h-4 w-4" />
        </div>
      )}
    </motion.div>
  );
}
