"use client";
import { useState, useRef, useEffect } from "react";
import { FileText, Loader2, Plus, RefreshCw, Send, Trash2 } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Card, CardContent } from "@/components/ui/card";
import { PersonaSelector } from "@/components/persona-selector";
import { TemperatureSlider } from "@/components/temperature-slider";
import {
  createConversation,
  loadChatPreferences,
  loadChatStore,
  removeConversation,
  saveChatPreferences,
  saveChatStore,
  titleFromMessages,
  upsertConversation,
  type ChatConversation,
  type ChatMessage,
} from "@/lib/chat-storage";
import {
  DEFAULT_PERSONAS,
  DEFAULT_PERSONA_ID,
  fetchPersonas,
  type Persona,
} from "@/lib/personas";

const DEFAULT_TEMPERATURE = 0.4;

function latestThinkingPreview(reasoning: string) {
  const text = reasoning.trim();
  if (!text) return "";
  return text.length > 420 ? `...${text.slice(-420)}` : text;
}

function displayDate(value: string) {
  const date = new Date(value);
  if (Number.isNaN(date.getTime())) return "";
  return date.toLocaleString(undefined, {
    month: "short",
    day: "numeric",
    hour: "2-digit",
    minute: "2-digit",
  });
}

export function ChatInterface() {
  const [personas, setPersonas] = useState<Persona[]>(DEFAULT_PERSONAS);
  const [documents, setDocuments] = useState<string[] | null>(null);
  const [documentsError, setDocumentsError] = useState<string | null>(null);
  const [activePersonaId, setActivePersonaId] = useState(DEFAULT_PERSONA_ID);
  const [temperature, setTemperature] = useState(DEFAULT_TEMPERATURE);
  const [conversations, setConversations] = useState<ChatConversation[]>([]);
  const [activeId, setActiveId] = useState<string | null>(null);
  const [messages, setMessages] = useState<ChatMessage[]>([]);
  const [input, setInput] = useState("");
  const [busy, setBusy] = useState(false);
  const [hydrated, setHydrated] = useState(false);

  const scrollRef = useRef<HTMLDivElement>(null);
  const activeIdRef = useRef<string | null>(null);
  const personaIdRef = useRef(activePersonaId);
  const temperatureRef = useRef(temperature);

  const persona =
    personas.find((p) => p.id === activePersonaId) ??
    personas.find((p) => p.id === DEFAULT_PERSONA_ID) ??
    personas[0] ??
    DEFAULT_PERSONAS[0];

  useEffect(() => {
    const store = loadChatStore();
    const preferences = loadChatPreferences();
    const active = store.conversations.find((conversation) => conversation.id === store.activeId);
    const personaId = active?.personaId ?? preferences.personaId ?? DEFAULT_PERSONA_ID;
    const nextTemperature = active?.temperature ?? preferences.temperature ?? DEFAULT_TEMPERATURE;

    setConversations(store.conversations);
    setActiveId(store.activeId);
    setMessages(active?.messages ?? []);
    setActivePersonaId(personaId);
    setTemperature(nextTemperature);
    activeIdRef.current = store.activeId;
    personaIdRef.current = personaId;
    temperatureRef.current = nextTemperature;
    setHydrated(true);
  }, []);

  useEffect(() => {
    fetchPersonas().then((list) => {
      setPersonas(list);
    });
  }, []);

  async function loadDocuments() {
    setDocumentsError(null);
    try {
      const response = await fetch("/api/documents");
      if (!response.ok) throw new Error(await response.text());
      const data = await response.json();
      setDocuments(Array.isArray(data.files) ? data.files : []);
    } catch (error) {
      setDocuments([]);
      setDocumentsError((error as Error).message);
    }
  }

  useEffect(() => {
    loadDocuments();
  }, []);

  useEffect(() => {
    activeIdRef.current = activeId;
  }, [activeId]);

  useEffect(() => {
    personaIdRef.current = activePersonaId;
  }, [activePersonaId]);

  useEffect(() => {
    temperatureRef.current = temperature;
  }, [temperature]);

  useEffect(() => {
    if (hydrated) saveChatStore({ activeId, conversations });
  }, [activeId, conversations, hydrated]);

  useEffect(() => {
    scrollRef.current?.scrollTo({ top: scrollRef.current.scrollHeight });
  }, [messages, busy]);

  function ensureActiveConversation() {
    const existing = conversations.find((conversation) => conversation.id === activeIdRef.current);
    if (existing) return existing.id;

    const conversation = createConversation(personaIdRef.current, temperatureRef.current);
    activeIdRef.current = conversation.id;
    setActiveId(conversation.id);
    setConversations((prev) => upsertConversation(prev, conversation));
    return conversation.id;
  }

  function commitMessages(conversationId: string, nextMessages: ChatMessage[]) {
    setMessages(nextMessages);
    setConversations((prev) => {
      const existing =
        prev.find((conversation) => conversation.id === conversationId) ??
        createConversation(personaIdRef.current, temperatureRef.current);
      return upsertConversation(prev, {
        ...existing,
        id: conversationId,
        title: titleFromMessages(nextMessages),
        updatedAt: new Date().toISOString(),
        personaId: personaIdRef.current,
        temperature: temperatureRef.current,
        messages: nextMessages,
      });
    });
  }

  function updateActiveSettings(personaId: string, nextTemperature: number) {
    if (!activeIdRef.current) return;
    setConversations((prev) =>
      prev.map((conversation) =>
        conversation.id === activeIdRef.current
          ? {
              ...conversation,
              personaId,
              temperature: nextTemperature,
              updatedAt: new Date().toISOString(),
            }
          : conversation
      )
    );
  }

  function handlePersonaChange(nextPersona: Persona) {
    setActivePersonaId(nextPersona.id);
    personaIdRef.current = nextPersona.id;
    saveChatPreferences({ personaId: nextPersona.id, temperature: temperatureRef.current });
    updateActiveSettings(nextPersona.id, temperatureRef.current);
  }

  function handleTemperatureChange(nextTemperature: number) {
    setTemperature(nextTemperature);
    temperatureRef.current = nextTemperature;
    saveChatPreferences({ personaId: personaIdRef.current, temperature: nextTemperature });
    updateActiveSettings(personaIdRef.current, nextTemperature);
  }

  function newChat() {
    if (busy) return;
    activeIdRef.current = null;
    setActiveId(null);
    setMessages([]);
    setInput("");
    const preferences = loadChatPreferences();
    const personaId = preferences.personaId ?? personaIdRef.current;
    const nextTemperature = preferences.temperature ?? temperatureRef.current;
    setActivePersonaId(personaId);
    setTemperature(nextTemperature);
    personaIdRef.current = personaId;
    temperatureRef.current = nextTemperature;
  }

  function openConversation(conversation: ChatConversation) {
    if (busy) return;
    activeIdRef.current = conversation.id;
    setActiveId(conversation.id);
    setMessages(conversation.messages);
    setActivePersonaId(conversation.personaId);
    setTemperature(conversation.temperature);
    personaIdRef.current = conversation.personaId;
    temperatureRef.current = conversation.temperature;
    saveChatPreferences({
      personaId: conversation.personaId,
      temperature: conversation.temperature,
    });
    setInput("");
  }

  function deleteConversation(id: string) {
    if (busy) return;
    const remaining = removeConversation(conversations, id);
    const nextActive = activeId === id ? remaining[0] : conversations.find((item) => item.id === activeId);

    setConversations(remaining);
    if (nextActive) {
      activeIdRef.current = nextActive.id;
      setActiveId(nextActive.id);
      setMessages(nextActive.messages);
      setActivePersonaId(nextActive.personaId);
      setTemperature(nextActive.temperature);
      personaIdRef.current = nextActive.personaId;
      temperatureRef.current = nextActive.temperature;
    } else {
      activeIdRef.current = null;
      setActiveId(null);
      setMessages([]);
      const preferences = loadChatPreferences();
      const personaId = preferences.personaId ?? personaIdRef.current;
      const nextTemperature = preferences.temperature ?? temperatureRef.current;
      setActivePersonaId(personaId);
      setTemperature(nextTemperature);
      personaIdRef.current = personaId;
      temperatureRef.current = nextTemperature;
    }
  }

  function clearHistory() {
    if (busy) return;
    setConversations([]);
    activeIdRef.current = null;
    setActiveId(null);
    setMessages([]);
    setInput("");
    const preferences = loadChatPreferences();
    const personaId = preferences.personaId ?? personaIdRef.current;
    const nextTemperature = preferences.temperature ?? temperatureRef.current;
    setActivePersonaId(personaId);
    setTemperature(nextTemperature);
    personaIdRef.current = personaId;
    temperatureRef.current = nextTemperature;
  }

  async function send() {
    const text = input.trim();
    if (!text || busy) return;

    const conversationId = ensureActiveConversation();
    setInput("");

    let currentMessages: ChatMessage[] = [
      ...messages,
      { role: "user", content: text },
      { role: "assistant", content: "" },
    ];
    commitMessages(conversationId, currentMessages);
    setBusy(true);

    try {
      const resp = await fetch("/api/chat", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          messages: currentMessages
            .slice(0, -1)
            .map(({ role, content }) => ({ role, content })),
          persona_prompt: persona.prompt,
          temperature: temperatureRef.current,
        }),
      });

      if (!resp.ok) {
        const message = (await resp.text()).trim() || `HTTP ${resp.status}`;
        throw new Error(message);
      }

      if (!resp.body) throw new Error("No response stream");
      const reader = resp.body.getReader();
      const decoder = new TextDecoder();
      let buf = "";
      let acc = "";
      let reasoning = "";
      let sources: string[] | undefined;
      let status: string | undefined;

      while (true) {
        const { done, value } = await reader.read();
        if (done) break;
        buf += decoder.decode(value, { stream: true });
        const lines = buf.split("\n");
        buf = lines.pop() ?? "";
        for (const line of lines) {
          if (!line.trim()) continue;
          try {
            const evt = JSON.parse(line);
            if (evt.type === "delta") {
              acc += evt.text;
              status = undefined;
            } else if (evt.type === "reasoning") {
              reasoning += evt.text;
            } else if (evt.type === "phase") {
              status = evt.message || evt.value;
            } else if (evt.type === "sources") {
              sources = evt.sources;
            } else if (evt.type === "error") {
              acc += `\n\n*[error: ${evt.message}]*`;
            }

            currentMessages = [...currentMessages];
            currentMessages[currentMessages.length - 1] = {
              role: "assistant",
              content: acc,
              reasoning,
              sources,
              status,
            };
            commitMessages(conversationId, currentMessages);
          } catch {
            // partial JSON, ignore
          }
        }
      }
    } catch (e) {
      currentMessages = [...currentMessages];
      currentMessages[currentMessages.length - 1] = {
        role: "assistant",
        content: `*Failed to reach the model: ${(e as Error).message}*`,
      };
      commitMessages(conversationId, currentMessages);
    } finally {
      setBusy(false);
    }
  }

  return (
    <div className="grid h-full grid-cols-1 gap-6 lg:grid-cols-[1fr_320px]">
      <Card className="flex flex-col overflow-hidden">
        <div ref={scrollRef} className="flex-1 space-y-4 overflow-y-auto p-6">
          {messages.length === 0 && (
            <div className="flex h-full flex-col items-center justify-center text-center text-muted-foreground">
              <img
                src="/luminaria.svg"
                alt="Luminaria holding a light"
                className="mb-4 h-28 w-28 rounded-lg object-contain"
              />
              <p className="text-sm">
                Ask a question or share a thought.
              </p>
            </div>
          )}
          {messages.map((m, i) => {
            const isStreamingAssistant =
              busy && i === messages.length - 1 && m.role === "assistant";
            const thinkingPreview =
              isStreamingAssistant && !m.content && m.reasoning
                ? latestThinkingPreview(m.reasoning)
                : "";

            return (
              <div
                key={i}
                className={`flex ${m.role === "user" ? "justify-end" : "justify-start"}`}
              >
                <div
                  className={`max-w-[85%] rounded-lg px-4 py-2.5 text-sm leading-relaxed ${
                    m.role === "user"
                      ? "bg-primary text-primary-foreground"
                      : "bg-muted text-foreground"
                  }`}
                >
                  <div className="whitespace-pre-wrap">
                    {m.content ||
                      thinkingPreview ||
                      (isStreamingAssistant ? "Thinking..." : "")}
                  </div>
                  {thinkingPreview && (
                    <div className="mt-2 text-xs italic opacity-70">
                      Thinking stream
                    </div>
                  )}
                  {m.reasoning && (
                    <details className="mt-3 rounded-md border border-border/60 bg-background/60 p-3 text-xs text-muted-foreground">
                      <summary className="cursor-pointer select-none font-medium text-foreground">
                        Thinking
                      </summary>
                      <div className="mt-2 whitespace-pre-wrap leading-relaxed">
                        {m.reasoning}
                      </div>
                    </details>
                  )}
                  {m.status && (
                    <div className="mt-2 text-xs italic opacity-80">{m.status}</div>
                  )}
                  {m.sources && m.sources.length > 0 && (
                    <div className="mt-2 border-t border-border/50 pt-2 text-xs opacity-80">
                      <span className="font-medium">Sources: </span>
                      {m.sources.join(", ")}
                    </div>
                  )}
                </div>
              </div>
            );
          })}
        </div>
        <div className="border-t p-4">
          <form
            className="flex gap-2"
            onSubmit={(e) => {
              e.preventDefault();
              send();
            }}
          >
            <Input
              placeholder="Ask a question or share a thought"
              value={input}
              onChange={(e) => setInput(e.target.value)}
              disabled={busy}
            />
            <Button type="submit" disabled={busy || !input.trim()} size="icon">
              {busy ? (
                <Loader2 className="h-4 w-4 animate-spin" />
              ) : (
                <Send className="h-4 w-4" />
              )}
            </Button>
          </form>
        </div>
      </Card>

      <Card>
        <CardContent className="space-y-6 p-6">
          <div className="space-y-2">
            <div className="flex items-center justify-between gap-2">
              <div className="text-sm font-medium">Conversations</div>
              <Button variant="outline" size="sm" onClick={newChat} disabled={busy}>
                <Plus className="h-4 w-4" /> New
              </Button>
            </div>
            <div className="max-h-56 space-y-1 overflow-y-auto rounded-md border bg-background p-1">
              {conversations.length === 0 ? (
                <div className="px-2 py-6 text-center text-xs text-muted-foreground">
                  No saved chats yet.
                </div>
              ) : (
                conversations.map((conversation) => (
                  <div
                    key={conversation.id}
                    className={`group flex items-center gap-1 rounded-md ${
                      conversation.id === activeId ? "bg-accent" : "hover:bg-accent/60"
                    }`}
                  >
                    <button
                      type="button"
                      className="min-w-0 flex-1 px-2 py-2 text-left"
                      onClick={() => openConversation(conversation)}
                      disabled={busy}
                    >
                      <div className="truncate text-sm font-medium">
                        {conversation.title}
                      </div>
                      <div className="text-xs text-muted-foreground">
                        {displayDate(conversation.updatedAt)}
                      </div>
                    </button>
                    <Button
                      type="button"
                      variant="ghost"
                      size="icon"
                      className="h-8 w-8 shrink-0 opacity-70 group-hover:opacity-100"
                      onClick={() => deleteConversation(conversation.id)}
                      disabled={busy}
                      aria-label={`Delete ${conversation.title}`}
                    >
                      <Trash2 className="h-4 w-4" />
                    </Button>
                  </div>
                ))
              )}
            </div>
            {conversations.length > 0 && (
              <Button variant="ghost" size="sm" onClick={clearHistory} disabled={busy}>
                <Trash2 className="h-4 w-4" /> Clear history
              </Button>
            )}
          </div>

          <div className="space-y-2">
            <div className="flex items-center justify-between gap-2">
              <div className="text-sm font-medium">Course Materials</div>
              <Button
                type="button"
                variant="ghost"
                size="sm"
                onClick={loadDocuments}
                disabled={documents === null}
              >
                <RefreshCw className="h-4 w-4" /> Refresh
              </Button>
            </div>
            <div className="max-h-44 overflow-y-auto rounded-md border bg-background">
              {documents === null ? (
                <div className="px-3 py-6 text-center text-xs text-muted-foreground">
                  Loading materials...
                </div>
              ) : documentsError ? (
                <div className="px-3 py-3 text-xs text-destructive">
                  {documentsError}
                </div>
              ) : documents.length === 0 ? (
                <div className="px-3 py-6 text-center text-xs text-muted-foreground">
                  No materials uploaded yet.
                </div>
              ) : (
                <ul className="divide-y">
                  {documents.map((name) => (
                    <li key={name} className="flex min-w-0 items-center gap-2 px-3 py-2">
                      <FileText className="h-4 w-4 shrink-0 text-muted-foreground" />
                      <span className="truncate text-sm" title={name}>
                        {name}
                      </span>
                    </li>
                  ))}
                </ul>
              )}
            </div>
          </div>

          <div className="space-y-2">
            <div className="text-sm font-medium">Persona</div>
            <PersonaSelector personas={personas} value={persona.id} onChange={handlePersonaChange} />
            <p className="text-xs text-muted-foreground">{persona.description}</p>
          </div>
          <TemperatureSlider value={temperature} onChange={handleTemperatureChange} />
        </CardContent>
      </Card>
    </div>
  );
}
