import {
  ActionIcon,
  Badge,
  Box,
  Group,
  Loader,
  Paper,
  ScrollArea,
  Stack,
  Text,
  Textarea,
  Tooltip,
} from "@mantine/core";
import { notifications } from "@mantine/notifications";
import { IconMessageChatbot, IconSend, IconTrash } from "@tabler/icons-react";
import { KeyboardEvent, useEffect, useRef, useState } from "react";
import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm";
import { ChatMessage, ChatStreamEvent, ChatToolCall, streamChat } from "../api";

const STORAGE_KEY = "kanban_chat_v1";

interface StoredMessage extends ChatMessage {
  ts: number;
  tool_calls?: ChatToolCall[];
}

interface Props {
  onBoardChange: () => void;
}

function loadStored(): StoredMessage[] {
  try {
    const raw = localStorage.getItem(STORAGE_KEY);
    if (!raw) return [];
    const parsed = JSON.parse(raw);
    if (Array.isArray(parsed)) return parsed;
  } catch {
    /* fall through */
  }
  return [];
}

export function ChatPanel({ onBoardChange }: Props) {
  const [messages, setMessages] = useState<StoredMessage[]>(() => loadStored());
  const [input, setInput] = useState("");
  const [streaming, setStreaming] = useState(false);
  // Live in-flight assistant turn: incremental text + tool calls collected so
  // far. When the turn finishes (done/error) it is committed to messages.
  const [liveText, setLiveText] = useState("");
  const [liveCalls, setLiveCalls] = useState<ChatToolCall[]>([]);
  const scrollRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    localStorage.setItem(STORAGE_KEY, JSON.stringify(messages));
  }, [messages]);

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

  const send = async () => {
    const text = input.trim();
    if (!text || streaming) return;
    const userMsg: StoredMessage = { role: "user", content: text, ts: Date.now() };
    const next = [...messages, userMsg];
    setMessages(next);
    setInput("");
    setStreaming(true);
    setLiveText("");
    setLiveCalls([]);

    let accumulatedText = "";
    const accumulatedCalls: ChatToolCall[] = [];
    let boardChanged = false;

    const onEvent = (ev: ChatStreamEvent) => {
      switch (ev.type) {
        case "delta":
          accumulatedText += ev.text;
          setLiveText(accumulatedText);
          break;
        case "tool_use": {
          const call: ChatToolCall = { tool: ev.tool, ok: true, input: ev.input };
          accumulatedCalls.push(call);
          setLiveCalls([...accumulatedCalls]);
          break;
        }
        case "tool_result": {
          // Map by reverse order: the latest tool_use without is_error set.
          for (let i = accumulatedCalls.length - 1; i >= 0; i--) {
            const c = accumulatedCalls[i];
            if (c.error === undefined && c.ok) {
              if (ev.is_error) {
                c.ok = false;
                c.error = ev.result || "tool error";
              }
              break;
            }
          }
          setLiveCalls([...accumulatedCalls]);
          break;
        }
        case "result":
          if (ev.text) {
            // Final result text replaces the streamed delta only when no
            // delta was emitted (some claude paths only emit the final).
            if (accumulatedText.trim() === "") {
              accumulatedText = ev.text;
              setLiveText(accumulatedText);
            }
          }
          break;
        case "done":
          if (ev.board_changed) boardChanged = true;
          break;
        case "error":
          accumulatedText = `Error: ${ev.error}`;
          setLiveText(accumulatedText);
          break;
      }
    };

    try {
      const payload: ChatMessage[] = next.map((m) => ({ role: m.role, content: m.content }));
      await streamChat(payload, onEvent);
    } catch (e) {
      const msg = (e as Error).message;
      notifications.show({ color: "red", message: msg });
      accumulatedText = accumulatedText || `Error: ${msg}`;
    } finally {
      const assistant: StoredMessage = {
        role: "assistant",
        content: accumulatedText,
        ts: Date.now(),
        tool_calls: accumulatedCalls.length > 0 ? accumulatedCalls : undefined,
      };
      setMessages((prev) => [...prev, assistant]);
      setLiveText("");
      setLiveCalls([]);
      setStreaming(false);
      if (boardChanged) onBoardChange();
    }
  };

  const onKey = (e: KeyboardEvent<HTMLTextAreaElement>) => {
    if (e.key === "Enter" && !e.shiftKey) {
      e.preventDefault();
      send();
    }
  };

  const clear = () => {
    setMessages([]);
    localStorage.removeItem(STORAGE_KEY);
  };

  return (
    <Stack gap={0} h="100%">
      <Group justify="space-between" p="xs" style={{ borderBottom: "1px solid var(--mantine-color-dark-4)" }}>
        <Group gap={6}>
          <IconMessageChatbot size={18} />
          <Text fw={600} size="sm">
            Asistente
          </Text>
        </Group>
        <Tooltip label="Limpiar conversacion" withArrow>
          <ActionIcon variant="subtle" color="gray" size="sm" onClick={clear} disabled={messages.length === 0}>
            <IconTrash size={14} />
          </ActionIcon>
        </Tooltip>
      </Group>

      <ScrollArea viewportRef={scrollRef} style={{ flex: 1 }} type="auto" p="xs">
        <Stack gap="xs">
          {messages.length === 0 && !streaming && (
            <Text size="sm" c="dimmed" ta="center" mt="md">
              Escribe algo. Ejemplos:
              <br />- "crea columna Backlog"
              <br />- "anade tarjeta para revisar PR de Lucas en Doing"
              <br />- "que hay en Doing?"
            </Text>
          )}
          {messages.map((m, i) => (
            <ChatBubble key={i} msg={m} />
          ))}
          {streaming && (
            <ChatBubble
              msg={{
                role: "assistant",
                content: liveText,
                ts: Date.now(),
                tool_calls: liveCalls.length > 0 ? liveCalls : undefined,
              }}
              streaming
            />
          )}
          {streaming && liveText === "" && liveCalls.length === 0 && (
            <Group gap={6} pl="xs">
              <Loader size="xs" />
              <Text size="xs" c="dimmed">
                Pensando...
              </Text>
            </Group>
          )}
        </Stack>
      </ScrollArea>

      <Stack gap={4} p="xs" style={{ borderTop: "1px solid var(--mantine-color-dark-4)" }}>
        <Group align="flex-end" gap={4} wrap="nowrap">
          <Textarea
            placeholder="Pide algo... (Enter envia, Shift+Enter newline)"
            value={input}
            onChange={(e) => setInput(e.currentTarget.value)}
            onKeyDown={onKey}
            disabled={streaming}
            autosize
            minRows={1}
            maxRows={6}
            style={{ flex: 1 }}
          />
          <ActionIcon
            size="lg"
            variant="filled"
            onClick={send}
            disabled={!input.trim() || streaming}
            aria-label="Send"
          >
            {streaming ? <Loader size="xs" color="white" /> : <IconSend size={16} />}
          </ActionIcon>
        </Group>
      </Stack>
    </Stack>
  );
}

function ChatBubble({ msg, streaming = false }: { msg: StoredMessage; streaming?: boolean }) {
  const isUser = msg.role === "user";
  return (
    <Paper
      p="xs"
      radius="md"
      withBorder
      bg={isUser ? "blue.9" : "dark.6"}
      style={{ alignSelf: isUser ? "flex-end" : "flex-start", maxWidth: "92%" }}
    >
      <Stack gap={4}>
        {msg.content && (
          <Box
            className="kanban-md"
            style={{ fontSize: 13, lineHeight: 1.45, color: "var(--mantine-color-text)" }}
          >
            <ReactMarkdown remarkPlugins={[remarkGfm]}>{msg.content}</ReactMarkdown>
          </Box>
        )}
        {streaming && msg.content && (
          <Box style={{ display: "inline-block", width: 8, height: 14, background: "currentColor", opacity: 0.6 }} />
        )}
        {msg.tool_calls && msg.tool_calls.length > 0 && (
          <Group gap={4} wrap="wrap">
            {msg.tool_calls.map((c, i) => (
              <Badge
                key={i}
                size="xs"
                color={c.ok ? "teal" : "red"}
                variant="light"
                title={c.error || ""}
                leftSection={c.ok && streaming ? <Loader size={8} color="teal" /> : null}
              >
                {c.tool}
                {!c.ok && c.error ? `: ${c.error}` : ""}
              </Badge>
            ))}
          </Group>
        )}
      </Stack>
    </Paper>
  );
}
