package com.app.simplitend.patient_dashboard;

import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.location.Address;
import android.location.Geocoder;
import android.location.Location;
import android.location.LocationManager;
import android.net.Uri;
import android.os.Bundle;
import android.provider.Settings;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.Toast;

import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresPermission;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.lifecycle.ViewModelProvider;

import com.app.simplitend.R;
import com.app.simplitend.apputils.AppUtil;
import com.app.simplitend.apputils.PatientDataCache;
import com.app.simplitend.databinding.ActivityDirectionToHomeBinding;
import com.google.android.gms.location.FusedLocationProviderClient;
import com.google.android.gms.location.LocationListener;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.maps.CameraUpdate;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.LatLngBounds;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.maps.model.PolylineOptions;
import com.google.android.material.bottomsheet.BottomSheetBehavior;
import com.google.maps.DirectionsApi;
import com.google.maps.GeoApiContext;
import com.google.maps.android.PolyUtil;
import com.google.maps.model.DirectionsResult;
import com.google.maps.model.DirectionsRoute;

import java.util.List;
import java.util.Locale;

public class DirectionToHomeActivity extends AppCompatActivity
        implements OnMapReadyCallback, LocationListener {

    protected ActivityDirectionToHomeBinding binding;

    private PatientMainViewModel viewModel;

    public static final String LAT_KEY = "lat_key";
    public static final String LNG_KEY = "lng_key";

    private double pat_lat, pat_lng;
    private double pat_cur_lat, pat_cur_lng;

    private String your_loc;

    private ActivityResultLauncher<String[]> locationPermissionLauncher;
    private ActivityResultLauncher<Intent> enableLocationIntent;
    private FusedLocationProviderClient fusedLocationProviderClient;

    private LocationRequest locationRequest;
    private GoogleMap mGoogleMap;

    private BottomSheetBehavior<LinearLayout> bottomSheetBehavior;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = ActivityDirectionToHomeBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());

        viewModel = new ViewModelProvider(this).get(PatientMainViewModel.class);

        Intent intent = getIntent();
        if (intent != null && intent.hasExtra(LAT_KEY) && intent.hasExtra(LNG_KEY)){
            pat_lat = intent.getDoubleExtra(LAT_KEY, 0);
            pat_lng = intent.getDoubleExtra(LNG_KEY, 0);
        }else {
            finish();
            return;
        }

        getLocationPermission();

        initViews();

        clickEvents();

    }

    @Override
    protected void attachBaseContext(Context newBase) {
        final Configuration configuration = new Configuration(newBase.getResources().getConfiguration());
        configuration.fontScale = Math.min(configuration.fontScale, 1.1f);
        applyOverrideConfiguration(configuration);
        super.attachBaseContext(newBase);
    }

    private void clickEvents() {
        binding.closeBtn.setOnClickListener(v -> {
            if(bottomSheetBehavior.getState() == BottomSheetBehavior.STATE_EXPANDED){
                bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
            }
        });

        binding.goBtn.setOnClickListener(v -> {
            String pat_location = pat_lat + "," + pat_lng;
            Intent intent = new Intent(android.content.Intent.ACTION_VIEW,
                    Uri.parse("https://www.google.com/maps/dir/?api=1&destination=" + pat_location + "&travelmode=walking"));

            startActivity(intent);

//            String current_address = AppUtil.getCompleteAddress(this, pat_lat, pat_lng);
//            if (current_address == null){
//                current_address = "Unable to locate";
//            }

            double distance;
            try {

                Location homeLocation = new Location("homeLatLng");
                homeLocation.setLatitude(pat_lat);
                homeLocation.setLongitude(pat_lng);

                Location currLocation = new Location("currentLocation");
                currLocation.setLatitude(pat_cur_lat);
                currLocation.setLongitude(pat_cur_lng);

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

            viewModel.notifyRequestedDirections(AppUtil.getPatientUid(this)+"",
                    String.format(Locale.getDefault(),"%.2f", distance),
                    AppUtil.getPatientToken(this));
        });

        binding.backBtn.setOnClickListener(v -> {
            onBackPressed();
        });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (fusedLocationProviderClient != null) {
            fusedLocationProviderClient.removeLocationUpdates(this);
        }
    }

    private void initViews() {
        // Obtain the SupportMapFragment and get notified when the map is ready to be used.
        SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
                .findFragmentById(R.id.direction_map);
        if (mapFragment != null) {
            mapFragment.getMapAsync(this);
        } else {
            Toast.makeText(this, "Couldn't load map. Please try again.", Toast.LENGTH_SHORT).show();
        }

        fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this);
        locationRequest = new LocationRequest.Builder(LocationRequest.PRIORITY_HIGH_ACCURACY, 5000)
                .build();

        bottomSheetBehavior = BottomSheetBehavior.from(binding.directionBs);
        bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
        bottomSheetBehavior.addBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
            @Override
            public void onStateChanged(@NonNull View bottomSheet, int newState) {
                if (newState == BottomSheetBehavior.STATE_COLLAPSED){
                    binding.closeBtn.setVisibility(View.GONE);
                }else if (newState == BottomSheetBehavior.STATE_EXPANDED){
                    binding.closeBtn.setVisibility(View.VISIBLE);
                }
            }

            @Override
            public void onSlide(@NonNull View bottomSheet, float slideOffset) {

            }
        });
    }

    private void getLocationPermission() {
        enableLocationIntent = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),
                result -> {
                    if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                        locationPermissionLauncher.launch(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION});
                        return;
                    }

                    drawRoutes();
                });

        // launching request to location permissions
        locationPermissionLauncher = registerForActivityResult(new ActivityResultContracts
                        .RequestMultiplePermissions(), result -> {

                    Boolean fineLocationGranted;

                    if (result.containsKey(Manifest.permission.ACCESS_FINE_LOCATION)) {
                        fineLocationGranted = result.get(Manifest.permission.ACCESS_FINE_LOCATION);
                    } else {
                        fineLocationGranted = false;
                    }

                    Boolean coarseLocationGranted;

                    if (result.containsKey(Manifest.permission.ACCESS_COARSE_LOCATION)) {
                        coarseLocationGranted = result.get(Manifest.permission.ACCESS_COARSE_LOCATION);
                    } else {
                        coarseLocationGranted = false;
                    }

                    if ((fineLocationGranted != null && fineLocationGranted) || (coarseLocationGranted != null && coarseLocationGranted)) {
                        // one of the location access granted.

                        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                            return;
                        }

                        drawRoutes();
                    }
                }
        );
    }

    @RequiresPermission(anyOf = {Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION})
    private void drawRoutes() {
        if (mGoogleMap == null) return;

        mGoogleMap.setMyLocationEnabled(true);
        mGoogleMap.getUiSettings().setMyLocationButtonEnabled(true);

        if (!isLocationProviderEnabled()) {
            AppUtil.showAlert(this,
                    getString(R.string.GPS_ENABLE_REQUIRED),
                    getString(R.string.GPS_ENABLE_MESSAGE),
                    getString(R.string.SETTINGS),
                    (dialog, which) -> {
                        // Settings button click callback
                        // Sending user to turn location on

                        Intent intent = new Intent();
                        intent.setAction(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
                        enableLocationIntent.launch(intent);
                    },
                    getString(R.string.NOT_NOW),
                    (dialog, which) -> dialog.dismiss()
            );

            return;
        }

        fusedLocationProviderClient.requestLocationUpdates(locationRequest, this, null);
    }

    public boolean isLocationProviderEnabled() {
        LocationManager locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
        return locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
    }

    private void addMarkersToMap(DirectionsResult results, GoogleMap mMap) throws Exception {
        pat_cur_lat = results.routes[0].legs[0].startLocation.lat;
        pat_cur_lng = results.routes[0].legs[0].startLocation.lng;
        mMap.addMarker(new MarkerOptions().position(new com.google.android.gms.maps.model.LatLng(results.routes[0].legs[0].startLocation.lat ,results.routes[0].legs[0].startLocation.lng)).title("Your location").icon(BitmapDescriptorFactory.fromResource(R.drawable.img_pat_curr_location)));
        mMap.addMarker(new MarkerOptions().position(new com.google.android.gms.maps.model.LatLng(results.routes[0].legs[0].endLocation.lat ,results.routes[0].legs[0].endLocation.lng)).title("Home location").icon(BitmapDescriptorFactory.fromResource(R.drawable.img_home_marker)));
    }

    private void positionCamera(DirectionsRoute route, GoogleMap mMap) throws Exception{
//        LatLng center_latlng = computeCentroid(new com.google.android.gms.maps.model.LatLng(route.legs[0].startLocation.lat, route.legs[0].startLocation.lng),
//                new com.google.android.gms.maps.model.LatLng(route.legs[0].endLocation.lat, route.legs[0].endLocation.lng));
//        mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(center_latlng, 10.5f));

        LatLngBounds.Builder builder = new LatLngBounds.Builder();
        builder.include(new com.google.android.gms.maps.model.LatLng(route.legs[0].startLocation.lat, route.legs[0].startLocation.lng));
        builder.include(new com.google.android.gms.maps.model.LatLng(route.legs[0].endLocation.lat, route.legs[0].endLocation.lng));
        LatLngBounds bounds = builder.build();
        int padding = ((getWindowManager().getDefaultDisplay().getWidth() * 10) / 100); // offset from edges of the map
        // in pixels
        CameraUpdate cu = CameraUpdateFactory.newLatLngBounds(bounds,
                padding+100);
        mMap.animateCamera(cu);
    }

    private LatLng computeCentroid(LatLng... points) {
        double latitude = 0;
        double longitude = 0;
        int n = points.length;

        for (LatLng point : points) {
            latitude += point.latitude;
            longitude += point.longitude;
        }

        return new LatLng(latitude/n, longitude/n);
    }

    private void addPolyline(DirectionsResult results, GoogleMap mMap) throws Exception{
        for (int i = 0; i < results.routes.length; i++) {
            List<com.google.android.gms.maps.model.LatLng> decodedPath = PolyUtil.decode(results.routes[i].overviewPolyline.getEncodedPath());
            mMap.addPolyline(new PolylineOptions()
                    .addAll(decodedPath).color(R.color.color_primary).width(15));
        }
    }

    @Override
    public void onMapReady(@NonNull GoogleMap googleMap) {
        this.mGoogleMap = googleMap;
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED &&
                ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
            drawRoutes();
        } else {
            locationPermissionLauncher.launch(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION});
        }
    }

    @Override
    public void onLocationChanged(@NonNull Location location) {
        if (fusedLocationProviderClient != null) {
            fusedLocationProviderClient.removeLocationUpdates(this);
        }

        try {
            DirectionsResult directionsResult = DirectionsApi.newRequest(
                            new GeoApiContext.Builder()
                                    .apiKey(getString(R.string.GOOGLE_MAPS_API_KEY))
                                    .build())
                    .origin(new com.google.maps.model.LatLng(location.getLatitude(), location.getLongitude()))
                    .destination(new com.google.maps.model.LatLng(pat_lat, pat_lng))
                    .alternatives(true)
                    .await();

            addMarkersToMap(directionsResult, mGoogleMap);
            positionCamera(directionsResult.routes[0], mGoogleMap);
            addPolyline(directionsResult, mGoogleMap);
            loadAddresses(directionsResult);

            binding.progress.setVisibility(View.GONE);
            binding.progress.setEnabled(false);
            binding.directionBs.setVisibility(View.VISIBLE);
        } catch (Exception e) {
            Toast.makeText(this, "Couldn't load routes", Toast.LENGTH_SHORT).show();
            binding.progress.setVisibility(View.GONE);
            binding.progress.setEnabled(false);
        }

    }

    private void loadAddresses(DirectionsResult directionsResult) {
//        String home_loc = AppUtil.getCompleteAddress(this, pat_lat, pat_lng);
        PatientDataCache.getPatientData(this, (patientData -> {
            if (patientData != null){
                StringBuilder home_add = new StringBuilder();

                if (patientData.address_line1 != null && !patientData.address_line1.trim().isEmpty()){
                    home_add.append(patientData.address_line1);
                }

                if (home_add.length() > 0){
                    home_add.append(", ");
                }

                if (patientData.city != null && !patientData.city.trim().isEmpty()){
                    home_add.append(patientData.city);
                }

                if (home_add.length() > 0){
                    home_add.append(", ");
                }

                if (patientData.state != null && !patientData.state.trim().isEmpty()){
                    home_add.append(patientData.state);
                }

                if (home_add.length() > 0){
                    home_add.append(", ");
                }

                if (patientData.country != null && !patientData.country.trim().isEmpty()){
                    home_add.append(patientData.country);
                }

                if (home_add.length() > 0){
                    binding.homeLoc.setText(home_add);
                }
            }
        }), true);

    }
}