import { useEffect, useRef, useState, useCallback, memo } from "react";
import { useParams } from "react-router-dom";
import { useConversationStore, type ConversationMessage } from "../stores/conversation.ts";
import { useAuthStore } from "../stores/auth.ts";
import { useTypingStore } from "../stores/typing.ts";
import { useLayoutStore } from "../stores/layout.ts";
import { useReadStatesStore } from "../stores/readStates.ts";
import { type Gif } from "./GiphyPicker.tsx";
import { EmojiPicker as KaomojiPicker } from "./EmojiPicker.tsx";
import Picker, { Theme } from 'emoji-picker-react';
import { MessageInput } from "./MessageInput.tsx";
import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm";
import { useContextMenu } from "./ContextMenu.tsx";
import { ReactionBar } from "./ReactionBar.tsx";
import { api } from "../lib/api.ts";
import { ExpandableImage } from "./ExpandableImage.tsx";

function formatTime(iso: string): string {
  const date = new Date(iso);
  const day = date.getDate().toString().padStart(2, "0");
  const month = (date.getMonth() + 1).toString().padStart(2, "0");
  const year = date.getFullYear();
  const hours = date.getHours().toString().padStart(2, "0");
  const minutes = date.getMinutes().toString().padStart(2, "0");
  return `[${day}.${month}.${year} @ ${hours}:${minutes}]`;
}

function renderDMContent(content: string) {
  return (
    <ReactMarkdown
      remarkPlugins={[remarkGfm]}
      components={{
        a: ({ ...props }) => <a {...props} className="text-gb-aqua hover:underline" target="_blank" rel="noreferrer" />,
        code: ({ ...props }) => <code {...props} className="bg-gb-bg-t px-1 rounded text-gb-fg-s" />,
        pre: ({ ...props }) => <pre {...props} className="bg-gb-bg-s p-2 my-1 overflow-x-auto text-gb-fg-s" />,
        blockquote: ({ ...props }) => <blockquote {...props} className="border-l-2 border-gb-orange pl-2 my-1 text-gb-fg-s" />,
        h1: ({ ...props }) => <h1 {...props} className="text-lg font-bold my-1" />,
        h2: ({ ...props }) => <h2 {...props} className="text-base font-bold my-1" />,
        h3: ({ ...props }) => <h3 {...props} className="text-sm font-bold my-0.5" />,
        p: ({ ...props }) => <span {...props} className="inline" />,
        img: ({ src, alt, ...props }) => (
          <ExpandableImage src={src} alt={alt} {...props} />
        ),
      }}
    >
      {content}
    </ReactMarkdown>
  );
}

const DMMessageItem = memo(({
  msg,
  onAddReaction,
  activeReactionMessageId,
  setActiveReactionMessageId,
  activeNativeReactionMessageId,
  setActiveNativeReactionMessageId,
  currentUserId,
  conversationId,
}: {
  msg: ConversationMessage;
  onAddReaction: (messageId: string, emoji: string) => void;
  activeReactionMessageId: string | null;
  setActiveReactionMessageId: (id: string | null) => void;
  activeNativeReactionMessageId: string | null;
  setActiveNativeReactionMessageId: (id: string | null) => void;
  currentUserId?: string;
  conversationId: string;
}) => {
  const { showMenu, MenuPortal } = useContextMenu();

  const handleContextMenu = (e: React.MouseEvent) => {
    e.preventDefault();
    const menuItems = [
      {
        label: "[ADD KAOMOJI REACTION]",
        onClick: () => {
          setActiveReactionMessageId(msg.id);
          setActiveNativeReactionMessageId(null);
        },
      },
      {
        label: "[ADD EMOJI REACTION]",
        onClick: () => {
          setActiveNativeReactionMessageId(msg.id);
          setActiveReactionMessageId(null);
        },
      },
      {
        label: "[DELETE MESSAGE]",
        onClick: () => {
          if (msg.author_id === currentUserId) {
            api.delete(`/conversations/${conversationId}/messages/${msg.id}`).catch(console.error);
          }
        },
        disabled: msg.author_id !== currentUserId,
        danger: true,
      },
    ];

    showMenu(e, menuItems);
  };

  return (
    <div className="group relative break-words" onContextMenu={handleContextMenu}>
      <span className="text-gb-fg-f">[{formatTime(msg.created_at)}]</span>{" "}
      <span className="text-gb-aqua">&lt;{msg.author_username}&gt;</span>{" "}
      <span className="text-gb-fg">{renderDMContent(msg.content)}</span>
      
      {/* Message reactions */}
      <ReactionBar
        reactions={(msg.reactions || []).map((r: any) => ({
          ...r,
          reacted: r.users.includes(currentUserId),
        }))}
        onToggle={async (emoji, isReacted) => {
          if (isReacted) {
            await api.delete(`/conversations/${conversationId}/messages/${msg.id}/reactions/${encodeURIComponent(emoji)}`);
          } else {
            await api.post(`/conversations/${conversationId}/messages/${msg.id}/reactions`, { emoji });
          }
        }}
      />

      {/* Kaomoji picker popover */}
      {activeReactionMessageId === msg.id && (
        <KaomojiPicker
          onSelect={(emoji) => onAddReaction(msg.id, emoji)}
          onClose={() => setActiveReactionMessageId(null)}
        />
      )}
      {activeNativeReactionMessageId === msg.id && (
        <div className="absolute z-50">
          <Picker
            theme={Theme.DARK}
            onEmojiClick={(emoji) => {
              onAddReaction(msg.id, emoji.emoji);
              setActiveNativeReactionMessageId(null);
            }}
          />
        </div>
      )}
      {MenuPortal}
    </div>
  );
});
DMMessageItem.displayName = "DMMessageItem";

export function DMChat() {
  const { conversationId } = useParams<{ conversationId: string }>();
  const activeId = useConversationStore((s) => s.activeConversationId);
  const setActive = useConversationStore((s) => s.setActiveConversation);
  const conversations = useConversationStore((s) => s.conversations);
  const messagesByConv = useConversationStore((s) => s.messagesByConversation);
  const fetchMessages = useConversationStore((s) => s.fetchMessages);
  const fetchOlderMessages = useConversationStore((s) => s.fetchOlderMessages);
  const isLoadingOlder = useConversationStore((s) => s.isLoadingOlder);
  const isLoading = useConversationStore((s) => s.isLoading);
  const sendMessage = useConversationStore((s) => s.sendMessage);
  const fetchConversations = useConversationStore((s) => s.fetchConversations);
  const currentUser = useAuthStore((s) => s.user);
  const typingUsers = useTypingStore((s) => s.typingUsers);
  const sendTypingStart = useTypingStore((s) => s.sendTypingStart);
  const [input, setInput] = useState("");
  const [error, setError] = useState<string | null>(null);
  const [activeReactionMessageId, setActiveReactionMessageId] = useState<string | null>(null);
  const [activeNativeReactionMessageId, setActiveNativeReactionMessageId] = useState<string | null>(null);
  const bottomRef = useRef<HTMLDivElement>(null);
  const scrollContainerRef = useRef<HTMLDivElement>(null);
  const inputRef = useRef<HTMLTextAreaElement>(null);
  const lastTypingRef = useRef<number>(0);
  const markConvRead = useReadStatesStore((s) => s.markConvRead);
  const convStates = useReadStatesStore((s) => s.convStates);

  const id = conversationId || activeId;
  const conversation = conversations.find((c) => c.id === id);
  const hasMore = useConversationStore((s) => id ? s.hasMoreByConversation[id] !== false : true);

  const handleAddReaction = useCallback(async (messageId: string, emoji: string) => {
    if (!id) return;
    try {
      await api.post(`/conversations/${id}/messages/${messageId}/reactions`, { emoji });
      setActiveReactionMessageId(null);
      setActiveNativeReactionMessageId(null);
    } catch (err) {
      setError(err instanceof Error ? err.message : "Failed to add reaction");
    }
  }, [id]);

  const handleScroll = useCallback(() => {
    const el = scrollContainerRef.current;
    if (!el || !id || isLoadingOlder || !hasMore) return;
    if (el.scrollTop < 100) {
      const prevHeight = el.scrollHeight;
      fetchOlderMessages(id).then(() => {
        requestAnimationFrame(() => {
          el.scrollTop = el.scrollHeight - prevHeight;
        });
      });
    }
  }, [id, isLoadingOlder, hasMore, fetchOlderMessages]);
  const messages = id ? messagesByConv[id] || [] : [];

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

  useEffect(() => {
    if (id) {
      setActive(id);
      fetchMessages(id);
      setError(null);
    }
  }, [id, setActive, fetchMessages]);

  useEffect(() => {
    bottomRef.current?.scrollIntoView({ behavior: "auto" });
  }, [messages]);

  useEffect(() => {
    if (!id || messages.length === 0 || isLoading) return;
    const lastMsg = messages[messages.length - 1];
    if (lastMsg?.id) {
      markConvRead(id, lastMsg.id);
    }
  }, [id, messages.length, isLoading]);

  const handleGifSelect = async (gif: Gif) => {
    if (!id) return;
    const content = `![${gif.title || "GIF"}](${gif.images.fixed_height.url})`;
    try {
      await sendMessage(id, content);
    } catch (err) {
      setError(err instanceof Error ? err.message : "Failed to send");
    }
  };

  const handleSubmit = useCallback(() => {
    const trimmed = input.trim();
    if (!id || !trimmed) return;
    setError(null);
    sendMessage(id, trimmed).then(() => {
      setInput("");
    }).catch((err) => {
      setError(err instanceof Error ? err.message : "Failed to send");
    });
  }, [id, input, sendMessage]);

  const handlePaste = async (e: React.ClipboardEvent<HTMLTextAreaElement>) => {
    const items = e.clipboardData.items;
    let imageFile: File | null = null;
    
    for (let i = 0; i < items.length; i++) {
      if (items[i].type.startsWith('image/')) {
        imageFile = items[i].getAsFile();
        break;
      }
    }
    
    if (imageFile) {
      e.preventDefault();
      
      const formData = new FormData();
      formData.append('file', imageFile);
      
      try {
        const res = await fetch('/api/v1/upload', {
          method: 'POST',
          body: formData,
          credentials: 'include'
        });
        
        if (!res.ok) throw new Error('Upload failed');
        
        const data = await res.json();
        const imageUrl = data.url;
        
        // Insert into input
        const imgMarkdown = `![image](${imageUrl})`;
        
        const inputEl = inputRef.current;
        if (inputEl) {
          const start = inputEl.selectionStart || 0;
          const end = inputEl.selectionEnd || 0;
          const newValue = input.slice(0, start) + imgMarkdown + input.slice(end);
          setInput(newValue);
          
          setTimeout(() => {
            inputEl.focus();
            inputEl.setSelectionRange(start + imgMarkdown.length, start + imgMarkdown.length);
          }, 0);
        } else {
          setInput(prev => prev + (prev.length > 0 && !prev.endsWith(' ') ? ' ' : '') + imgMarkdown);
        }
      } catch (err) {
        console.error('Failed to upload image:', err);
      }
    }
  };

  const selfDM = conversation && conversation.members.length === 1 && conversation.members[0].id === currentUser?.id;
  const title = selfDM
    ? "Notes"
    : conversation
      ? conversation.type === "group_dm"
        ? conversation.name || conversation.members.map((m) => m.username).join(", ")
        : conversation.members.find((m) => m.id !== currentUser?.id)?.username || "DM"
      : id
        ? "[LOADING...]"
        : "[NO CONVERSATION]";

  return (
    <div className="flex flex-col h-full bg-gb-bg">
      <div className="terminal-border border-t-0 border-x-0 px-2 md:px-3 py-2 text-gb-fg-s flex items-center gap-2">
        <button
          type="button"
          onClick={() => useLayoutStore.getState().setMobileView('sidebar')}
          className="md:hidden terminal-button text-xs px-2 py-0.5 shrink-0"
        >
          ←
        </button>
        <span className="truncate">@ {title}</span>
      </div>
      <div ref={scrollContainerRef} onScroll={handleScroll} className="flex-1 overflow-y-auto p-3 space-y-1 font-mono text-sm">
        {isLoadingOlder && <p className="text-center text-gb-fg-f text-xs">[loading older messages...]</p>}
        {!id && <p className="text-gb-fg-f">[select a conversation]</p>}
        {id && messages.length === 0 && <p className="text-gb-fg-f">[no messages]</p>}
        {messages.map((msg: ConversationMessage, i: number) => {
          const lastReadId = id ? convStates[id] : undefined;
          const showDivider = lastReadId && msg.id === lastReadId && i < messages.length - 1;
          return (
            <>
              <DMMessageItem
                key={msg.id}
                msg={msg}
                onAddReaction={handleAddReaction}
                activeReactionMessageId={activeReactionMessageId}
                setActiveReactionMessageId={setActiveReactionMessageId}
                activeNativeReactionMessageId={activeNativeReactionMessageId}
                setActiveNativeReactionMessageId={setActiveNativeReactionMessageId}
                currentUserId={currentUser?.id}
                conversationId={id || ""}
              />
              {showDivider && (
                <div className="flex items-center gap-2 my-1">
                  <span className="flex-1 border-t border-gb-red"></span>
                  <span className="text-gb-red text-xs font-bold shrink-0">── NEW ──</span>
                  <span className="flex-1 border-t border-gb-red"></span>
                </div>
              )}
            </>
          );
        })}
        <div ref={bottomRef} />
      </div>
      <div className="px-3 pt-1 text-xs text-gb-fg-f font-mono italic h-5 select-none">
        {(() => {
          const convTyping = id ? (typingUsers[id] || []).filter(u => u.userId !== currentUser?.id) : [];
          if (convTyping.length === 0) return "\u00A0";
          const names = convTyping.map(u => u.username);
          return names.length === 1 ? `${names[0]} is typing...` : names.length === 2 ? `${names[0]} and ${names[1]} are typing...` : `${names[0]} and ${names.length - 1} others are typing...`;
        })()}
      </div>
      {error && (
        <div className="px-3 pb-1">
          <p className="text-gb-red text-xs font-mono">ERR: {error}</p>
        </div>
      )}
      <div className="p-3 relative">
        <MessageInput
          ref={inputRef}
          value={input}
          onChange={(v) => {
            setInput(v);
            if (id && v.length > 0) {
              const now = Date.now();
              if (now - lastTypingRef.current > 3000) {
                sendTypingStart(id);
                lastTypingRef.current = now;
              }
            }
          }}
          onSubmit={handleSubmit}
          onPaste={handlePaste}
          onGifSelect={handleGifSelect}
          disabled={!id}
        />
      </div>
    </div>
  );
}
