import { create } from "zustand";
import { api } from "../lib/api.ts";
import { type Reaction } from "./message.ts";

export interface ConversationMember {
  id: string;
  username: string;
  display_name: string;
  avatar: string;
}

export interface Conversation {
  id: string;
  type: "dm" | "group_dm";
  name: string;
  members: ConversationMember[];
  created_at: string;
}

export interface ConversationMessage {
  id: string;
  conversation_id: string;
  author_id: string;
  author_username: string;
  author_display_name: string | null;
  content: string;
  created_at: string;
  edited_at: string | null;
  reactions?: Reaction[];
}

interface ConversationState {
  conversations: Conversation[];
  activeConversationId: string | null;
  messagesByConversation: Record<string, ConversationMessage[]>;
  isLoading: boolean;
  isLoadingOlder: boolean;
  hasMoreByConversation: Record<string, boolean>;
  error: string | null;
  fetchConversations: () => Promise<void>;
  createConversation: (userIds: string[]) => Promise<Conversation>;
  setActiveConversation: (id: string | null) => void;
  fetchMessages: (conversationId: string, before?: string) => Promise<void>;
  fetchOlderMessages: (conversationId: string) => Promise<void>;
  sendMessage: (conversationId: string, content: string) => Promise<ConversationMessage>;
  addMessage: (message: ConversationMessage) => void;
  updateMessage: (message: ConversationMessage) => void;
  deleteMessage: (conversationId: string, messageId: string) => void;
  addReaction: (conversationId: string, messageId: string, emoji: string, userId: string) => void;
  removeReaction: (conversationId: string, messageId: string, emoji: string, userId: string) => void;
}

export const useConversationStore = create<ConversationState>((set, get) => ({
  conversations: [],
  activeConversationId: null,
  messagesByConversation: {},
  isLoading: false,
  isLoadingOlder: false,
  hasMoreByConversation: {},
  error: null,

  fetchConversations: async () => {
    try {
      const conversations = await api.get<Conversation[]>("/conversations");
      set({ conversations: Array.isArray(conversations) ? conversations : [] });
    } catch (error) {
      set({
        error: error instanceof Error ? error.message : "Failed to fetch conversations",
      });
    }
  },

  createConversation: async (userIds) => {
    const conversation = await api.post<Conversation>("/conversations", { user_ids: userIds });
    set((state) => ({
      conversations: [conversation, ...state.conversations],
      activeConversationId: conversation.id,
    }));
    return conversation;
  },

  setActiveConversation: (id) => set({ activeConversationId: id }),

  fetchMessages: async (conversationId, before) => {
    set({ isLoading: true });
    try {
      const params = before ? "?before=" + encodeURIComponent(before) : "";
      const messages = await api.get<ConversationMessage[]>(
        `/conversations/${conversationId}/messages${params}`,
      );
      const list = Array.isArray(messages) ? messages : [];
      set((state) => ({
        messagesByConversation: {
          ...state.messagesByConversation,
          [conversationId]: list,
        },
        hasMoreByConversation: {
          ...state.hasMoreByConversation,
          [conversationId]: list.length >= 50,
        },
        isLoading: false,
      }));
    } catch (error) {
      set({ isLoading: false, error: error instanceof Error ? error.message : "Failed" });
    }
  },

  fetchOlderMessages: async (conversationId) => {
    const state = get();
    if (state.isLoadingOlder || state.hasMoreByConversation[conversationId] === false) return;
    const existing = state.messagesByConversation[conversationId] || [];
    if (existing.length === 0) return;
    // ponytail: existing is now oldest-first, so existing[0] is the true oldest
    const oldestId = existing[0].id;
    set({ isLoadingOlder: true });
    try {
      const older = await api.get<ConversationMessage[]>(
        `/conversations/${conversationId}/messages?before=${encodeURIComponent(oldestId)}`,
      );
      const list = Array.isArray(older) ? older : [];
      set((state) => ({
        messagesByConversation: {
          ...state.messagesByConversation,
          [conversationId]: [...list, ...existing],
        },
        hasMoreByConversation: {
          ...state.hasMoreByConversation,
          [conversationId]: list.length >= 50,
        },
        isLoadingOlder: false,
      }));
    } catch {
      set({ isLoadingOlder: false });
    }
  },

  sendMessage: async (conversationId, content) => {
    // don't append here — WS MESSAGE_CREATE broadcast is the single source of truth.
    // Appending in both places caused double messages when the WS event arrived before the HTTP response.
    const message = await api.post<ConversationMessage>(
      `/conversations/${conversationId}/messages`,
      { content },
    );
    return message;
  },

  addMessage: (message) => {
    set((state) => {
      const existing = state.messagesByConversation[message.conversation_id] || [];
      if (existing.some((m) => m.id === message.id)) {
        return state;
      }
      return {
        messagesByConversation: {
          ...state.messagesByConversation,
          [message.conversation_id]: [...existing, message]
            .sort((a, b) => new Date(a.created_at).getTime() - new Date(b.created_at).getTime()),
        },
      };
    });
  },
  updateMessage: (message) => {
    set((state) => {
      const convMsgs = state.messagesByConversation[message.conversation_id] || [];
      return {
        messagesByConversation: {
          ...state.messagesByConversation,
          [message.conversation_id]: convMsgs.map((m) => (m.id === message.id ? message : m)),
        },
      };
    });
  },
  deleteMessage: (conversationId, messageId) => {
    set((state) => {
      const convMsgs = state.messagesByConversation[conversationId] || [];
      return {
        messagesByConversation: {
          ...state.messagesByConversation,
          [conversationId]: convMsgs.filter((m) => m.id !== messageId),
        },
      };
    });
  },

  addReaction: (conversationId, messageId, emoji, userId) => {
    set((state) => {
      const messages = state.messagesByConversation[conversationId];
      if (!messages) return state;

      const newMessages = messages.map((m) => {
        if (m.id !== messageId) return m;

        const reactions = [...(m.reactions || [])];
        const existing = reactions.find((r) => r.emoji === emoji);

        if (existing) {
          if (!existing.users.includes(userId)) {
            existing.users.push(userId);
            existing.count++;
          }
        } else {
          reactions.push({ emoji, count: 1, users: [userId] });
        }

        return { ...m, reactions };
      });

      return {
        messagesByConversation: {
          ...state.messagesByConversation,
          [conversationId]: newMessages,
        },
      };
    });
  },

  removeReaction: (conversationId, messageId, emoji, userId) => {
    set((state) => {
      const messages = state.messagesByConversation[conversationId];
      if (!messages) return state;

      const newMessages = messages.map((m) => {
        if (m.id !== messageId) return m;

        let reactions = [...(m.reactions || [])];
        const existing = reactions.find((r) => r.emoji === emoji);

        if (existing) {
          existing.users = existing.users.filter((id) => id !== userId);
          existing.count--;
          if (existing.count <= 0) {
            reactions = reactions.filter((r) => r.emoji !== emoji);
          }
        }

        return { ...m, reactions };
      });

      return {
        messagesByConversation: {
          ...state.messagesByConversation,
          [conversationId]: newMessages,
        },
      };
    });
  },
}));
