package com.app.simplitend.chats;

import android.location.Location;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.app.simplitend.chats.mvvm.Author;
import com.app.simplitend.chats.mvvm.Message;
import com.app.simplitend.chats.mvvm.Receiver;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;

import io.socket.client.IO;
import io.socket.client.Socket;
import io.socket.emitter.Emitter;

public class SocketHelper {

    private static final String TAG = "SOCKET_CHAT_TAG";

    public static final String kHost = "http://simplitendapp.betadelivery.com:3002/";

    private static SocketHelper instance;
    private Socket mSocket;
    private SockCallBack socketCallback;

    private SocketHelper() {
        try {
            IO.Options options = new IO.Options();
            options.forceNew = true;
            options.reconnection = true;
            mSocket = IO.socket(kHost, options);

            mSocket.on(Socket.EVENT_CONNECT, new Emitter.Listener() {
                @Override
                public void call(Object... args) {
                    Log.d(TAG, "Socket connected ");
                    if (socketCallback != null){
                        socketCallback.onSocketConnected();
                    }
                }
            });

            mSocket.on(Socket.EVENT_DISCONNECT, new Emitter.Listener() {
                @Override
                public void call(Object... args) {
                    Log.d(TAG, "Socket disconnected ");
                    if (socketCallback != null){
                        socketCallback.onDisconnected();
                    }
                }
            });

            mSocket.on(Socket.EVENT_CONNECT_ERROR, new Emitter.Listener() {
                @Override
                public void call(Object... args) {
                    Exception e = (Exception) args[0];
                    Log.e(TAG, "call: ", e);
                    if (socketCallback != null){
                        socketCallback.onConnectionError(e);
                    }
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
            Log.e(TAG, "SocketHelper: ", e);
        }
    }

    public static synchronized SocketHelper getInstance() {
        if (instance == null) {
            instance = new SocketHelper();
        }
        return instance;
    }

    public void establishConnection(@Nullable SockCallBack sockCallBack) {
        this.socketCallback = sockCallBack;
        if (!mSocket.connected()) {
            mSocket.connect();
        }else if (sockCallBack != null) {
             socketCallback.onSocketConnected();
        }
    }

    public boolean isConnected(){
        return mSocket.connected();
    }

    public void closeConnection() {
        if (mSocket.connected()) {
            mSocket.disconnect();
        }
    }

    public void getMessage(final Callback<Message> callback,
                           @NonNull String sender_id,
                           @NonNull String channel_id,
                           String receiver_image,
                           String author_image) {
        mSocket.on(channel_id, new Emitter.Listener() {
            @Override
            public void call(Object... args) {
                try {
                    if (args.length >= 4) {
                        String received_sender_id = (String) args[1];
                        String message_txt = (String) args[2];
//                      2023-11-24T12:30:41.652Z
                        String date = (String) args[3]; // mm/dd/yyyy',' HH:mm:ss a

                        Date message_date;
                        try {
                            SimpleDateFormat sdf =
                                    new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
                            sdf.setTimeZone(TimeZone.getTimeZone("UTC"));

                            message_date = sdf.parse(date);
                            if (message_date == null) throw new Exception();
                        }catch (Exception e){
                            message_date = Calendar.getInstance().getTime();
                        }

                        Message message;

                        if (sender_id.equals(received_sender_id)){
                            // Author message
                            message = new Message(new Author(received_sender_id, author_image), message_txt, message_date);
                            callback.onMessageSentSuccessfully();
                        }else{
                            // receiver message
                            message = new Message(new Receiver(received_sender_id, receiver_image), message_txt, message_date);
                        }

                        callback.onMessageReceived(message);
                    }
                } catch (Exception e) {
                    callback.onError(e);
                }
            }
        });

        Log.d(TAG, "LISTENER FOR CHANNEL ID : " + channel_id);
    }

    public void stopMessages(@NonNull String channel_id){
        mSocket.off(channel_id);
    }

    public void sendMessage(@NonNull String message, @NonNull String sender_id, @NonNull String receiver_id, @NonNull String channel_id, String user_principal_id) {
        mSocket.emit("chatMessage", sender_id, message, receiver_id, channel_id, user_principal_id);
    }

    private static final String LOCATION_EVENT_PREFIX = "patientCurrentLocation";

    public void sendLocationUpdates(Location location, String patient_id){
        mSocket.emit("currentLocationUpdate", location.getLatitude() + "", location.getLongitude() + "",
                LOCATION_EVENT_PREFIX + patient_id);
    }

    public void getLocationUpdates(String patient_id, Callback<Location> callback){
        mSocket.on(LOCATION_EVENT_PREFIX + patient_id, new Emitter.Listener() {
            @Override
            public void call(Object... args) {
                try {
                    if (args.length >= 2) {
                        String received_lat = (String) args[0];
                        String received_lng = (String) args[1];

                        try {
                            Location location = new Location("received_location");
                            location.setLatitude(Double.parseDouble(received_lat));
                            location.setLongitude(Double.parseDouble(received_lng));

                            if (callback != null){
                                callback.onMessageReceived(location);
                            }
                        }catch (Exception e){
                            if (callback != null){
                                callback.onError(e);
                            }
                        }

                        if (callback != null) {
                            callback.onMessageSentSuccessfully();
                        }
                    }else{
                        if (callback != null) {
                            callback.onError(new Exception("LOCATION UPDATES RECEIVED WITH INVALID ARGS " + args));
                        }
                    }
                } catch (Exception e) {
                    if (callback != null){
                        callback.onError(e);
                    }
                }
            }
        });
    }

    public void removeLocationUpdateListener(String patientId) {
        mSocket.off(LOCATION_EVENT_PREFIX+patientId);
    }

    public interface Callback<T> {
        void onMessageReceived(T result);
        void onMessageSentSuccessfully();
        void onError(Exception e);
    }

    public interface SockCallBack{
        void onSocketConnected();
        void onConnectionError(Exception e);
        void onDisconnected();
    }
}

