package io.appgain.reactsdk;

import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.Callback;

import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.ReadableNativeMap;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

import com.parse.FindCallback;
import com.parse.Parse;
import com.parse.ParseException;
import com.parse.ParseUser;
import com.parse.ParseInstallation;
import com.parse.ParseObject;
import com.parse.ParseQuery;
import com.parse.SaveCallback;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import io.appgain.sdk.appcompat.controller.Appgain;
import io.appgain.sdk.appcompat.controller.AppgainEvents;
import io.appgain.sdk.appcompat.controller.Automator;
import io.appgain.sdk.appcompat.controller.DeferredDeepLinking;
import io.appgain.sdk.appcompat.controller.NotificationChannelUtils;
import io.appgain.sdk.appcompat.controller.PurchaseTransactions;
import io.appgain.sdk.appcompat.interfaces.AppgainSDKInitCallback;
import io.appgain.sdk.appcompat.interfaces.AutomatorCallback;
import io.appgain.sdk.appcompat.interfaces.DeferredDeepLinkingCallback;
import io.appgain.sdk.appcompat.interfaces.LogEventCallback;
import io.appgain.sdk.appcompat.interfaces.NotificationChannelCallback;
import io.appgain.sdk.appcompat.interfaces.UpdateUserIdCallback;
import io.appgain.sdk.appcompat.model.AutomatorResponse;
import io.appgain.sdk.appcompat.model.BaseResponse;
import io.appgain.sdk.appcompat.model.User;
import io.appgain.sdk.appcompat.model.deferredlinking.DeferredDeepLinkingResponse;

public class AppgainSDKJavaModule extends ReactContextBaseJavaModule {

    private final ReactApplicationContext reactContext;
    private Gson gson;

    public AppgainSDKJavaModule(ReactApplicationContext reactContext) {
      super(reactContext);
      this.reactContext = reactContext;
      this.gson = new GsonBuilder().create();
    }

      @Override
      public String getName() {
          return "AppgainSDK";
      }
  
      @ReactMethod
    public void initAppgainSDK(
            final String projectId, final String apiKey ,final boolean autoconfig, final Callback successCallback, final Callback errorCallback
    ){
        try{
            Appgain.initialize(getReactApplicationContext(), projectId, apiKey, autoconfig, true,  new AppgainSDKInitCallback() {
                @Override
                public void onSuccess() {
                    successCallback.invoke("{}");
                }
                @Override
                public void onFailure(BaseResponse failure) {
                    errorCallback.invoke(gson.toJson(failure));
                }
            });
        }catch(Exception e){
            errorCallback.invoke(gson.toJson(e));
        }
    }

    @ReactMethod
    public void inituser(
            final String email,
            final String pass,
            final String username,
            final Callback successCallback,
            final Callback errorCallback
    ){
        try {
            User user = new User(username, email, pass);
            Appgain.signupUser(getReactApplicationContext(), user,  new AppgainSDKInitCallback() {
                @Override
                public void onSuccess() {
                    successCallback.invoke("{}");
                }
                @Override
                public void onFailure(BaseResponse failure) {
                    errorCallback.invoke(gson.toJson(failure));
                }
            });
        } catch (Exception e) {
            errorCallback.invoke(gson.toJson(e));
        }
    }

    @ReactMethod
    public void getuserId(final Callback successCallback,
                                 final Callback errorCallback
    ){
        try{
            ParseUser current = ParseUser.getCurrentUser();
            if(current != null && current.getObjectId() != null && !current.getObjectId().isEmpty()){
                ParseUser.getQuery().whereEqualTo("objectId", current.getObjectId()).findInBackground(new FindCallback<ParseUser>() {
                    @Override
                    public void done(List<ParseUser> objects, ParseException e) {
                        if(e == null && objects.size() > 0){
                            ParseUser userInfo = objects.get(0);
                            String userId = userInfo.getString("userId");
                            if(userId != null && !userId.isEmpty()){
                                successCallback.invoke("{\"userId\":" + userId + "}");
                            }else{
                                errorCallback.invoke("{\"message\": \"UserId is not found\"}");
                            }
                        }else{
                            errorCallback.invoke(gson.toJson(e));
                        }
                    }
                });
            }
        }catch(Exception e){
            errorCallback.invoke(gson.toJson(e));
        }
    }

    @ReactMethod
    public void fireAutomatorWithPersonalization(final String triggerPointName, final ReadableMap personalizationMap,
                              final Callback successCallback, final Callback errorCallback){
        try {
            HashMap<String, Object> objHashmap =personalizationMap.toHashMap();
            HashMap<String, String> strHashmap = new HashMap<>();
            for (Map.Entry<String, Object> entry : objHashmap.entrySet()) {
                String key = entry.getKey();
                Object value = entry.getValue();
                if(value != null){
                    strHashmap.put(key, value.toString());
                }else{
                    strHashmap.put(key, "");
                }
            }
            Automator.enqueue(triggerPointName, strHashmap, new AutomatorCallback() {
                @Override
                public void onAutomatorFired(AutomatorResponse response) {
                    successCallback.invoke("{}");
                }

                @Override
                public void onFailure(BaseResponse failure) {
                    errorCallback.invoke(gson.toJson(failure));
                }
            });
        } catch (Exception e) {
            errorCallback.invoke(gson.toJson(e));
        }
    }

    @ReactMethod
    public void fireAutomator(final String triggerPointName, final Callback successCallback, final Callback errorCallback){
        try {
            HashMap<String, String> strHashmap = new HashMap<>();
            Automator.enqueue(triggerPointName, strHashmap, new AutomatorCallback() {
                @Override
                public void onAutomatorFired(AutomatorResponse response) {
                    successCallback.invoke("{}");
                }

                @Override
                public void onFailure(BaseResponse failure) {
                    errorCallback.invoke(gson.toJson(failure));
                }
            });
        } catch (Exception e) {
            errorCallback.invoke(gson.toJson(e));
        }
    }


    @ReactMethod
    public void matchLink(final Callback successCallback, final Callback errorCallback){
        try {
            DeferredDeepLinking.enqueue(getReactApplicationContext(), new DeferredDeepLinkingCallback() {
                @Override
                public void onMatch(DeferredDeepLinkingResponse response) {
                    successCallback.invoke(new Gson().toJson(response));
                }
                @Override
                public void onFailure(BaseResponse failure) {
                    errorCallback.invoke(gson.toJson(failure));
                }
            });
        } catch (Exception e) {
            errorCallback.invoke(gson.toJson(e));
        }
    }

    @ReactMethod
    public void addPurchase(String productName, float amount, String curruncy, final Callback successCallback, final Callback errorCallback){
        try{
            PurchaseTransactions.logPurchase(productName, amount, curruncy, new PurchaseTransactions.PurchaseTransactionsCallback() {
                @Override
                public void onSuccess() {
                    successCallback.invoke("{}");
                }

                @Override
                public void onFailure(BaseResponse failure) {
                    errorCallback.invoke(gson.toJson(failure));
                }
            });
        }catch(Exception e){
            errorCallback.invoke(gson.toJson(e));
        }
    }

    @ReactMethod
    public void addNotificationChannel(final String type, final String item, final Callback successCallback, final Callback errorCallback){
        try {
            if("sms".equals(type)){
                NotificationChannelUtils.addSmsNotificationChannel(item, new NotificationChannelCallback() {
                    @Override
                    public void onSuccess() {
                        successCallback.invoke("{}");
                    }

                    @Override
                    public void onFailure(BaseResponse failure) {
                        errorCallback.invoke(gson.toJson(failure));
                    }
                });
            }else if("email".equals(type)){
                NotificationChannelUtils.addEmailNotificationChannel(item, new NotificationChannelCallback() {
                    @Override
                    public void onSuccess() {
                        successCallback.invoke("{}");
                    }

                    @Override
                    public void onFailure(BaseResponse failure) {
                        errorCallback.invoke(gson.toJson(failure));
                    }
                });
            }else{
                errorCallback.invoke("{\"message\": \"type must be either \'phone\' or \'sms\'\"}");
            }

        } catch (Exception e) {
            errorCallback.invoke(gson.toJson(e));
        }
    }


    @ReactMethod
    public void logEvent(final String action, final String type, final Callback successCallback, final Callback errorCallback){
        try {
            AppgainEvents.logEvent(action, type, new LogEventCallback() {
                @Override
                public void onSuccess() {
                  successCallback.invoke("{}");
                }

                @Override
                public void onFailure(BaseResponse failure) {
                    errorCallback.invoke(gson.toJson(failure));
                }
            });
        } catch (Exception e) {
            errorCallback.invoke(gson.toJson(e));
        }
    }

    @ReactMethod
    public void setuserId(final String userId, final Callback successCallback, final Callback errorCallback){
        try {
            final String installationUserId = getCurrentUserId();
            final  String parseUserObjUserIdField = ParseUser.getCurrentUser().getString("userId");
            // case Appgain sdk  initialized
            final String currentUserId;
            if (installationUserId !=null&& installationUserId.equals(userId)){
                if (parseUserObjUserIdField!=null&&parseUserObjUserIdField.equals(userId)){
                    //  installation and parse user update so cancel update
                    successCallback.invoke("{\"message\": \"installation and parse user update so cancel update\"}");
                    return;
                }else {
                    currentUserId=ParseUser.getCurrentUser().getObjectId();
                }
            }else {
                currentUserId = installationUserId;
            }
            Appgain.updateUserId(userId, new UpdateUserIdCallback() {
                @Override
                public void onSuccess() {
                  successCallback.invoke("{}");
                }

                @Override
                public void onFailure(BaseResponse errorResponse) {
                    errorCallback.invoke(gson.toJson(errorResponse));
                }
            });
        } catch (Exception e) {
            errorCallback.invoke(gson.toJson(e));
        }
    }

    private String getCurrentUserId() {
        ParseInstallation installation = ParseInstallation.getCurrentInstallation();
        final String curuntUserId = installation.getString("userId");
        return curuntUserId;
    }
}
