package com.bfoura.ussd;


import android.net.Uri;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.telecom.PhoneAccountHandle;
import android.telephony.SubscriptionInfo;
import android.telephony.TelephonyManager;
import android.telecom.TelecomManager;
import android.telephony.PhoneStateListener;
import android.telephony.SubscriptionManager;
import android.os.Handler;
import android.util.Log;

import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;

import java.util.List;
import java.util.Map;
import java.util.HashMap;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;


import android.content.Context;
import android.content.Intent;

import javax.annotation.Nullable;

import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.Arguments;


public class UssdModule extends ReactContextBaseJavaModule {
    private static ReactApplicationContext reactContext;

    public static String TAG = UssdModule.class.getName();

    public UssdModule(ReactApplicationContext context) {
        super(context);
        reactContext = context;
    }

    @Override
    public String getName() {
        return "Ussd";
    }

    @Override
    public Map<String, Object> getConstants() {
        final Map<String, Object> constants = new HashMap<>();
        return constants;
    }


    private static class RequestExecutionException extends Exception {

        /**
         *
         */
        private static final long serialVersionUID = 1L;
        static String type = "ussd_plugin_ussd_execution_failure";
        String message;

        RequestExecutionException(String message) {
            this.message = message;
        }
    }

    @ReactMethod
    public void dial(String code, String providerMCC, String providerMNC, final ReadableMap context) {
        Log.d(TAG, "Dialing code");
        TelephonyManager.UssdResponseCallback callback =
                new TelephonyManager.UssdResponseCallback() {
                    @Override
                    public void onReceiveUssdResponse(
                            TelephonyManager telephonyManager, String request, CharSequence response) {
                        Log.d(TAG, response.toString());
                        String someData = response.toString();
                        WritableMap params = Arguments.createMap();
                        params.putString("ussdReply", someData);
                        params.merge(context);
                        sendEvent(reactContext, "ussdEvent", params);
                    }

                    @Override
                    public void onReceiveUssdResponseFailed(
                            TelephonyManager telephonyManager, String request, int failureCode) {
                        Log.e(TAG, "Code Dialing error");
                        WritableMap params = Arguments.createMap();
                        params.putString("request", request);
                        params.putString("failureCode", Integer.toString(failureCode));
                        params.merge(context);
                        sendEvent(reactContext, "ussdFailureEvent", params);
                    }
                };
        Log.d(TAG, "calling Dialing code");
        TelephonyManager manager = (TelephonyManager) this.reactContext.getSystemService(Context.TELEPHONY_SERVICE);
        int subscriptionId = getTargetSubscriptionId(providerMCC, providerMNC);
        TelephonyManager simManager = manager.createForSubscriptionId(subscriptionId);
        simManager.sendUssdRequest(code, callback, new Handler());
    }

    @ReactMethod
    public void makeCall(String code, String providerMCC, String providerMNC, final ReadableMap context) {
        Log.d(TAG, "Calling code");
        TelephonyManager manager = (TelephonyManager) this.reactContext.getSystemService(Context.TELEPHONY_SERVICE);
        SubscriptionInfo subscriptionInfo = getTargetSubscriptionInfo(providerMCC, providerMNC);
        TelecomManager telecomManager = (TelecomManager) this.reactContext.getSystemService(Context.TELECOM_SERVICE);
        PhoneAccountHandle phoneAccountHandle = getPhoneAccountHandle(subscriptionInfo, telecomManager);
        Uri uri = Uri.fromParts("tel", code, "");
        Bundle extras = new Bundle();
        extras.putParcelable(
                TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle);
        telecomManager.placeCall(uri, extras);
    }

    private int getTargetSubscriptionId(final String providerMCC, String providerMNC) {
        final int mcc = Integer.parseInt(providerMCC);
        final int mnc = Integer.parseInt(providerMNC);
        final SubscriptionManager subscriptionManager = SubscriptionManager.from(this.reactContext);
        SubscriptionInfo subscriptionInfo = null;
        final List<SubscriptionInfo> activeSubscriptionInfoList = subscriptionManager.getActiveSubscriptionInfoList();
        for (SubscriptionInfo sub : activeSubscriptionInfoList) {
            int subscriptionMCC = sub.getMcc();
            int subscriptionMNC = sub.getMnc();

            if (subscriptionMCC == mcc && subscriptionMNC == mnc) {
                subscriptionInfo = sub;
            }
        }
        if (subscriptionInfo != null)
            return subscriptionInfo.getSubscriptionId();
        return activeSubscriptionInfoList.get(0).getSubscriptionId();
    }

    private SubscriptionInfo getTargetSubscriptionInfo(final String providerMCC, String providerMNC) {
        final int mcc = Integer.parseInt(providerMCC);
        final int mnc = Integer.parseInt(providerMNC);
        final SubscriptionManager subscriptionManager = SubscriptionManager.from(this.reactContext);
        SubscriptionInfo subscriptionInfo = null;
        final List<SubscriptionInfo> activeSubscriptionInfoList = subscriptionManager.getActiveSubscriptionInfoList();
        for (SubscriptionInfo sub : activeSubscriptionInfoList) {
            int subscriptionMCC = sub.getMcc();
            int subscriptionMNC = sub.getMnc();

            if (subscriptionMCC == mcc && subscriptionMNC == mnc) {
                subscriptionInfo = sub;
            }
        }
        if (subscriptionInfo != null)
            return subscriptionInfo;
        return activeSubscriptionInfoList.get(0);
    }


    private PhoneAccountHandle getPhoneAccountHandle(SubscriptionInfo subscriptionInfo, TelecomManager telecomManager) {
        List<PhoneAccountHandle> list = telecomManager.getCallCapablePhoneAccounts();
        for (PhoneAccountHandle phoneAccountHandle : list) {
            String phoneAccountHandleId = phoneAccountHandle.getId();
            String simICCID = subscriptionInfo.getIccId();
            if (phoneAccountHandleId.contains(simICCID)) {
                return phoneAccountHandle;
            }
        }
        return null;
    }

    private void sendEvent(ReactContext reactContext,
                           String eventName,
                           @Nullable WritableMap params) {
        reactContext
                .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
                .emit(eventName, params);
    }


}
