package com.app.simplitend.patientgeofencing;

import static com.app.simplitend.chats.SocketHelper.kHost;
import static com.app.simplitend.locationupdates.LocationService.LOCATION_INTERVAL_BASE_TIME;
import static com.app.simplitend.locationupdates.LocationService.LOCATION_UPDATE_MIN_INTERVAL;
import static com.app.simplitend.patientgeofencing.GeoFenceHelper.GEOFENCE_TAG;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.location.Location;
import android.os.Build;
import android.util.Log;

import androidx.annotation.NonNull;

import com.app.simplitend.apputils.AppUtil;
import com.app.simplitend.apputils.RetrofitHelper;
import com.app.simplitend.caregiverdashboard.mvvm.NotificationApiService;
import com.app.simplitend.chats.SocketHelper;
import com.app.simplitend.locationupdates.LocationService;
import com.app.simplitend.welcome.welcomepatient.mvvm.models.CallResponse;
import com.google.android.gms.location.Geofence;
import com.google.android.gms.location.GeofencingEvent;

import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

import io.socket.client.IO;
import io.socket.client.Socket;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class GeoFenceBroadcastReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);

        if (geofencingEvent == null || geofencingEvent.hasError()) {
            Log.d(GEOFENCE_TAG, "onReceive: Couldn't add geofence");
            return;
        }

        int transition_type = geofencingEvent.getGeofenceTransition();
        Location location = geofencingEvent.getTriggeringLocation();

//        String senior_address = null;
//        if (location != null){
//            senior_address = AppUtil.getCompleteAddress(context, location.getLatitude(), location.getLongitude());
//        }
//
//        if (senior_address == null) {
//            senior_address = "Unable to locate";
//        }

        double distance;
        try {
            String[] homeLatLng = AppUtil.getPatientLatLng(context);

            Location homeLocation = new Location("homeLatLng");
            homeLocation.setLatitude(Double.parseDouble(homeLatLng[0]));
            homeLocation.setLongitude(Double.parseDouble(homeLatLng[1]));

            if (location == null) throw new Exception();

            distance = homeLocation.distanceTo(location);
            distance /= 1609; // converting to miles
        } catch (Exception e) {
            distance = 0;
        }

        switch (transition_type) {
            case Geofence.GEOFENCE_TRANSITION_DWELL:
                Log.d(GEOFENCE_TAG, "onReceive: DWELL");
                break;
            case Geofence.GEOFENCE_TRANSITION_ENTER:
                Log.d(GEOFENCE_TAG, "onReceive: ENTER");
                Intent locationServiceIntent = new Intent(context, LocationService.class);
                locationServiceIntent.setAction(LocationService.ACTION_START_LOCATION_UPDATES);
                locationServiceIntent.putExtra(LOCATION_UPDATE_MIN_INTERVAL, LOCATION_INTERVAL_BASE_TIME);
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                    context.startForegroundService(intent);
                } else {
                    context.startService(intent);
                }
                break;
            case Geofence.GEOFENCE_TRANSITION_EXIT:
                Log.d(GEOFENCE_TAG, "onReceive: EXIT");
                notifyOutOfGeofence(context, String.format(Locale.getDefault(), "%.2f", distance));

                notifyPatient(context);
                break;
        }
    }

    private void notifyPatient(Context context) {
        Log.d(GEOFENCE_TAG, "notifyPatient: SENDING MESSAGE TO SENIOR PHONE FROM CAREGIVER PHONE");

        String message = AppUtil.getPatientGeofenceMessage(context);
        if (message == null || message.trim().isEmpty()) {
            message = "You are too far from home";
        }

        int[] chatsCred = AppUtil.getPatientGeofenceChatCred(context);
        if (chatsCred[0] == -1 || chatsCred[1] == -1 || chatsCred[2] == -1) {
            Log.d(GEOFENCE_TAG, "notifyPatient: CANNOT SEND MESSAGE AS EITHER PAT_ID, CG_ID OR CHANNEL_ID IS NOT AVAILABLE");
            return;
        }

        Log.d(GEOFENCE_TAG, "notifyPatient: " + chatsCred[2]);
        // sending message
        sendMessage(message, chatsCred[0], chatsCred[1], chatsCred[2], chatsCred[3]);
    }

    private void sendMessage(@NonNull String message, int patientId, int cg_id, int channel_id, int patient_principal_id) {
        SocketHelper socketHelper = SocketHelper.getInstance();

        Log.d(GEOFENCE_TAG, "doInBackground: CONNECTING TO SOCKET");

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

            mSocket.on(Socket.EVENT_CONNECT, args -> {
                Log.d(GEOFENCE_TAG, "Socket connected ");
                socketHelper.sendMessage(message, cg_id + "", patientId + "", channel_id + "",
                        patient_principal_id < 0 ? null : patient_principal_id + "");
                mSocket.disconnect();
            });

            mSocket.on(Socket.EVENT_DISCONNECT, args -> Log.d(GEOFENCE_TAG, "Socket disconnected "));

            mSocket.on(Socket.EVENT_CONNECT_ERROR, args -> {
                Exception e = (Exception) args[0];
                Log.e(GEOFENCE_TAG, "call: ", e);
            });

            mSocket.connect();
        } catch (Exception e) {
            e.printStackTrace();
            Log.e(GEOFENCE_TAG, "SocketHelper: ", e);
        }
    }

    private void notifyOutOfGeofence(Context context, String senior_address) {
        Log.d(GEOFENCE_TAG, "Sending notification to patient");
        Log.d(GEOFENCE_TAG, "Current location: " + senior_address);
        NotificationApiService apiService = RetrofitHelper.getRetrofit().create(NotificationApiService.class);

        Map<String, String> body = new HashMap<>();
        body.put("patient_id", AppUtil.getPatientUid(context) + "");
        body.put("address", senior_address);

        apiService.notifyOutOfGeoFence(body, "Bearer " + AppUtil.getPatientToken(context)).enqueue(new Callback<CallResponse<Object>>() {
            @Override
            public void onResponse(Call<CallResponse<Object>> call, Response<CallResponse<Object>> response) {
                if (response.code() == 200) {
                    Log.d(GEOFENCE_TAG, "OUT OF GEOFENCE NOTIFICATION SENT SUCCESSFULLY.");
                } else {
                    Log.d(GEOFENCE_TAG, "Couldn't notify patient " + response.message());
                }
            }

            @Override
            public void onFailure(Call<CallResponse<Object>> call, Throwable t) {
                Log.d(GEOFENCE_TAG, "Couldn't notify patient due to " + t);
            }
        });

        // getting faster location updates as patient is out of geofence
        Intent intent = new Intent(context, LocationService.class);
        intent.setAction(LocationService.ACTION_START_LOCATION_UPDATES);
        intent.putExtra(LOCATION_UPDATE_MIN_INTERVAL, 5 * 1000); // every 5 seconds
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            context.startForegroundService(intent);
        } else {
            context.startService(intent);
        }
    }
}