import { useCallback, useEffect, useRef, useState } from "react";
import type { Message, UploadedFile } from "@/lib/api";
import { getMessages, sendMessage, uploadFile, getAttachmentUrl } from "@/lib/api";
import { wsClient } from "@/lib/ws";
import MentionInput from "./MentionInput";

const PAGE_SIZE = 30;

const AUTHOR_ICON: Record<string, string> = {
  human: "👤",
  agent: "🤖",
  system: "⚙️",
};

/** Streaming state for agent that is currently generating */
interface StreamingState {
  agentSlug: string;
  text: string;
  thinking: string;
  tools: Array<{ tool: string; status: string }>;
}

interface Props {
  chatId: string;
  projectSlug?: string;
}

function formatFileSize(bytes: number): string {
  if (bytes < 1024) return `${bytes} B`;
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
  return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
}

function isImageMime(mime: string | null): boolean {
  return !!mime && mime.startsWith("image/");
}

export default function ChatPanel({ chatId, projectSlug }: Props) {
  const [messages, setMessages] = useState<Message[]>([]);
  const [input, setInput] = useState("");
  const [sending, setSending] = useState(false);
  const [loadingOlder, setLoadingOlder] = useState(false);
  const [hasMore, setHasMore] = useState(true);
  const [pendingFiles, setPendingFiles] = useState<UploadedFile[]>([]);
  const [uploading, setUploading] = useState(false);
  const [streaming, setStreaming] = useState<StreamingState | null>(null);
  const scrollRef = useRef<HTMLDivElement>(null);
  const bottomRef = useRef<HTMLDivElement>(null);
  const fileInputRef = useRef<HTMLInputElement>(null);
  const isInitial = useRef(true);

  // Load initial messages
  useEffect(() => {
    if (!chatId) return;
    isInitial.current = true;
    setHasMore(true);
    getMessages({ chat_id: chatId, limit: PAGE_SIZE })
      .then((msgs) => {
        setMessages(msgs);
        setHasMore(msgs.length >= PAGE_SIZE);
        setTimeout(() => bottomRef.current?.scrollIntoView(), 50);
      })
      .catch(() => {});
  }, [chatId]);

  // WS: new messages
  useEffect(() => {
    const unsub = wsClient.on("message.new", (msg: Message) => {
      if (msg.chat_id !== chatId) return;
      setMessages((prev) => {
        if (prev.some((m) => m.id === msg.id)) return prev;
        return [...prev, msg];
      });
      // Clear streaming when final message arrives
      setStreaming(null);
    });
    return () => { unsub?.(); };
  }, [chatId]);

  // WS: agent streaming events
  useEffect(() => {
    const unsubStart = wsClient.on("agent.stream.start", (data: any) => {
      if (data.chat_id !== chatId) return;
      setStreaming({ agentSlug: data.agent_slug || "agent", text: "", thinking: "", tools: [] });
    });
    const unsubDelta = wsClient.on("agent.stream.delta", (data: any) => {
      if (data.chat_id !== chatId) return;
      setStreaming((prev) => {
        if (!prev) return { agentSlug: data.agent_slug || "agent", text: "", thinking: "", tools: [] };
        if (data.block_type === "thinking") {
          return { ...prev, thinking: prev.thinking + (data.text || "") };
        }
        return { ...prev, text: prev.text + (data.text || "") };
      });
    });
    const unsubTool = wsClient.on("agent.stream.tool", (data: any) => {
      if (data.chat_id !== chatId) return;
      setStreaming((prev) => {
        if (!prev) return null;
        const tools = [...prev.tools];
        const existing = tools.findIndex((t) => t.tool === data.tool);
        if (existing >= 0) {
          tools[existing] = { tool: data.tool, status: data.status };
        } else {
          tools.push({ tool: data.tool, status: data.status });
        }
        return { ...prev, tools };
      });
    });
    const unsubEnd = wsClient.on("agent.stream.end", (data: any) => {
      if (data.chat_id !== chatId) return;
      // Don't clear immediately — wait for message.new with final content
    });
    return () => {
      unsubStart?.();
      unsubDelta?.();
      unsubTool?.();
      unsubEnd?.();
    };
  }, [chatId]);

  // Auto-scroll on new messages (only if near bottom)
  useEffect(() => {
    if (isInitial.current) {
      isInitial.current = false;
      return;
    }
    const el = scrollRef.current;
    if (!el) return;
    if (el.scrollHeight - el.scrollTop - el.clientHeight < 100) {
      bottomRef.current?.scrollIntoView({ behavior: "smooth" });
    }
  }, [messages]);

  // Load older messages
  const loadOlder = useCallback(async () => {
    if (loadingOlder || !hasMore || !chatId || messages.length === 0) return;
    setLoadingOlder(true);
    const el = scrollRef.current;
    const prevHeight = el?.scrollHeight || 0;
    try {
      const older = await getMessages({ chat_id: chatId, limit: PAGE_SIZE, offset: messages.length });
      if (older.length < PAGE_SIZE) setHasMore(false);
      if (older.length > 0) {
        setMessages((prev) => {
          const ids = new Set(prev.map((m) => m.id));
          return [...older.filter((m) => !ids.has(m.id)), ...prev];
        });
        requestAnimationFrame(() => {
          if (el) el.scrollTop = el.scrollHeight - prevHeight;
        });
      }
    } catch (e) {
      console.error("Failed to load older messages:", e);
    } finally {
      setLoadingOlder(false);
    }
  }, [chatId, messages.length, loadingOlder, hasMore]);

  const handleScroll = useCallback(() => {
    const el = scrollRef.current;
    if (el && el.scrollTop < 50 && hasMore && !loadingOlder) loadOlder();
  }, [loadOlder, hasMore, loadingOlder]);

  const handleFileSelect = async (e: React.ChangeEvent<HTMLInputElement>) => {
    const files = e.target.files;
    if (!files?.length) return;
    setUploading(true);
    try {
      const uploaded: UploadedFile[] = [];
      for (const file of Array.from(files)) {
        const result = await uploadFile(file);
        uploaded.push(result);
      }
      setPendingFiles((prev) => [...prev, ...uploaded]);
    } catch (err) {
      console.error("Upload failed:", err);
    } finally {
      setUploading(false);
      if (fileInputRef.current) fileInputRef.current.value = "";
    }
  };

  const removePendingFile = (fileId: string) => {
    setPendingFiles((prev) => prev.filter((f) => f.file_id !== fileId));
  };

  const handleSend = async () => {
    if ((!input.trim() && pendingFiles.length === 0) || !chatId || sending) return;
    const text = input.trim() || (pendingFiles.length > 0 ? `📎 ${pendingFiles.map(f => f.filename).join(", ")}` : "");
    const attachments = pendingFiles.map((f) => ({
      file_id: f.file_id,
      filename: f.filename,
      mime_type: f.mime_type,
      size: f.size,
      storage_name: f.storage_name,
    }));
    setInput("");
    setPendingFiles([]);
    setSending(true);
    try {
      // Extract @mentions from text
      const mentionMatches = text.match(/@(\w+)/g);
      const mentions = mentionMatches ? [...new Set(mentionMatches.map(m => m.slice(1)))] : [];

      const msg = await sendMessage({
        chat_id: chatId,
        content: text,
        mentions: mentions.length > 0 ? mentions : undefined,
        attachments: attachments.length > 0 ? attachments : undefined,
      });
      setMessages((prev) => {
        if (prev.some((m) => m.id === msg.id)) return prev;
        return [...prev, msg];
      });
      setTimeout(() => bottomRef.current?.scrollIntoView({ behavior: "smooth" }), 50);
    } catch (e) {
      console.error("Failed to send:", e);
      setInput(text);
    } finally {
      setSending(false);
    }
  };

  const renderContent = (text: string) => {
    const parts = text.split(/(@\w+)/g);
    return parts.map((part, i) =>
      /^@\w+$/.test(part) ? (
        <span key={i} className="text-[var(--accent)] font-medium">{part}</span>
      ) : (
        <span key={i}>{part}</span>
      )
    );
  };

  const renderAttachments = (attachments: Message["attachments"]) => {
    if (!attachments?.length) return null;
    return (
      <div className="ml-5 mt-1 flex flex-wrap gap-2">
        {attachments.map((att) => {
          const url = getAttachmentUrl(att.id);
          if (isImageMime(att.mime_type)) {
            return (
              <a key={att.id} href={url} target="_blank" rel="noopener noreferrer">
                <img
                  src={url}
                  alt={att.filename}
                  className="max-w-[300px] max-h-[200px] rounded border border-[var(--border)] object-cover"
                />
              </a>
            );
          }
          return (
            <a
              key={att.id}
              href={url}
              target="_blank"
              rel="noopener noreferrer"
              className="flex items-center gap-2 px-3 py-2 rounded border border-[var(--border)] bg-[var(--bg)] hover:bg-white/5 text-sm"
            >
              <span>📎</span>
              <span className="truncate max-w-[200px]">{att.filename}</span>
              <span className="text-[var(--muted)] text-xs">{formatFileSize(att.size)}</span>
            </a>
          );
        })}
      </div>
    );
  };

  const renderMessage = (msg: Message) => {
    const bgStyle =
      msg.author_type === "system"
        ? { backgroundColor: "rgba(161, 120, 0, 0.2)", borderRadius: 6, padding: "4px 8px" }
        : msg.author_type === "agent"
          ? { backgroundColor: "rgba(0, 120, 60, 0.2)", borderRadius: 6, padding: "4px 8px" }
          : undefined;
    return (
      <div key={msg.id} className="text-sm" style={bgStyle}>
        <div className="flex items-center gap-1 text-xs text-[var(--muted)] mb-0.5">
          <span>{AUTHOR_ICON[msg.author_type] || "👤"}</span>
          <span className="font-medium">
            {msg.author_type === "system"
              ? (msg.actor ? `Система (${msg.actor.name})` : "Система")
              : (msg.author?.name || msg.author?.slug || "Unknown")}
          </span>
          <span>·</span>
          <span>{new Date(msg.created_at).toLocaleString("ru-RU", { hour: "2-digit", minute: "2-digit" })}</span>
        </div>
        {msg.thinking && (
          <details className="ml-5 mb-1">
            <summary className="text-xs text-[var(--muted)] cursor-pointer hover:text-[var(--fg)]">
              💭 Размышления
            </summary>
            <div className="mt-1 text-xs text-[var(--muted)] whitespace-pre-wrap bg-white/5 rounded p-2 max-h-[200px] overflow-y-auto">
              {msg.thinking}
            </div>
          </details>
        )}
        <div className="ml-5 whitespace-pre-wrap">{renderContent(msg.content)}</div>
        {renderAttachments(msg.attachments)}
      </div>
    );
  };

  return (
    <div className="flex flex-col h-full">
      <div
        ref={scrollRef}
        onScroll={handleScroll}
        className="flex-1 overflow-y-auto px-6 py-4 space-y-3"
      >
        {loadingOlder && (
          <div className="text-center text-xs text-[var(--muted)] py-2">Загрузка...</div>
        )}
        {!hasMore && messages.length > 0 && (
          <div className="text-center text-xs text-[var(--muted)] py-2">— начало истории —</div>
        )}
        {messages.map(renderMessage)}
        {messages.length === 0 && !streaming && (
          <div className="text-sm text-[var(--muted)] italic py-8 text-center">Нет сообщений</div>
        )}

        {/* Streaming indicator */}
        {streaming && (
          <div className="text-sm" style={{ backgroundColor: "rgba(0, 120, 60, 0.2)", borderRadius: 6, padding: "4px 8px" }}>
            <div className="flex items-center gap-1 text-xs text-[var(--muted)] mb-0.5">
              <span>🤖</span>
              <span className="font-medium">{streaming.agentSlug}</span>
              <span className="animate-pulse">●</span>
            </div>
            {streaming.thinking && (
              <details className="ml-5 mb-1" open>
                <summary className="text-xs text-[var(--muted)] cursor-pointer hover:text-[var(--fg)]">
                  💭 Размышления
                </summary>
                <div className="mt-1 text-xs text-[var(--muted)] whitespace-pre-wrap bg-white/5 rounded p-2 max-h-[200px] overflow-y-auto">
                  {streaming.thinking}
                </div>
              </details>
            )}
            {streaming.tools.length > 0 && (
              <div className="ml-5 mb-1 text-xs text-[var(--muted)]">
                {streaming.tools.map((t, i) => (
                  <span key={i} className="mr-2">
                    {t.status === "running" ? "🔧" : "✅"} {t.tool}
                  </span>
                ))}
              </div>
            )}
            {streaming.text && (
              <div className="ml-5 whitespace-pre-wrap">{streaming.text}</div>
            )}
            {!streaming.text && streaming.tools.length === 0 && !streaming.thinking && (
              <div className="ml-5 text-[var(--muted)] italic">Думает...</div>
            )}
          </div>
        )}

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

      {/* Pending files preview */}
      {pendingFiles.length > 0 && (
        <div className="px-6 py-2 border-t border-[var(--border)] flex flex-wrap gap-2">
          {pendingFiles.map((f) => (
            <div
              key={f.file_id}
              className="flex items-center gap-1 px-2 py-1 rounded bg-[var(--accent)]/10 text-xs"
            >
              <span>📎</span>
              <span className="truncate max-w-[150px]">{f.filename}</span>
              <span className="text-[var(--muted)]">{formatFileSize(f.size)}</span>
              <button
                onClick={() => removePendingFile(f.file_id)}
                className="ml-1 text-[var(--muted)] hover:text-red-400"
              >
                ✕
              </button>
            </div>
          ))}
        </div>
      )}

      <div className="px-6 py-3 border-t border-[var(--border)] flex gap-2 items-end">
        <input
          ref={fileInputRef}
          type="file"
          multiple
          className="hidden"
          onChange={handleFileSelect}
        />
        <button
          onClick={() => fileInputRef.current?.click()}
          disabled={uploading}
          className="px-2 py-2 text-[var(--muted)] hover:text-[var(--fg)] transition-colors shrink-0 disabled:opacity-50"
          title="Прикрепить файл"
        >
          {uploading ? "⏳" : "📎"}
        </button>
        <MentionInput
          projectSlug={projectSlug || ""}
          value={input}
          onChange={setInput}
          onSubmit={handleSend}
          placeholder="Сообщение... (@mention)"
          disabled={sending}
        />
        <button
          onClick={handleSend}
          disabled={sending}
          className="px-4 py-2 bg-[var(--accent)] text-white rounded-lg text-sm hover:opacity-90 disabled:opacity-50 shrink-0"
        >
          ↵
        </button>
      </div>
    </div>
  );
}
