import React, { createContext, useContext, useReducer, useCallback } from 'react';
import { 
  MessagingContextType, 
  MessagingState, 
  Message, 
  Conversation, 
  Notification, 
  TypingUser, 
  UserStatus, 
  NotificationStatistics 
} from '../types';
import { messagingApi } from '../services/messagingApi';

// Initial state
const initialState: MessagingState = {
  conversations: [],
  currentConversation: null,
  messages: [],
  notifications: [],
  typingUsers: [],
  onlineUsers: [],
  isLoading: false,
  error: null,
  unreadCount: 0,
};

// Action types
type MessagingAction =
  | { type: 'SET_LOADING'; payload: boolean }
  | { type: 'SET_ERROR'; payload: string | null }
  | { type: 'SET_CONVERSATIONS'; payload: Conversation[] }
  | { type: 'ADD_CONVERSATION'; payload: Conversation }
  | { type: 'UPDATE_CONVERSATION'; payload: Conversation }
  | { type: 'REMOVE_CONVERSATION'; payload: number }
  | { type: 'SET_CURRENT_CONVERSATION'; payload: Conversation | null }
  | { type: 'SET_MESSAGES'; payload: Message[] }
  | { type: 'ADD_MESSAGE'; payload: Message }
  | { type: 'UPDATE_MESSAGE'; payload: Message }
  | { type: 'REMOVE_MESSAGE'; payload: number }
  | { type: 'SET_NOTIFICATIONS'; payload: Notification[] }
  | { type: 'ADD_NOTIFICATION'; payload: Notification }
  | { type: 'UPDATE_NOTIFICATION'; payload: Notification }
  | { type: 'REMOVE_NOTIFICATION'; payload: number }
  | { type: 'SET_TYPING_USERS'; payload: TypingUser[] }
  | { type: 'ADD_TYPING_USER'; payload: TypingUser }
  | { type: 'REMOVE_TYPING_USER'; payload: number }
  | { type: 'SET_ONLINE_USERS'; payload: UserStatus[] }
  | { type: 'UPDATE_USER_STATUS'; payload: UserStatus }
  | { type: 'SET_UNREAD_COUNT'; payload: number }
  | { type: 'MARK_MESSAGES_READ'; payload: { conversationId: number; messageIds?: number[] } }
  | { type: 'CLEAR_MESSAGES' };

// Reducer
function messagingReducer(state: MessagingState, action: MessagingAction): MessagingState {
  switch (action.type) {
    case 'SET_LOADING':
      return { ...state, isLoading: action.payload };
    
    case 'SET_ERROR':
      return { ...state, error: action.payload };
    
    case 'SET_CONVERSATIONS':
      return { ...state, conversations: action.payload };
    
    case 'ADD_CONVERSATION':
      return { 
        ...state, 
        conversations: [action.payload, ...state.conversations.filter(c => c.id !== action.payload.id)]
      };
    
    case 'UPDATE_CONVERSATION':
      return {
        ...state,
        conversations: state.conversations.map(c => 
          c.id === action.payload.id ? action.payload : c
        ),
        currentConversation: state.currentConversation?.id === action.payload.id ? action.payload : state.currentConversation
      };
    
    case 'REMOVE_CONVERSATION':
      return {
        ...state,
        conversations: state.conversations.filter(c => c.id !== action.payload),
        currentConversation: state.currentConversation?.id === action.payload ? null : state.currentConversation
      };
    
    case 'SET_CURRENT_CONVERSATION':
      return { ...state, currentConversation: action.payload };
    
    case 'SET_MESSAGES':
      return { ...state, messages: action.payload };
    
    case 'ADD_MESSAGE':
      return { ...state, messages: [...state.messages, action.payload] };
    
    case 'UPDATE_MESSAGE':
      return {
        ...state,
        messages: state.messages.map(m => m.id === action.payload.id ? action.payload : m)
      };
    
    case 'REMOVE_MESSAGE':
      return { ...state, messages: state.messages.filter(m => m.id !== action.payload) };
    
    case 'SET_NOTIFICATIONS':
      return { ...state, notifications: action.payload };
    
    case 'ADD_NOTIFICATION':
      return { ...state, notifications: [action.payload, ...state.notifications] };
    
    case 'UPDATE_NOTIFICATION':
      return {
        ...state,
        notifications: state.notifications.map(n => 
          n.id === action.payload.id ? action.payload : n
        )
      };
    
    case 'REMOVE_NOTIFICATION':
      return { ...state, notifications: state.notifications.filter(n => n.id !== action.payload) };
    
    case 'SET_TYPING_USERS':
      return { ...state, typingUsers: action.payload };
    
    case 'ADD_TYPING_USER':
      return {
        ...state,
        typingUsers: [
          ...state.typingUsers.filter(u => u.user_id !== action.payload.user_id),
          action.payload
        ]
      };
    
    case 'REMOVE_TYPING_USER':
      return { ...state, typingUsers: state.typingUsers.filter(u => u.user_id !== action.payload) };
    
    case 'SET_ONLINE_USERS':
      return { ...state, onlineUsers: action.payload };
    
    case 'UPDATE_USER_STATUS':
      return {
        ...state,
        onlineUsers: [
          ...state.onlineUsers.filter(u => u.user_id !== action.payload.user_id),
          action.payload
        ]
      };
    
    case 'SET_UNREAD_COUNT':
      return { ...state, unreadCount: action.payload };
    
    case 'MARK_MESSAGES_READ':
      return {
        ...state,
        messages: state.messages.map(m => 
          m.conversation_id === action.payload.conversationId && 
          (!action.payload.messageIds || action.payload.messageIds.includes(m.id))
            ? { ...m, is_read: true }
            : m
        )
      };
    
    case 'CLEAR_MESSAGES':
      return { ...state, messages: [] };
    
    default:
      return state;
  }
}

// Context
const MessagingContext = createContext<MessagingContextType | undefined>(undefined);

// Provider component
export const MessagingProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
  const [state, dispatch] = useReducer(messagingReducer, initialState);

  // API functions
  const sendMessage = useCallback(async (
    conversationId: number, 
    content: string, 
    type: 'text' | 'system' | 'file' | 'image' = 'text',
    replyTo?: number
  ) => {
    try {
      dispatch({ type: 'SET_LOADING', payload: true });
      dispatch({ type: 'SET_ERROR', payload: null });

      const response = await messagingApi.messages.sendMessage({
        conversation_id: conversationId,
        content,
        type,
        reply_to: replyTo
      });

      if (response.status === 'success' && response.data) {
        dispatch({ type: 'ADD_MESSAGE', payload: response.data.message });
      } else {
        throw new Error(response.message || 'Failed to send message');
      }
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : 'Failed to send message';
      dispatch({ type: 'SET_ERROR', payload: errorMessage });
      throw error;
    } finally {
      dispatch({ type: 'SET_LOADING', payload: false });
    }
  }, []);

  const createConversation = useCallback(async (
    type: 'direct' | 'group',
    participantIds: number[],
    name?: string,
    description?: string
  ): Promise<Conversation> => {
    try {
      dispatch({ type: 'SET_LOADING', payload: true });
      dispatch({ type: 'SET_ERROR', payload: null });

      const response = await messagingApi.conversations.createConversation({
        type,
        participant_ids: participantIds,
        name,
        description
      });

      if (response.status === 'success' && response.data) {
        dispatch({ type: 'ADD_CONVERSATION', payload: response.data.conversation });
        return response.data.conversation;
      } else {
        throw new Error(response.message || 'Failed to create conversation');
      }
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : 'Failed to create conversation';
      dispatch({ type: 'SET_ERROR', payload: errorMessage });
      throw error;
    } finally {
      dispatch({ type: 'SET_LOADING', payload: false });
    }
  }, []);

  const getMessages = useCallback(async (
    conversationId: number,
    page: number = 1,
    limit: number = 50
  ) => {
    try {
      dispatch({ type: 'SET_LOADING', payload: true });
      dispatch({ type: 'SET_ERROR', payload: null });

      const response = await messagingApi.messages.getMessages({
        conversation_id: conversationId,
        page,
        limit
      });

      if (response.status === 'success' && response.data) {
        if (page === 1) {
          dispatch({ type: 'SET_MESSAGES', payload: response.data.messages });
        } else {
          dispatch({ type: 'SET_MESSAGES', payload: [...response.data.messages, ...state.messages] });
        }
      } else {
        throw new Error(response.message || 'Failed to get messages');
      }
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : 'Failed to get messages';
      dispatch({ type: 'SET_ERROR', payload: errorMessage });
      throw error;
    } finally {
      dispatch({ type: 'SET_LOADING', payload: false });
    }
  }, [state.messages]);

  const markMessagesAsRead = useCallback(async (
    conversationId: number,
    messageIds?: number[]
  ) => {
    try {
      const response = await messagingApi.messages.markMessagesAsRead({
        conversation_id: conversationId,
        message_ids: messageIds
      });

      if (response.status === 'success') {
        dispatch({ 
          type: 'MARK_MESSAGES_READ', 
          payload: { conversationId, messageIds } 
        });
      }
    } catch (error) {
      console.error('Failed to mark messages as read:', error);
    }
  }, []);

  const searchMessages = useCallback(async (
    query: string,
    conversationId?: number
  ): Promise<Message[]> => {
    try {
      const response = await messagingApi.messages.searchMessages({
        q: query,
        conversation_id: conversationId
      });

      if (response.status === 'success' && response.data) {
        return response.data.messages;
      } else {
        throw new Error(response.message || 'Failed to search messages');
      }
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : 'Failed to search messages';
      dispatch({ type: 'SET_ERROR', payload: errorMessage });
      throw error;
    }
  }, []);

  const updateMessage = useCallback(async (messageId: number, content: string) => {
    try {
      const response = await messagingApi.messages.updateMessage(messageId, { content });

      if (response.status === 'success' && response.data) {
        dispatch({ type: 'UPDATE_MESSAGE', payload: response.data.message });
      } else {
        throw new Error(response.message || 'Failed to update message');
      }
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : 'Failed to update message';
      dispatch({ type: 'SET_ERROR', payload: errorMessage });
      throw error;
    }
  }, []);

  const deleteMessage = useCallback(async (messageId: number) => {
    try {
      const response = await messagingApi.messages.deleteMessage(messageId);

      if (response.status === 'success') {
        dispatch({ type: 'REMOVE_MESSAGE', payload: messageId });
      } else {
        throw new Error(response.message || 'Failed to delete message');
      }
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : 'Failed to delete message';
      dispatch({ type: 'SET_ERROR', payload: errorMessage });
      throw error;
    }
  }, []);

  const setTyping = useCallback((conversationId: number, isTyping: boolean) => {
    // Typing functionality removed (socket connection removed)
    console.log('Typing status:', { conversationId, isTyping });
  }, []);

  const setCurrentConversation = useCallback((conversation: Conversation | null) => {
    dispatch({ type: 'SET_CURRENT_CONVERSATION', payload: conversation });
    
    if (conversation) {
      // Load messages for this conversation
      getMessages(conversation.id);
    } else {
      // Clear messages when no conversation is selected
      dispatch({ type: 'CLEAR_MESSAGES' });
    }
  }, [getMessages]);

  const refreshConversations = useCallback(async () => {
    try {
      dispatch({ type: 'SET_LOADING', payload: true });
      const response = await messagingApi.conversations.getConversations();

      if (response.status === 'success' && response.data) {
        dispatch({ type: 'SET_CONVERSATIONS', payload: response.data.conversations });
      } else {
        throw new Error(response.message || 'Failed to get conversations');
      }
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : 'Failed to get conversations';
      dispatch({ type: 'SET_ERROR', payload: errorMessage });
    } finally {
      dispatch({ type: 'SET_LOADING', payload: false });
    }
  }, []);

  const getNotifications = useCallback(async (
    page: number = 1,
    limit: number = 20,
    type?: string,
    isRead?: boolean
  ) => {
    try {
      const response = await messagingApi.notifications.getNotifications({
        page,
        limit,
        type: type as any,
        is_read: isRead
      });

      if (response.status === 'success' && response.data) {
        if (page === 1) {
          dispatch({ type: 'SET_NOTIFICATIONS', payload: response.data.notifications });
        } else {
          dispatch({ type: 'SET_NOTIFICATIONS', payload: [...response.data.notifications, ...state.notifications] });
        }
      } else {
        throw new Error(response.message || 'Failed to get notifications');
      }
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : 'Failed to get notifications';
      dispatch({ type: 'SET_ERROR', payload: errorMessage });
    }
  }, [state.notifications]);

  const markNotificationAsRead = useCallback(async (notificationId: number) => {
    try {
      const response = await messagingApi.notifications.markNotificationAsRead(notificationId);

      if (response.status === 'success') {
        dispatch({
          type: 'UPDATE_NOTIFICATION',
          payload: { ...state.notifications.find(n => n.id === notificationId)!, is_read: true }
        });
      }
    } catch (error) {
      console.error('Failed to mark notification as read:', error);
    }
  }, [state.notifications]);

  const markAllNotificationsAsRead = useCallback(async () => {
    try {
      const response = await messagingApi.notifications.markAllAsRead();

      if (response.status === 'success') {
        dispatch({
          type: 'SET_NOTIFICATIONS',
          payload: state.notifications.map(n => ({ ...n, is_read: true }))
        });
      }
    } catch (error) {
      console.error('Failed to mark all notifications as read:', error);
    }
  }, [state.notifications]);

  const deleteNotification = useCallback(async (notificationId: number) => {
    try {
      const response = await messagingApi.notifications.deleteNotification(notificationId);

      if (response.status === 'success') {
        dispatch({ type: 'REMOVE_NOTIFICATION', payload: notificationId });
      }
    } catch (error) {
      console.error('Failed to delete notification:', error);
    }
  }, []);

  const getNotificationStatistics = useCallback(async (): Promise<NotificationStatistics> => {
    try {
      const response = await messagingApi.notifications.getStatistics();

      if (response.status === 'success' && response.data) {
        return response.data;
      } else {
        throw new Error(response.message || 'Failed to get notification statistics');
      }
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : 'Failed to get notification statistics';
      dispatch({ type: 'SET_ERROR', payload: errorMessage });
      throw error;
    }
  }, []);

  const contextValue: MessagingContextType = {
    ...state,
    sendMessage,
    createConversation,
    getMessages,
    markMessagesAsRead,
    searchMessages,
    updateMessage,
    deleteMessage,
    setTyping,
    setCurrentConversation,
    refreshConversations,
    getNotifications,
    markNotificationAsRead,
    markAllNotificationsAsRead,
    deleteNotification,
    getNotificationStatistics,
  };

  return (
    <MessagingContext.Provider value={contextValue}>
      {children}
    </MessagingContext.Provider>
  );
};

// Hook to use messaging context
export const useMessaging = (): MessagingContextType => {
  const context = useContext(MessagingContext);
  if (context === undefined) {
    throw new Error('useMessaging must be used within a MessagingProvider');
  }
  return context;
};

