import { create } from 'zustand';
import { ChatApi } from '../infrastructure/chatApi';
import { useAuthStore } from '../../auth/application/authStore';
import type { Chat, ChatMember, Message, TypingUser } from '../../../core/domain/types';

interface ChatState {
  chats: Chat[];
  activeChat: string | null;
  messages: Record<string, Message[]>;
  pinnedMessages: Record<string, Message[]>;
  typingUsers: TypingUser[];
  replyTo: Message | null;
  editingMessage: Message | null;
  isLoadingChats: boolean;
  isLoadingMessages: boolean;
  searchQuery: string;
  drafts: Record<string, string>;
  hasMoreMessages: Record<string, boolean>;

  setActiveChat: (chatId: string | null) => void;
  setSearchQuery: (query: string) => void;
  setDraft: (chatId: string, text: string) => void;
  getDraft: (chatId: string) => string;
  loadChats: () => Promise<void>;
  loadMessages: (chatId: string, reset?: boolean, isHistory?: boolean) => Promise<void>;
  addMessage: (message: Message) => void;
  updateMessage: (message: Message) => void;
  removeMessage: (messageId: string, chatId: string) => void;
  removeMessages: (messageIds: string[], chatId: string) => void;
  hideMessages: (messageIds: string[], chatId: string) => void;
  addReaction: (messageId: string, chatId: string, userId: string, username: string, emoji: string) => void;
  removeReaction: (messageId: string, chatId: string, userId: string, emoji: string) => void;
  markRead: (chatId: string, userId: string, lastReadSequenceId: number) => void;
  markAllAsRead: (chatId: string) => void;
  addTypingUser: (chatId: string, userId: string) => void;
  removeTypingUser: (chatId: string, userId: string) => void;
  updateUserOnlineStatus: (userId: string, isOnline: boolean, lastSeen?: string) => void;
  setReplyTo: (message: Message | null) => void;
  setEditingMessage: (message: Message | null) => void;
  addChat: (chat: Chat) => void;
  updateChat: (chat: Chat) => void;
  removeChat: (chatId: string) => void;
  clearMessages: (chatId: string) => void;
  setPinnedMessage: (chatId: string, message: Message) => void;
  removePinnedMessage: (chatId: string, messageId: string, newPinned?: Message[] | null) => void;
  jumpToMessage: (chatId: string, sequenceId: number) => Promise<void>;
  clearStore: () => void;
}

export const useChatStore = create<ChatState>((set, get) => ({
  chats: [],
  activeChat: null,
  messages: {},
  pinnedMessages: {},
  typingUsers: [],
  replyTo: null,
  editingMessage: null,
  isLoadingChats: false,
  isLoadingMessages: false,
  searchQuery: '',
  drafts: JSON.parse(localStorage.getItem('knot_drafts') || '{}'),
  hasMoreMessages: {},

  setActiveChat: (chatId) => set((state) => ({
    activeChat: chatId,
    replyTo: null,
    editingMessage: null,
    chats: chatId
      ? state.chats.map((c) => c.id === chatId ? { ...c, unreadCount: 0 } : c)
      : state.chats,
  })),
  setSearchQuery: (query) => set({ searchQuery: query }),

  setDraft: (chatId, text) => {
    set((state) => {
      const drafts = { ...state.drafts };
      if (text.trim()) {
        drafts[chatId] = text;
      } else {
        delete drafts[chatId];
      }
      localStorage.setItem('knot_drafts', JSON.stringify(drafts));
      return { drafts };
    });
  },

  getDraft: (chatId) => {
    return get().drafts[chatId] || '';
  },

  loadChats: async () => {
    try {
      set({ isLoadingChats: true });
      const chats = await ChatApi.getChats();
      // Auto-create favorites chat if not present
      if (!chats.some((c: any) => c.type === 'favorites')) {
        try {
          const favChat = await ChatApi.getOrCreateFavorites();
          chats.unshift(favChat);
        } catch { }
      }
      // Extract pinned messages from chats
      const pinnedMessages: Record<string, Message[]> = {};
      for (const chat of chats) {
        if (chat.pinnedMessages && chat.pinnedMessages.length > 0) {
          pinnedMessages[chat.id] = chat.pinnedMessages.map((pm: any) => pm.message);
        }
      }
      set({ chats, pinnedMessages, isLoadingChats: false });
    } catch (error: any) {
      console.error('Load chats error:', error);
      set({ isLoadingChats: false });
      const { addNotification } = (await import('../../../core/application/stores/notificationStore')).useNotificationStore.getState();
      addNotification('error', error.message || 'Failed to load chats');
    }
  },

  loadMessages: async (chatId, reset = false, isHistory = false) => {
    try {
      const state = get();
      if (!reset && !isHistory && typeof state.hasMoreMessages[chatId] !== 'undefined') return;
      if (!reset && isHistory && state.messages[chatId] && state.hasMoreMessages[chatId] === false) return;
      if (state.isLoadingMessages) return;

      set({ isLoadingMessages: true });
      const currentMessages = state.messages[chatId] || [];
      const cursor = !reset && currentMessages.length > 0 ? currentMessages[0].sequenceId.toString() : undefined;

      const fetched = await ChatApi.getMessages(chatId, cursor);

      set((state) => {
        // Merge fetched messages with any that arrived via socket
        const existing = reset ? [] : (state.messages[chatId] || []);
        const fetchedIds = new Set(fetched.map(m => m.id));
        const socketOnly = existing.filter(m => !fetchedIds.has(m.id));
        const merged = [...fetched, ...socketOnly].sort((a, b) => {
          const tDiff = new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime();
          return tDiff !== 0 ? tDiff : a.sequenceId - b.sequenceId;
        });
        return {
          messages: { ...state.messages, [chatId]: merged },
          hasMoreMessages: { ...state.hasMoreMessages, [chatId]: fetched.length >= 50 },
          isLoadingMessages: false,
        };
      });
    } catch (error: any) {
      console.error('Load messages error:', error);
      set({ isLoadingMessages: false });
      const { addNotification } = (await import('../../../core/application/stores/notificationStore')).useNotificationStore.getState();
      addNotification('error', error.message || 'Failed to load messages');
    }
  },

  addMessage: (message) => {
    const userId = useAuthStore.getState().user?.id;
    set((state) => {
      const chatMessages = state.messages[message.chatId] || [];
      if (chatMessages.some((m) => m.id === message.id)) return state;

      const updatedMessages = {
        ...state.messages,
        [message.chatId]: [...chatMessages, message],
      };

      const updatedChats = state.chats.map((chat) => {
        if (chat.id === message.chatId) {
          return {
            ...chat,
            messages: [message],
            unreadCount: (chat.id === state.activeChat || message.senderId === userId) ? chat.unreadCount : chat.unreadCount + 1,
          };
        }
        return chat;
      });

      updatedChats.sort((a, b) => {
        const aPin = a.members?.find((m) => m.user?.id === userId)?.isPinned ? 1 : 0;
        const bPin = b.members?.find((m) => m.user?.id === userId)?.isPinned ? 1 : 0;
        if (aPin !== bPin) return bPin - aPin;
        const aTime = a.messages[0]?.createdAt || a.createdAt;
        const bTime = b.messages[0]?.createdAt || b.createdAt;
        return new Date(bTime).getTime() - new Date(aTime).getTime();
      });

      return { messages: updatedMessages, chats: updatedChats };
    });
  },

  updateMessage: (message) => {
    set((state) => {
      const chatMessages = state.messages[message.chatId] || [];
      const updatedMessages = chatMessages.map((m) => (m.id === message.id ? { ...m, ...message } : m));

      const updatedChats = state.chats.map((chat) => {
        if (chat.id === message.chatId) {
          return {
            ...chat,
            messages: chat.messages?.map((m) => (m.id === message.id ? { ...m, ...message } : m)),
          };
        }
        return chat;
      });

      return {
        messages: {
          ...state.messages,
          [message.chatId]: updatedMessages,
        },
        chats: updatedChats,
      };
    });
  },

  removeMessage: (messageId, chatId) => {
    set((state) => {
      const chatMessages = state.messages[chatId] || [];
      const updatedMessages = chatMessages.map((m) =>
        m.id === messageId ? { ...m, isDeleted: true, content: null } : m
      );

      // Find the latest non-deleted message to show in sidebar
      const latestVisible = updatedMessages
        .filter(m => !m.isDeleted)
        .sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime())[0];

      const updatedChats = state.chats.map((chat) => {
        if (chat.id === chatId) {
          // If the deleted message was the last message shown, replace with previous one
          const currentLast = chat.messages?.[0];
          if (currentLast?.id === messageId) {
            return {
              ...chat,
              messages: latestVisible ? [latestVisible] : [{ ...currentLast, isDeleted: true, content: null }],
            };
          }
        }
        return chat;
      });

      return {
        messages: {
          ...state.messages,
          [chatId]: updatedMessages,
        },
        chats: updatedChats,
      };
    });
  },

  removeMessages: (messageIds, chatId) => {
    const idsSet = new Set(messageIds);
    set((state) => {
      const chatMessages = state.messages[chatId] || [];
      const updatedMessages = chatMessages.map((m) =>
        idsSet.has(m.id) ? { ...m, isDeleted: true, content: null } : m
      );

      const latestVisible = updatedMessages
        .filter(m => !m.isDeleted)
        .sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime())[0];

      const updatedChats = state.chats.map((chat) => {
        if (chat.id === chatId) {
          const currentLast = chat.messages?.[0];
          if (currentLast && idsSet.has(currentLast.id)) {
            return {
              ...chat,
              messages: latestVisible ? [latestVisible] : [{ ...currentLast, isDeleted: true, content: null }],
            };
          }
        }
        return chat;
      });

      return {
        messages: { ...state.messages, [chatId]: updatedMessages },
        chats: updatedChats,
      };
    });
  },

  hideMessages: (messageIds, chatId) => {
    const idsSet = new Set(messageIds);
    set((state) => {
      const chatMessages = state.messages[chatId] || [];
      const updatedMessages = chatMessages.filter((m) => !idsSet.has(m.id));

      const latestVisible = updatedMessages
        .filter(m => !m.isDeleted)
        .sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime())[0];

      const updatedChats = state.chats.map((chat) => {
        if (chat.id === chatId) {
          const currentLast = chat.messages?.[0];
          if (currentLast && idsSet.has(currentLast.id)) {
            return {
              ...chat,
              messages: latestVisible ? [latestVisible] : [],
            };
          }
        }
        return chat;
      });

      return {
        messages: { ...state.messages, [chatId]: updatedMessages },
        chats: updatedChats,
      };
    });
  },

  addReaction: (messageId, chatId, userId, username, emoji) => {
    console.log('[ChatStore] addReaction called:', { messageId, chatId, userId, username, emoji });
    set((state) => {
      const chatMessages = state.messages[chatId] || [];
      const updateMsg = (m: Message) => {
        if (m.id === messageId) {
          const reactions = m.reactions || [];
          const exists = reactions.some((r) => r.userId === userId && r.emoji === emoji);
          if (exists) {
            console.log('[ChatStore] Reaction already exists, skipping');
            return m;
          }
          console.log('[ChatStore] Adding reaction to message:', m.id);
          return {
            ...m,
            reactions: [
              ...reactions,
              { id: `${messageId}-${userId}-${emoji}`, emoji, userId, user: { id: userId, username, displayName: username } },
            ],
          };
        }
        return m;
      };

      const updatedMessages = chatMessages.map(updateMsg);
      const updatedChats = state.chats.map((chat) => {
        if (chat.id === chatId) {
          return {
            ...chat,
            messages: chat.messages?.map(updateMsg),
          };
        }
        return chat;
      });

      console.log('[ChatStore] State updated for chatId:', chatId);
      return {
        messages: {
          ...state.messages,
          [chatId]: updatedMessages,
        },
        chats: updatedChats,
      };
    });
  },

  removeReaction: (messageId, chatId, userId, emoji) => {
    console.log('[ChatStore] removeReaction called:', { messageId, chatId, userId, emoji });
    set((state) => {
      const chatMessages = state.messages[chatId] || [];
      const updateMsg = (m: Message) => {
        if (m.id === messageId) {
          console.log('[ChatStore] Removing reaction from message:', m.id);
          return {
            ...m,
            reactions: (m.reactions || []).filter((r) => !(r.userId === userId && r.emoji === emoji)),
          };
        }
        return m;
      };

      const updatedMessages = chatMessages.map(updateMsg);
      const updatedChats = state.chats.map((chat) => {
        if (chat.id === chatId) {
          return {
            ...chat,
            messages: chat.messages?.map(updateMsg),
          };
        }
        return chat;
      });

      console.log('[ChatStore] State updated for chatId:', chatId);
      return {
        messages: {
          ...state.messages,
          [chatId]: updatedMessages,
        },
        chats: updatedChats,
      };
    });
  },

  markRead: (chatId, userId, lastReadSequenceId) => {
    const currentUserId = useAuthStore.getState().user?.id;
    set((state) => {
      const chatMessages = state.messages[chatId] || [];
      let newlyReadCount = 0;
      const updateMsg = (m: Message) => {
        if (m.sequenceId <= lastReadSequenceId) {
          const alreadyRead = m.readBy?.some((r) => r.userId === userId);
          if (alreadyRead) return m;
          // Увеличиваем счётчик только если текущий пользователь читает чужие сообщения
          if (userId === currentUserId && m.senderId !== currentUserId) newlyReadCount++;
          return { ...m, readBy: [...(m.readBy || []), { userId }] };
        }
        return m;
      };

      const updatedMessages = chatMessages.map(updateMsg);

      const updatedChats = state.chats.map((chat) => {
        if (chat.id === chatId) {
          const updatedLastMessages = chat.messages?.map(updateMsg);
          // Уменьшаем unreadCount только если текущий пользователь прочитал сообщения
          if (userId === currentUserId) {
            return { ...chat, messages: updatedLastMessages, unreadCount: Math.max(0, (chat.unreadCount || 0) - newlyReadCount) };
          }
          return { ...chat, messages: updatedLastMessages };
        }
        return chat;
      });

      return {
        messages: {
          ...state.messages,
          [chatId]: updatedMessages,
        },
        chats: updatedChats,
      };
    });
  },

  markAllAsRead: (chatId) => {
    set((state) => {
      const updatedChats = state.chats.map((chat) => {
        if (chat.id === chatId) {
          return { ...chat, unreadCount: 0 };
        }
        return chat;
      });
      return { chats: updatedChats };
    });
  },

  addTypingUser: (chatId, userId) => {
    set((state) => {
      const exists = state.typingUsers.some((t) => t.chatId === chatId && t.userId === userId);
      if (exists) return state;
      return { typingUsers: [...state.typingUsers, { chatId, userId }] };
    });
  },

  removeTypingUser: (chatId, userId) => {
    set((state) => ({
      typingUsers: state.typingUsers.filter((t) => !(t.chatId === chatId && t.userId === userId)),
    }));
  },

  updateUserOnlineStatus: (userId, isOnline, lastSeen) => {
    set((state) => ({
      chats: state.chats.map((chat) => ({
        ...chat,
        members: chat.members.map((m) =>
          m.user.id === userId
            ? { ...m, user: { ...m.user, isOnline, lastSeen: lastSeen || m.user.lastSeen } }
            : m
        ),
      })),
    }));
  },

  setReplyTo: (message) => set({ replyTo: message, editingMessage: null }),
  setEditingMessage: (message) => set({ editingMessage: message, replyTo: null }),

  addChat: (chat) => {
    set((state) => {
      const existing = state.chats.find((c) => c.id === chat.id);

      const messagesFromState = state.messages[chat.id] || [];
      const messagesToUse = messagesFromState.length > 0 ? messagesFromState : (chat.messages || []);

      let unreadCount = chat.unreadCount || 0;
      if (!existing && messagesFromState.length > 0) {
        const userId = useAuthStore.getState().user?.id;
        unreadCount = messagesFromState.filter((m) => m.senderId !== userId && !m.readBy?.some(r => r.userId === userId)).length;
      }

      const updatedChat = { ...chat, messages: messagesToUse.length > 0 ? [messagesToUse[messagesToUse.length - 1]] : [], unreadCount };

      if (existing) {
        return {
          chats: state.chats.map((c) => (c.id === chat.id ? { ...c, ...updatedChat } : c)),
        };
      }
      return { chats: [updatedChat, ...state.chats] };
    });
  },

  updateChat: (chat) => {
    set((state) => ({
      chats: state.chats.map((c) => (c.id === chat.id ? { ...c, ...chat } : c)),
    }));
  },

  removeChat: (chatId) => {
    set((state) => ({
      chats: state.chats.filter((c) => c.id !== chatId),
      activeChat: state.activeChat === chatId ? null : state.activeChat,
      messages: (() => { const m = { ...state.messages }; delete m[chatId]; return m; })(),
    }));
  },

  clearMessages: (chatId) => {
    set((state) => ({
      messages: { ...state.messages, [chatId]: [] },
      chats: state.chats.map((c) =>
        c.id === chatId ? { ...c, messages: [] } : c
      ),
    }));
  },

  setPinnedMessage: (chatId, message) => {
    set((state) => {
      const existing = state.pinnedMessages[chatId] || [];
      if (existing.some(m => m.id === message.id)) return state;
      return {
        pinnedMessages: {
          ...state.pinnedMessages,
          [chatId]: [...existing, message]
        },
      };
    });
  },

  removePinnedMessage: (chatId, messageId, newPinned?) => {
    set((state) => {
      const updated = { ...state.pinnedMessages };
      if (newPinned) {
        updated[chatId] = newPinned;
      } else {
        const filtered = (updated[chatId] || []).filter(m => m.id !== messageId);
        if (filtered.length === 0) delete updated[chatId];
        else updated[chatId] = filtered;
      }
      return { pinnedMessages: updated };
    });
  },

  jumpToMessage: async (chatId, sequenceId) => {
    try {
      set({ isLoadingMessages: true });
      const fetched = await ChatApi.getMessages(chatId, undefined, sequenceId, 50);

      set((state) => ({
        messages: { ...state.messages, [chatId]: fetched },
        // Since we jumped, we assume there is more history to load above
        hasMoreMessages: { ...state.hasMoreMessages, [chatId]: true },
        isLoadingMessages: false,
      }));
    } catch (error: any) {
      console.error('Jump to message error:', error);
      set({ isLoadingMessages: false });
      const { addNotification } = (await import('../../../core/application/stores/notificationStore')).useNotificationStore.getState();
      addNotification('error', error.message || 'Failed to jump to message');
    }
  },

  clearStore: () => {
    set({
      chats: [],
      activeChat: null,
      messages: {},
      pinnedMessages: {},
      typingUsers: [],
      replyTo: null,
      editingMessage: null,
    });
  },
}));
