import React, { useState, useEffect, useRef, useCallback } from 'react';
import Message from './Message';
import { parseMarkdown } from '../utils/markdown';

/* ────────────────────────────────────────────
   Helper: debounce
   ──────────────────────────────────────────── */
function useDebouncedCallback(fn, delay) {
  const timer = useRef(null);
  return useCallback((...args) => {
    if (timer.current) return;
    fn(...args);
    timer.current = setTimeout(() => { timer.current = null; }, delay);
  }, [fn, delay]);
}

/* ────────────────────────────────────────────
   Sub-component: MessageInput
   ──────────────────────────────────────────── */
function MessageInput({ token, channelId, socket, socketEvent, replyTo, onClearReply, isDm }) {
  const [text, setText] = useState('');
  const [file, setFile] = useState(null);
  const [uploading, setUploading] = useState(false);
  const [uploadProgress, setUploadProgress] = useState(0);
  const [error, setError] = useState('');
  const textareaRef = useRef(null);
  const fileRef = useRef(null);

  const emitTyping = useDebouncedCallback(() => {
    if (!socket) return;
    if (isDm) {
      socket.emit('dm-typing', { conversationId: channelId });
    } else {
      socket.emit('typing', { channelId });
    }
  }, 2000);

  const autoGrow = () => {
    const ta = textareaRef.current;
    if (!ta) return;
    ta.style.height = 'auto';
    const maxH = 5 * 24; // ~5 lines
    ta.style.height = Math.min(ta.scrollHeight, maxH) + 'px';
  };

  const handleChange = (e) => {
    setText(e.target.value);
    autoGrow();
    emitTyping();
  };

  const uploadFile = async (fileObj) => {
    const form = new FormData();
    form.append('file', fileObj);
    setUploading(true);
    setUploadProgress(0);
    setError('');

    try {
      // Use XMLHttpRequest for progress
      return await new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();
        xhr.open('POST', '/api/upload');
        xhr.setRequestHeader('Authorization', `Bearer ${token}`);
        xhr.upload.onprogress = (e) => {
          if (e.lengthComputable) setUploadProgress(Math.round((e.loaded / e.total) * 100));
        };
        xhr.onload = () => {
          if (xhr.status >= 200 && xhr.status < 300) {
            resolve(JSON.parse(xhr.responseText));
          } else {
            reject(new Error('Upload failed'));
          }
        };
        xhr.onerror = () => reject(new Error('Upload failed'));
        xhr.send(form);
      });
    } catch (err) {
      setError(err.message);
      return null;
    } finally {
      setUploading(false);
      setUploadProgress(0);
    }
  };

  const send = async () => {
    const content = text.trim();
    if (!content && !file) return;
    setError('');

    let fileData = null;
    if (file) {
      fileData = await uploadFile(file);
      if (!fileData) return;
      setFile(null);
      if (fileRef.current) fileRef.current.value = '';
    }

    const body = { content: content || '' };
    if (replyTo) body.replyTo = replyTo.id;
    if (fileData) {
      body.file_url = fileData.url;
      body.file_name = fileData.name;
      body.file_size = fileData.size;
      body.file_type = fileData.type;
    }

    const endpoint = isDm
      ? `/api/dms/${channelId}/messages`
      : `/api/channels/${channelId}/messages`;

    try {
      const res = await fetch(endpoint, {
        method: 'POST',
        headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' },
        body: JSON.stringify(body),
      });
      if (!res.ok) throw new Error('Failed to send message');
      setText('');
      if (onClearReply) onClearReply();
      if (textareaRef.current) {
        textareaRef.current.style.height = 'auto';
      }
    } catch (err) {
      setError(err.message);
    }
  };

  const handleKeyDown = (e) => {
    if (e.key === 'Enter' && !e.shiftKey) {
      e.preventDefault();
      send();
    }
  };

  const handleFileSelect = (e) => {
    if (e.target.files?.[0]) setFile(e.target.files[0]);
  };

  return (
    <div className="px-4 pb-4">
      {replyTo && (
        <div className="flex items-center gap-2 px-3 py-1.5 mb-1 bg-[#25252a] rounded-t-lg text-sm text-gray-400">
          <span>Replying to <span className="text-[#FFD700] font-medium">{replyTo.username}</span></span>
          <button onClick={onClearReply} className="ml-auto text-gray-500 hover:text-gray-300 transition-colors duration-200">✕</button>
        </div>
      )}
      {file && (
        <div className="flex items-center gap-2 px-3 py-1.5 mb-1 bg-[#25252a] rounded-t-lg text-sm text-gray-400">
          <svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15.172 7l-6.586 6.586a2 2 0 102.828 2.828l6.414-6.586a4 4 0 00-5.656-5.656l-6.415 6.585a6 6 0 108.486 8.486L20.5 13" /></svg>
          <span className="truncate">{file.name}</span>
          <button onClick={() => { setFile(null); if (fileRef.current) fileRef.current.value = ''; }} className="ml-auto text-gray-500 hover:text-gray-300 transition-colors duration-200">✕</button>
        </div>
      )}
      {uploading && (
        <div className="h-1 mb-1 bg-[#25252a] rounded overflow-hidden">
          <div className="h-full bg-[#FFD700] transition-all duration-300" style={{ width: `${uploadProgress}%` }} />
        </div>
      )}
      <div className="flex items-end gap-2 bg-[#25252a] rounded-lg px-3 py-2">
        <button
          onClick={() => fileRef.current?.click()}
          className="p-1 text-gray-400 hover:text-gray-200 transition-colors duration-200 flex-shrink-0 mb-0.5"
          title="Upload file"
        >
          <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" /></svg>
        </button>
        <input ref={fileRef} type="file" className="hidden" onChange={handleFileSelect} />
        <textarea
          ref={textareaRef}
          value={text}
          onChange={handleChange}
          onKeyDown={handleKeyDown}
          rows={1}
          placeholder={isDm ? 'Send a message…' : `Message #${''}`}
          className="flex-1 bg-transparent text-gray-100 placeholder-gray-500 resize-none outline-none text-sm leading-6 max-h-[120px]"
        />
        <button
          onClick={send}
          disabled={(!text.trim() && !file) || uploading}
          className="p-1 text-[#FFD700] hover:text-yellow-300 disabled:text-gray-600 transition-colors duration-200 flex-shrink-0 mb-0.5"
        >
          <svg className="w-5 h-5" fill="currentColor" viewBox="0 0 20 20"><path d="M10.894 2.553a1 1 0 00-1.788 0l-7 14a1 1 0 001.169 1.409l5-1.429A1 1 0 009 15.571V11a1 1 0 112 0v4.571a1 1 0 00.725.962l5 1.428a1 1 0 001.17-1.408l-7-14z" /></svg>
        </button>
      </div>
      {error && <p className="text-red-400 text-xs mt-1">{error}</p>}
    </div>
  );
}

/* ────────────────────────────────────────────
   Sub-component: TypingIndicator
   ──────────────────────────────────────────── */
function TypingIndicator({ typingUsers }) {
  if (!typingUsers || typingUsers.length === 0) return <div className="h-6 px-4" />;
  const names = typingUsers.map(u => u.username);
  const label = 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`;
  return (
    <div className="h-6 px-4 flex items-center gap-1 text-xs text-gray-400">
      <span className="flex gap-0.5">
        <span className="typing-dot w-1.5 h-1.5 bg-gray-400 rounded-full animate-bounce" style={{ animationDelay: '0ms' }} />
        <span className="typing-dot w-1.5 h-1.5 bg-gray-400 rounded-full animate-bounce" style={{ animationDelay: '150ms' }} />
        <span className="typing-dot w-1.5 h-1.5 bg-gray-400 rounded-full animate-bounce" style={{ animationDelay: '300ms' }} />
      </span>
      <span>{label}</span>
    </div>
  );
}

/* ────────────────────────────────────────────
   Sub-component: DMView (when server is null)
   ──────────────────────────────────────────── */
function DMView({ token, user, socket }) {
  const [conversations, setConversations] = useState([]);
  const [activeConvo, setActiveConvo] = useState(null);
  const [messages, setMessages] = useState([]);
  const [loading, setLoading] = useState(true);
  const [typingUsers, setTypingUsers] = useState([]);
  const [replyTo, setReplyTo] = useState(null);
  const messagesEndRef = useRef(null);
  const typingTimers = useRef({});

  // Fetch conversations
  useEffect(() => {
    (async () => {
      try {
        const res = await fetch('/api/dms', {
          headers: { 'Authorization': `Bearer ${token}` },
        });
        if (res.ok) {
          const data = await res.json();
          setConversations(data.conversations || data);
        }
      } catch (err) {
        console.error('Failed to load DMs', err);
      } finally {
        setLoading(false);
      }
    })();
  }, [token]);

  // Join / leave DM room
  useEffect(() => {
    if (!socket || !activeConvo) return;
    socket.emit('join-dm', activeConvo.id);
    return () => socket.emit('leave-dm', activeConvo.id);
  }, [socket, activeConvo?.id]);

  // Fetch messages for active convo
  useEffect(() => {
    if (!activeConvo) return;
    (async () => {
      try {
        const res = await fetch(`/api/dms/${activeConvo.id}/messages`, {
          headers: { 'Authorization': `Bearer ${token}` },
        });
        if (res.ok) {
          const data = await res.json();
          setMessages(data.messages || data);
        }
      } catch (err) {
        console.error('Failed to load DM messages', err);
      }
    })();
  }, [activeConvo?.id, token]);

  // Scroll to bottom
  useEffect(() => {
    messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
  }, [messages]);

  // Socket listeners
  useEffect(() => {
    if (!socket) return;

    const onNewDm = (msg) => {
      if (activeConvo && msg.conversation_id === activeConvo.id) {
        setMessages(prev => [...prev, msg]);
      }
    };

    const onDmTyping = ({ userId, username, conversationId }) => {
      if (!activeConvo || conversationId !== activeConvo.id || userId === user.id) return;
      setTypingUsers(prev => {
        if (prev.find(u => u.userId === userId)) return prev;
        return [...prev, { userId, username }];
      });
      if (typingTimers.current[userId]) clearTimeout(typingTimers.current[userId]);
      typingTimers.current[userId] = setTimeout(() => {
        setTypingUsers(prev => prev.filter(u => u.userId !== userId));
      }, 3000);
    };

    socket.on('new-dm-message', onNewDm);
    socket.on('user-dm-typing', onDmTyping);
    return () => {
      socket.off('new-dm-message', onNewDm);
      socket.off('user-dm-typing', onDmTyping);
    };
  }, [socket, activeConvo?.id, user?.id]);

  const handleEdit = async (messageId, content) => {
    try {
      const res = await fetch(`/api/messages/${messageId}`, {
        method: 'PUT',
        headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' },
        body: JSON.stringify({ content }),
      });
      if (res.ok) {
        setMessages(prev => prev.map(m => m.id === messageId ? { ...m, content, edited: 1 } : m));
      }
    } catch (err) { console.error(err); }
  };

  const handleDelete = async (messageId) => {
    try {
      const res = await fetch(`/api/messages/${messageId}`, {
        method: 'DELETE',
        headers: { 'Authorization': `Bearer ${token}` },
      });
      if (res.ok) {
        setMessages(prev => prev.filter(m => m.id !== messageId));
      }
    } catch (err) { console.error(err); }
  };

  if (loading) {
    return <div className="flex-1 flex items-center justify-center text-gray-500">Loading…</div>;
  }

  return (
    <div className="flex flex-1 h-full overflow-hidden">
      {/* Conversation list */}
      <div className="w-60 bg-[#111114] border-r border-[#2f2f35] flex flex-col">
        <div className="h-12 flex items-center px-4 font-semibold text-gray-100 border-b border-[#2f2f35]">
          Direct Messages
        </div>
        <div className="flex-1 overflow-y-auto py-2">
          {conversations.length === 0 && (
            <p className="text-gray-500 text-sm px-4 py-2">No conversations yet</p>
          )}
          {conversations.map(convo => {
            const other = convo.username || convo.other_username || 'User';
            const isActive = activeConvo?.id === convo.id;
            return (
              <button
                key={convo.id}
                onClick={() => { setActiveConvo(convo); setMessages([]); setReplyTo(null); }}
                className={`w-full flex items-center gap-3 px-4 py-2 text-left transition-colors duration-200 ${
                  isActive ? 'bg-[#25252a] text-gray-100' : 'text-gray-400 hover:bg-[#1e1e22] hover:text-gray-200'
                }`}
              >
                <div className="w-8 h-8 rounded-full bg-[#3a3a42] flex items-center justify-center text-xs font-bold text-gray-300 flex-shrink-0">
                  {convo.avatar ? (
                    <img src={convo.avatar} alt="" className="w-8 h-8 rounded-full object-cover" />
                  ) : (
                    other[0]?.toUpperCase()
                  )}
                </div>
                <span className="truncate text-sm">{other}</span>
              </button>
            );
          })}
        </div>
      </div>

      {/* Message area */}
      <div className="flex-1 flex flex-col bg-[#0e0e10]">
        {!activeConvo ? (
          <div className="flex-1 flex items-center justify-center text-gray-500">
            Select a conversation to start messaging
          </div>
        ) : (
          <>
            <div className="h-12 flex items-center px-4 border-b border-[#2f2f35]">
              <span className="font-semibold text-gray-100">{activeConvo.username || activeConvo.other_username || 'User'}</span>
            </div>
            <div className="flex-1 overflow-y-auto px-4 py-4 space-y-1">
              {messages.map(msg => (
                <Message
                  key={msg.id}
                  message={msg}
                  user={user}
                  token={token}
                  onReply={(m) => setReplyTo(m)}
                  onEdit={handleEdit}
                  onDelete={handleDelete}
                />
              ))}
              <div ref={messagesEndRef} />
            </div>
            <TypingIndicator typingUsers={typingUsers} />
            <MessageInput
              token={token}
              channelId={activeConvo.id}
              socket={socket}
              isDm
              replyTo={replyTo}
              onClearReply={() => setReplyTo(null)}
            />
          </>
        )}
      </div>
    </div>
  );
}

/* ────────────────────────────────────────────
   Main: ChatArea
   ──────────────────────────────────────────── */
export default function ChatArea({ server, channel, token, user, socket, onToggleMemberList, showMemberList }) {
  const [messages, setMessages] = useState([]);
  const [loading, setLoading] = useState(false);
  const [hasMore, setHasMore] = useState(true);
  const [typingUsers, setTypingUsers] = useState([]);
  const [replyTo, setReplyTo] = useState(null);
  const messagesEndRef = useRef(null);
  const containerRef = useRef(null);
  const typingTimers = useRef({});
  const prevScrollHeight = useRef(0);
  const isInitialLoad = useRef(true);

  // ── DM mode ──
  if (!server) {
    return <DMView token={token} user={user} socket={socket} />;
  }

  // ── Channel mode ──

  // Fetch messages
  const fetchMessages = useCallback(async (before) => {
    if (!channel) return;
    setLoading(true);
    try {
      let url = `/api/channels/${channel.id}/messages?limit=50`;
      if (before) url += `&before=${before}`;
      const res = await fetch(url, {
        headers: { 'Authorization': `Bearer ${token}` },
      });
      if (!res.ok) throw new Error('Failed to fetch messages');
      const data = await res.json();
      const fetched = data.messages || [];
      if (fetched.length < 50) setHasMore(false);
      if (before) {
        setMessages(prev => [...fetched, ...prev]);
      } else {
        setMessages(fetched);
        isInitialLoad.current = true;
      }
    } catch (err) {
      console.error(err);
    } finally {
      setLoading(false);
    }
  }, [channel?.id, token]);

  // Join channel & load messages on channel change
  useEffect(() => {
    if (!channel) return;
    setMessages([]);
    setHasMore(true);
    setReplyTo(null);
    setTypingUsers([]);
    fetchMessages();

    if (socket) socket.emit('join-channel', channel.id);
    return () => {
      if (socket) socket.emit('leave-channel', channel.id);
    };
  }, [channel?.id, socket]);

  // Scroll to bottom on initial load or new messages
  useEffect(() => {
    if (isInitialLoad.current && messages.length > 0) {
      messagesEndRef.current?.scrollIntoView();
      isInitialLoad.current = false;
    }
  }, [messages]);

  // Scroll to bottom on new message (only if already near bottom)
  const scrollToBottomIfNeeded = useCallback(() => {
    const c = containerRef.current;
    if (!c) return;
    const isNearBottom = c.scrollHeight - c.scrollTop - c.clientHeight < 150;
    if (isNearBottom) {
      messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
    }
  }, []);

  // Socket events
  useEffect(() => {
    if (!socket || !channel) return;

    const onNewMessage = (msg) => {
      if (msg.channel_id !== channel.id) return;
      setMessages(prev => [...prev, msg]);
      setTimeout(scrollToBottomIfNeeded, 50);
    };

    const onEdited = ({ id, content, edited }) => {
      setMessages(prev => prev.map(m => m.id === id ? { ...m, content, edited } : m));
    };

    const onDeleted = ({ id }) => {
      setMessages(prev => prev.filter(m => m.id !== id));
    };

    const onReactionAdded = ({ messageId, emoji, userId }) => {
      setMessages(prev => prev.map(m => {
        if (m.id !== messageId) return m;
        const reactions = [...(m.reactions || [])];
        const idx = reactions.findIndex(r => r.emoji === emoji);
        if (idx >= 0) {
          const users = reactions[idx].users ? reactions[idx].users.split(',') : [];
          if (!users.includes(String(userId))) users.push(String(userId));
          reactions[idx] = { ...reactions[idx], users: users.join(',') };
        } else {
          reactions.push({ emoji, users: String(userId) });
        }
        return { ...m, reactions };
      }));
    };

    const onReactionRemoved = ({ messageId, emoji, userId }) => {
      setMessages(prev => prev.map(m => {
        if (m.id !== messageId) return m;
        let reactions = [...(m.reactions || [])];
        const idx = reactions.findIndex(r => r.emoji === emoji);
        if (idx >= 0) {
          let users = reactions[idx].users ? reactions[idx].users.split(',') : [];
          users = users.filter(uid => uid !== String(userId));
          if (users.length === 0) {
            reactions.splice(idx, 1);
          } else {
            reactions[idx] = { ...reactions[idx], users: users.join(',') };
          }
        }
        return { ...m, reactions };
      }));
    };

    const onTyping = ({ userId, username, channelId }) => {
      if (channelId !== channel.id || userId === user?.id) return;
      setTypingUsers(prev => {
        if (prev.find(u => u.userId === userId)) return prev;
        return [...prev, { userId, username }];
      });
      if (typingTimers.current[userId]) clearTimeout(typingTimers.current[userId]);
      typingTimers.current[userId] = setTimeout(() => {
        setTypingUsers(prev => prev.filter(u => u.userId !== userId));
      }, 3000);
    };

    socket.on('new-message', onNewMessage);
    socket.on('message-edited', onEdited);
    socket.on('message-deleted', onDeleted);
    socket.on('reaction-added', onReactionAdded);
    socket.on('reaction-removed', onReactionRemoved);
    socket.on('user-typing', onTyping);

    return () => {
      socket.off('new-message', onNewMessage);
      socket.off('message-edited', onEdited);
      socket.off('message-deleted', onDeleted);
      socket.off('reaction-added', onReactionAdded);
      socket.off('reaction-removed', onReactionRemoved);
      socket.off('user-typing', onTyping);
    };
  }, [socket, channel?.id, user?.id, scrollToBottomIfNeeded]);

  // Infinite scroll
  const handleScroll = () => {
    const c = containerRef.current;
    if (!c || loading || !hasMore) return;
    if (c.scrollTop < 100) {
      prevScrollHeight.current = c.scrollHeight;
      const oldest = messages[0];
      if (oldest) {
        fetchMessages(oldest.id).then(() => {
          requestAnimationFrame(() => {
            if (containerRef.current) {
              containerRef.current.scrollTop = containerRef.current.scrollHeight - prevScrollHeight.current;
            }
          });
        });
      }
    }
  };

  const handleEdit = async (messageId, content) => {
    try {
      const res = await fetch(`/api/messages/${messageId}`, {
        method: 'PUT',
        headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' },
        body: JSON.stringify({ content }),
      });
      if (res.ok) {
        setMessages(prev => prev.map(m => m.id === messageId ? { ...m, content, edited: 1 } : m));
      }
    } catch (err) { console.error(err); }
  };

  const handleDelete = async (messageId) => {
    try {
      const res = await fetch(`/api/messages/${messageId}`, {
        method: 'DELETE',
        headers: { 'Authorization': `Bearer ${token}` },
      });
      if (res.ok) {
        setMessages(prev => prev.filter(m => m.id !== messageId));
      }
    } catch (err) { console.error(err); }
  };

  if (!channel) {
    return (
      <div className="flex-1 flex items-center justify-center bg-[#0e0e10] text-gray-500">
        <div className="text-center">
          <h2 className="text-2xl font-bold text-gray-300 mb-2">Welcome to {server?.name || 'the server'}</h2>
          <p>Select a channel to start chatting</p>
        </div>
      </div>
    );
  }

  return (
    <div className="flex-1 flex flex-col bg-[#0e0e10] min-w-0">
      {/* Header */}
      <div className="h-12 flex items-center px-4 border-b border-[#FFD700]/20 flex-shrink-0">
        <span className="text-gray-400 mr-1 font-mono">#</span>
        <span className="font-semibold text-gray-100">{channel.name}</span>
        {channel.description && (
          <>
            <div className="w-px h-5 bg-[#3a3a42] mx-3" />
            <span className="text-gray-500 text-sm truncate">{channel.description}</span>
          </>
        )}
        <div className="ml-auto">
          <button
            onClick={onToggleMemberList}
            className={`p-1.5 rounded transition-colors duration-200 ${
              showMemberList ? 'text-[#FFD700]' : 'text-gray-400 hover:text-gray-200'
            }`}
            title="Toggle member list"
          >
            <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
              <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0z" />
            </svg>
          </button>
        </div>
      </div>

      {/* Messages */}
      <div
        ref={containerRef}
        onScroll={handleScroll}
        className="flex-1 overflow-y-auto px-4 py-4 space-y-1"
      >
        {loading && messages.length === 0 && (
          <div className="flex items-center justify-center py-8 text-gray-500">Loading messages…</div>
        )}
        {!hasMore && messages.length > 0 && (
          <div className="text-center text-gray-600 text-sm py-4">Beginning of channel history</div>
        )}
        {loading && messages.length > 0 && (
          <div className="text-center text-gray-500 text-sm py-2">Loading older messages…</div>
        )}
        {messages.map(msg => (
          <Message
            key={msg.id}
            message={msg}
            user={user}
            token={token}
            onReply={(m) => setReplyTo(m)}
            onEdit={handleEdit}
            onDelete={handleDelete}
          />
        ))}
        <div ref={messagesEndRef} />
      </div>

      {/* Typing indicator */}
      <TypingIndicator typingUsers={typingUsers} />

      {/* Input */}
      <MessageInput
        token={token}
        channelId={channel.id}
        socket={socket}
        replyTo={replyTo}
        onClearReply={() => setReplyTo(null)}
      />
    </div>
  );
}
