package org.deke.risk.riskahead.helper;

import android.Manifest;
import android.app.NotificationManager;
import android.app.PendingIntent;

import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.*;
import androidx.core.app.ActivityCompat;
import androidx.core.app.NotificationCompat;
import androidx.core.app.TaskStackBuilder;
import android.util.Log;

import com.android.volley.Request;
import com.android.volley.toolbox.StringRequest;
import com.google.android.gms.maps.model.LatLng;
import com.google.firebase.crashlytics.FirebaseCrashlytics;

import org.deke.risk.riskahead.MapsActivity;
import org.deke.risk.riskahead.R;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

/**
 *
 * (C) Copyright 2016 RiskAhead (http://www.riskahead.net)
 * Developer & Contributor: Dennis Thießen (dennis.thiessen@riskahead.net)
 * Date of Release: 10.02.2016
 *
 */
public class NotificationService extends Service {
    private final static String TAG = NotificationService.class.getSimpleName();
    private final static String EXTRA_MESSAGE = "org.deke.risk.riskahead.MESSAGE";

    Handler mNotifyHandler = new Handler();
    NotificationManager mNotificationManager;

    private static final int MIN_DISTANCE_CHANGE_FOR_UPDATES = 10; // 10 meters
    private static final int MIN_TIME_BW_UPDATES = 1000 * 60; // 1 minute
    private static int gpscounter = 0;

    public SessionManager session;

    public HashMap<String, String> user;

    private LocationManager locationManager;
    private LocationListener locationListener;

    private LatLng myPosition;

    public NotificationService() {
        super();
    }

    @Override
    public void onCreate() {
        mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

        session = new SessionManager(getApplicationContext());
        user = session.getUserDetails();

        Log.d(TAG, "NotificationService created. Starting notification task...");
        mNotifyTask.run();
    }

    private void initAndGetPosition() {
        Log.d(TAG,"Trying to get a position. GPS enabled: "+session.isGPSForNotificationsEnabled());
        if(session.isGPSForNotificationsEnabled()) {

            if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
                locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
                locationListener = new MyLocationListener();

                if (locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
                    Log.d(TAG, "Locationservice enabled - GPS Provider Found");

                    locationManager.requestLocationUpdates(
                            LocationManager.GPS_PROVIDER,
                            MIN_TIME_BW_UPDATES,
                            MIN_DISTANCE_CHANGE_FOR_UPDATES, locationListener);
                    Log.d(TAG, "GPS Provider enabled");

                    if (locationManager != null) {
                        Location tmpLocation = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
                        if (tmpLocation != null) {
                            Log.d(TAG,"Last known position found from GPS Provider");
                            removeLocationListener();
                            session.setLocation(Double.doubleToRawLongBits(tmpLocation.getLatitude()), Double.doubleToRawLongBits(tmpLocation.getLongitude()));
                            myPosition = session.getLocation();
                            session.setNotificationServiceReady(true);
                        }
                    }
                } else if (locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {
                    // get location from Network Provider
                    Log.d(TAG, "Locationservice enabled - Network Provider Found");
                    locationManager.requestLocationUpdates(
                            LocationManager.NETWORK_PROVIDER,
                            MIN_TIME_BW_UPDATES,
                            MIN_DISTANCE_CHANGE_FOR_UPDATES, locationListener);
                    Log.d(TAG, "Network Provider enabled");
                    if (locationManager != null) {
                        Location tmpLocation = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
                        if (tmpLocation != null) {
                            Log.d(TAG,"Last known position found from Network Provider");
                            removeLocationListener();
                            session.setLocation(Double.doubleToRawLongBits(tmpLocation.getLatitude()), Double.doubleToRawLongBits(tmpLocation.getLongitude()));
                            myPosition = session.getLocation();
                            session.setNotificationServiceReady(true);
                        }
                    }
                }
            }
        }else{
            myPosition = session.getManualLocation();
        }
    }

    private void removeLocationListener() {
        if(locationManager != null && locationListener != null){
            locationManager.removeUpdates(locationListener);
            locationListener = null;
            locationManager = null;
        }
    }

    private class MyLocationListener implements LocationListener {
        @Override
        public void onLocationChanged(Location loc) {
            Log.d(TAG,"onLocationChanged: "+gpscounter);
            session.setLocation(Double.doubleToRawLongBits(loc.getLatitude()), Double.doubleToRawLongBits(loc.getLongitude()));
            session.setNotificationServiceReady(true);

            if(gpscounter > 3){
                removeLocationListener();
            }

            gpscounter++;
        }

        @Override
        public void onProviderDisabled(String provider) {}

        @Override
        public void onProviderEnabled(String provider) {}

        @Override
        public void onStatusChanged(String provider, int status, Bundle extras) {}
    }

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

    Runnable mNotifyTask = new Runnable() {
        @Override
        public void run() {
            android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND);

            boolean notifyEnabled = session.getNotificationEnabled();
            int radius = session.getNotificationRadius();
            int pollFrequency = session.getNotificationPollFreq() * 1000 * 60;

            Log.d(this.toString(), "Run Notification Task. Notifications enabled = " + notifyEnabled);

            if(notifyEnabled && session.isLoggedIn()){
                initAndGetPosition();

                if ((myPosition != null) && (myPosition.latitude != 0.0) && (myPosition.longitude != 0.0)) {
                    session.setNotificationServiceReady(true);
                    String lastNotificationTime;

                    if (session.getLastNotification().equals("")) {
                        lastNotificationTime = user.get(SessionManager.KEY_LASTLOGIN_AT);
                    } else {
                        lastNotificationTime = session.getLastNotification();
                    }

                    Log.d(this.toString(), "Lookup location with position: " + myPosition + " Radius: "+ radius +" PollFrequency: "+ pollFrequency +" lastNotifyTime: " + lastNotificationTime + " GPSActivated: "+session.isGPSForNotificationsEnabled());

                    StringRequest strReq = getStringRequestIncidentsFromAreaAndTime(myPosition.latitude, myPosition.longitude, radius, lastNotificationTime);
                    String tag_string_req = "req_incidents";
                    AppController.getInstance().addToRequestQueue(strReq, tag_string_req);
                }else{
                    Log.d(TAG,"No position found. No notification send.");
                    session.setNotificationServiceReady(false);
                }
            }


            mNotifyHandler.postDelayed(mNotifyTask, pollFrequency);
        }
    };

    private StringRequest getStringRequestIncidentsFromAreaAndTime(final Double latitude, final Double longitude, final int radius, final String time) {
        return new StringRequest(Request.Method.POST, AppConfig.REST_GET_INCIDENTS_FROMAREATIME, response -> {
            Log.d(TAG, "Incident notifications: " + response);

            try {
                JSONObject jObj = new JSONObject(response);
                boolean error = jObj.getBoolean("error");

                if (!error) {
                    JSONArray notification = jObj.getJSONArray("msg");
                    sentNotification(notification);
                } else {
                    String errorMsg = jObj.getString("error_msg");
                    FirebaseCrashlytics.getInstance().log("Error getting incident notification (server returned error): " + errorMsg);
                }
            } catch (JSONException e) {
                FirebaseCrashlytics.getInstance().log("JSONException caught");
                FirebaseCrashlytics.getInstance().recordException(e);
            }

        }, error -> FirebaseCrashlytics.getInstance().log("Error getting incident notification: " + error.getMessage())) {

            @Override
            protected Map<String, String> getParams() {
                Map<String, String> params = new HashMap<>();
                params.put("uid", user.get(SessionManager.KEY_UID));
                params.put("token", user.get(SessionManager.TOKEN));
                params.put("latitude", Double.toString(latitude));
                params.put("longitude", Double.toString(longitude));
                params.put("radius", Integer.toString(radius));
                params.put("time", time);
                return params;
            }
        };
    }

    private void sentNotification(JSONArray notification) {
        String title;
        String context;

        if(notification.length() == 1){
            title = getString(R.string.txt_notification_title);
            context = notification.length() + " " + getString(R.string.txt_notification_context);
        }else{
            title = getString(R.string.txt_notification_title_multi);
            context = notification.length() + " " + getString(R.string.txt_notification_context_multi);
        }
        NotificationCompat.Builder mBuilder =
                new NotificationCompat.Builder(getApplicationContext(), "MyChannelId_01")
                        .setSmallIcon(R.drawable.logo_380)
                        .setAutoCancel(true)
                        .setContentTitle(title)
                        .setContentText(context);


        Intent resultIntent = new Intent(this, MapsActivity.class);

        try {
            resultIntent.putExtra(EXTRA_MESSAGE, notification.getJSONObject(notification.length()-1).getDouble("latitude")+":"+notification.getJSONObject(notification.length()-1).getDouble("longitude"));
        } catch (JSONException e) {
            FirebaseCrashlytics.getInstance().log("JSONException caught");
            FirebaseCrashlytics.getInstance().recordException(e);
        }

        TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
        stackBuilder.addParentStack(MapsActivity.class);

        stackBuilder.addNextIntent(resultIntent);
        PendingIntent resultPendingIntent =
                stackBuilder.getPendingIntent(
                        0,
                        PendingIntent.FLAG_UPDATE_CURRENT
                );
        mBuilder.setContentIntent(resultPendingIntent);

        mNotificationManager.notify(1, mBuilder.build());
        session.setLastNotification(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US).format(Calendar.getInstance().getTime()));
    }

    @Override
    public void onDestroy(){
        if(locationManager != null && locationListener != null) locationManager.removeUpdates(locationListener);

        super.onDestroy();

        Log.d(TAG, "Destroy NotificationService");
    }
}
