package com.example.petstoremobile.services;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.example.petstoremobile.api.ChatApi;
import com.example.petstoremobile.api.CustomerApi;
import com.example.petstoremobile.api.auth.TokenManager;
import com.example.petstoremobile.dtos.ConversationDTO;
import com.example.petstoremobile.dtos.CustomerDTO;
import com.example.petstoremobile.dtos.MessageDTO;
import com.example.petstoremobile.dtos.PageResponse;
import com.example.petstoremobile.utils.NotificationHelper;
import com.example.petstoremobile.utils.RetrofitUtils;
import com.example.petstoremobile.websocket.StompChatManager;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.inject.Inject;
import javax.inject.Named;

import dagger.hilt.android.AndroidEntryPoint;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

// Service to receive notifications when a new conversation is created
@AndroidEntryPoint
public class ChatNotificationService extends Service {
    private static final String TAG = "ChatNotificationService";

    public static Long activeConversationIdInUi = null;
    
    private StompChatManager stompChatManager;
    private final Set<Long> knownConversationIds = new HashSet<>();
    private final Map<Long, Long> conversationToCustomerId = new HashMap<>();
    private final Map<Long, String> customerIdToName = new HashMap<>();
    private Long currentUserId;

    @Inject CustomerApi customerApi;
    @Inject ChatApi chatApi;
    @Inject TokenManager tokenManager;
    @Inject @Named("baseUrl") String baseUrl;

    //When the service starts, connect to the websocket
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "Service started");
        connectWebSocket();
        return START_STICKY;
    }

    // helper function to connect to the websocket
    private void connectWebSocket() {
        //get the token and role from the shared preferences
        String token = tokenManager.getToken();
        String role = tokenManager.getRole();
        currentUserId = tokenManager.getUserId();

        if (token != null && stompChatManager == null) {
            customerApi.getAllCustomers(0, 1000).enqueue(RetrofitUtils.createSilentCallback(TAG, result -> {
                for (CustomerDTO customer : result.getContent()) {
                    customerIdToName.put(customer.getCustomerId(), customer.getFullName());
                }
                loadConversationsAndStartStomp(token, role);
            }));
        }
    }

    private void loadConversationsAndStartStomp(String token, String role) {
        // Fetch existing conversations
        chatApi.getAllConversations().enqueue(RetrofitUtils.createSilentCallback(TAG, result -> {
            for (ConversationDTO conversation : result) {
                if (conversation.getId() != null) {
                    knownConversationIds.add(conversation.getId());
                    conversationToCustomerId.put(conversation.getId(), conversation.getCustomerId());
                    // subscribe to existing conversations to get message notifications
                    if (stompChatManager != null) {
                        stompChatManager.subscribeToConversation(conversation.getId());
                    }
                }
            }
            Log.d(TAG, "Loaded " + knownConversationIds.size() + " existing conversations");
            startStomp(token, role);
        }));
    }

    private void startStomp(String token, String role) {
        if (stompChatManager != null) return;

        stompChatManager = new StompChatManager(token, role, baseUrl);

        // Listen for messages in existing conversations
        stompChatManager.setMessageListener(message -> {
            if (message != null && !message.getSenderId().equals(currentUserId)) {
                // Check if this conversation is already active in the view
                //if it is then don't make a notification for this chat
                if (activeConversationIdInUi != null && activeConversationIdInUi.equals(message.getConversationId())) {
                    Log.d(TAG, "Disable notification for active conversation: " + message.getConversationId());
                    return;
                }

                String title = "New Message";
                Long customerId = conversationToCustomerId.get(message.getConversationId());
                if (customerId != null && customerIdToName.containsKey(customerId)) {
                    //append the customer name to the title of the notification
                    title = "New message from " + customerIdToName.get(customerId);
                }
                
                NotificationHelper.showNotification(
                        getApplicationContext(),
                        title,
                        message.getContent(),
                        message.getConversationId()
                );
            }
        });

        //When a conversation gets created, show a notification
        stompChatManager.setConversationListener(conversation -> {
            //check if the conversation exists
            if (conversation != null && conversation.getId() != null) {
                //check if the conversation is new
                if (!knownConversationIds.contains(conversation.getId())) {
                    //add the conversation to the set of known conversations
                    knownConversationIds.add(conversation.getId());
                    conversationToCustomerId.put(conversation.getId(), conversation.getCustomerId());

                    // Subscribe to the new conversation's messages
                    stompChatManager.subscribeToConversation(conversation.getId());
                    
                    String title = "New Support Request";
                    if (customerIdToName.containsKey(conversation.getCustomerId())) {
                        //append the customer name to the title of the notification
                        title = "New Support Request from " + customerIdToName.get(conversation.getCustomerId());
                    } else {
                        // Try to fetch customer name for the new request
                        fetchCustomerName(conversation.getCustomerId());
                    }

                    //Display a notification
                    NotificationHelper.showNotification(
                            getApplicationContext(),
                            title,
                            "A customer is requesting assistance",
                            conversation.getId()
                    );
                }
            }
        });

        // Subscribe to existing conversations if they were already loaded
        for (Long id : knownConversationIds) {
            stompChatManager.subscribeToConversation(id);
        }

        stompChatManager.setConnectionListener(new StompChatManager.ConnectionListener() {
            @Override
            public void onSocketOpened() {
                Log.d(TAG, "WebSocket connected in service");
            }

            @Override
            public void onSocketClosed() { Log.d(TAG, "WebSocket closed in service"); }

            @Override
            public void onSocketError() { Log.e(TAG, "WebSocket error in service"); }
        });
        stompChatManager.connect();
    }

    // Helper function to fetch customer name for a conversation
    private void fetchCustomerName(Long customerId) {
        customerApi.getCustomerById(customerId).enqueue(RetrofitUtils.createSilentCallback(TAG, result -> {
            customerIdToName.put(customerId, result.getFullName());
        }));
    }

    //When the service is destroyed, disconnect from the websocket
    @Override
    public void onDestroy() {
        if (stompChatManager != null) {
            stompChatManager.disconnect();
        }
        super.onDestroy();
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}
