
package in.slanglabs;

import static in.slanglabs.RNSlangUtils.getLocaleStringMapConfig;
import static in.slanglabs.assistants.retail.appstates.FarewellAppState.COMPLETE;
import static in.slanglabs.assistants.retail.appstates.GreetingsAppState.CONTINUE;

import android.app.Application;
import android.os.Handler;
import android.util.Log;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.ReadableType;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;

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

import in.slanglabs.assistants.base.BaseCondition;
import in.slanglabs.assistants.base.BaseUI;
import in.slanglabs.assistants.retail.AssistantConfiguration;
import in.slanglabs.assistants.retail.AssistantError;
import in.slanglabs.assistants.retail.AssistantSubDomain;
import in.slanglabs.assistants.retail.AssistantUI;
import in.slanglabs.assistants.retail.CheckoutInfo;
import in.slanglabs.assistants.retail.CheckoutUserJourney;
import in.slanglabs.assistants.retail.Item;
import in.slanglabs.assistants.retail.NavigationInfo;
import in.slanglabs.assistants.retail.NavigationUserJourney;
import in.slanglabs.assistants.retail.OfferInfo;
import in.slanglabs.assistants.retail.OfferManagementUserJourney;
import in.slanglabs.assistants.retail.OnSearchListener;
import in.slanglabs.assistants.retail.OrderInfo;
import in.slanglabs.assistants.retail.OrderManagementUserJourney;
import in.slanglabs.assistants.retail.RetailUserJourney;
import in.slanglabs.assistants.retail.SearchInfo;
import in.slanglabs.assistants.retail.SearchUserJourney;
import in.slanglabs.assistants.retail.SlangRetailAssistant;
import in.slanglabs.assistants.retail.SmallTalkUserJourney;
import in.slanglabs.assistants.retail.appstates.AddToCartAppState;
import in.slanglabs.assistants.retail.appstates.CheckoutAppState;
import in.slanglabs.assistants.retail.appstates.FarewellAppState;
import in.slanglabs.assistants.retail.appstates.GreetingsAppState;
import in.slanglabs.assistants.retail.appstates.NavigationAppState;
import in.slanglabs.assistants.retail.appstates.NavigationCompleteAppState;
import in.slanglabs.assistants.retail.appstates.OfferAppState;
import in.slanglabs.assistants.retail.appstates.OrderCancelAppState;
import in.slanglabs.assistants.retail.appstates.OrderManagementAppState;
import in.slanglabs.assistants.retail.appstates.OrderViewAppState;
import in.slanglabs.assistants.retail.appstates.SearchAppState;
import in.slanglabs.assistants.retail.appstates.SearchResultsAppState;
import in.slanglabs.assistants.retail.appstates.SmallTalkAppState;
import in.slanglabs.assistants.retail.appstates.UnsupportedAppState;
import in.slanglabs.assistants.retail.appstates.WaitingAppState;

public class RNSlangRetailAssistantModule extends ReactContextBaseJavaModule {

    private final ReactApplicationContext mReactContext;

    private static final String TAG = "RNSlangRetailAssistant";
    private static final String CONFIG_ASSISTANT_ID = "assistantId";
    private static final String CONFIG_API_KEY = "apiKey";
    private static final String CONFIG_LOCALE = "locale";
    private static final String CONFIG_REQUESTED_LOCALES = "requestedLocales";
    private static final String CONFIG_TRIGGER_POSITION = "triggerPosition";
    private static final String CONFIG_TRIGGER_STYLE = "triggerStyle";
    private static final String CONFIG_SURFACE_STYLE = "surfaceStyle";
    private static final String CONFIG_CUSTOM_TRIGGER_ENABLE = "enableCustomTrigger";
    private static final String CONFIG_STRICT_MODE_ENABLE = "enableStrictMode";
    private static final String CONFIG_ONBOARDING_INFO_TITLE = "onBoardingInfoTitle";
    private static final String CONFIG_ONBOARDING_INFO_DESCRIPTION = "onBoardingInfoDescription";
    private static final String CONFIG_BRAND_COLOR = "brandColor";
    private static final String CONFIG_ENVIRONMENT = "environment";

    private SearchUserJourney mSearchUserJourney;
    private OrderManagementUserJourney mOrderManagementUserJourney;
    private NavigationUserJourney mNavigationalUserJourney;

    private BaseCondition mSearchAppStateCondition;
    private BaseCondition mOrderMgmtAppStateCondition;
    private BaseCondition mNavigationAppStateCondition;

    private RNRetailSearchHandler mRetailSearchHandler;

    public RNSlangRetailAssistantModule(ReactApplicationContext reactContext) {
        super(reactContext);
        this.mReactContext = reactContext;
        mRetailSearchHandler = new RNRetailSearchHandler(this);
    }

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

    /**
     * Initialize Slang
     *
     * @param configOptions Configurations
     */
    @ReactMethod
    public void initialize(ReadableMap configOptions) {
        Log.e(TAG, "initialize");
        RNSlangUtils.setConfigOptions(configOptions);
        try {
            AssistantConfiguration.Builder configBuilder = new AssistantConfiguration.Builder()
                    .setAssistantId(RNSlangUtils.getStringConfig(CONFIG_ASSISTANT_ID))
                    .setAPIKey(RNSlangUtils.getStringConfig(CONFIG_API_KEY))
                    .setDefaultLocale(RNSlangLocaleMap.getDefaultLocale(RNSlangUtils.getStringConfig(
                            CONFIG_LOCALE)))
                    .setRequestedLocales(
                            RNSlangLocaleMap.getRequestedLocales(RNSlangUtils.getStringListConfig(
                                    CONFIG_REQUESTED_LOCALES)))
                    .enableCustomTrigger(RNSlangUtils.getBooleanConfig(CONFIG_CUSTOM_TRIGGER_ENABLE))
                    .setSurfaceStyle(RNSlangUtils.getSurfaceStyle(CONFIG_SURFACE_STYLE))
                    .setTriggerStyle(RNSlangUtils.getTriggerStyle(CONFIG_TRIGGER_STYLE))
                    .enableStrictMode(RNSlangUtils.getBooleanConfig(CONFIG_STRICT_MODE_ENABLE))
                    .setUIPosition(RNSlangUtils.getAssistantUIConfig(CONFIG_TRIGGER_POSITION))
                    .setOnboardingInfoTitle(getLocaleStringMapConfig(CONFIG_ONBOARDING_INFO_TITLE))
                    .setOnboardingInfoDescription(getLocaleStringMapConfig(CONFIG_ONBOARDING_INFO_DESCRIPTION))
                    .setEnvironment(RNSlangUtils.getEnvironment(CONFIG_ENVIRONMENT));

            if (RNSlangUtils.configHasKey(CONFIG_BRAND_COLOR)) {
                configBuilder.setBrandColor(RNSlangUtils.getStringConfig(CONFIG_BRAND_COLOR));
            }

            if (mReactContext.getApplicationContext() instanceof Application) {
                SlangRetailAssistant.initialize((Application) mReactContext.getApplicationContext(), configBuilder.build());
            } else if (getCurrentActivity() != null) {
                SlangRetailAssistant.initialize(getCurrentActivity().getApplication(), configBuilder.build());
            } else {
                WritableMap params = Arguments.createMap();
                params.putString("type", "Invalid call to initialize");
                sendEvent("onAssistantInitFailure", params);
            }
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    public void setSearchAction() {
        try {
            SlangRetailAssistant.setOnSearchListener(mRetailSearchHandler);
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    public void setAction() {
        try {
            SlangRetailAssistant.setAction(new SlangRetailAssistant.AppAction() {
                @Override
                public SearchAppState onSearch(final SearchInfo searchInfo,
                                               SearchUserJourney searchUserJourney) {
                    mSearchUserJourney = searchUserJourney;
                    new Handler().post(new Runnable() {
                        @Override
                        public void run() {
                            try {
                                WritableMap searchInfoMap = RNSlangUtils.mapSearchInfo(searchInfo);
                                sendEvent("onSearch", searchInfoMap);
                                Log.d(TAG, String.valueOf(searchInfo));
                            } catch (Exception exception) {
                                sendAssistantError(exception);
                            }
                        }
                    });
                    return new WaitingAppState();
                }

                @Override
                public OrderManagementAppState onOrderManagement(
                        final OrderInfo orderInfo,
                        OrderManagementUserJourney orderManagementUserJourney) {
                    mOrderManagementUserJourney = orderManagementUserJourney;
                    new Handler().post(new Runnable() {
                        @Override
                        public void run() {
                            try {
                                WritableMap orderInfoMap = RNSlangUtils.mapOrderInfo(orderInfo);
                                sendEvent("onOrderManagement", orderInfoMap);
                                Log.d(TAG, String.valueOf(orderInfo));
                            } catch (Exception exception) {
                                sendAssistantError(exception);
                            }
                        }
                    });
                    return new WaitingAppState();
                }

                @Override
                public NavigationAppState onNavigation(final NavigationInfo navigationInfo,
                                                       NavigationUserJourney navigationUserJourney) {
                    mNavigationalUserJourney = navigationUserJourney;
                    new Handler().post(new Runnable() {
                        @Override
                        public void run() {
                            try {
                                WritableMap navigationInfoMap = RNSlangUtils.mapNavigationInfo(navigationInfo);
                                sendEvent("onNavigation", navigationInfoMap);
                                Log.d(TAG, String.valueOf(navigationInfo));
                            } catch (Exception exception) {
                                sendAssistantError(exception);
                            }
                        }
                    });
                    return new WaitingAppState();
                }

                @Override
                public OfferAppState onOfferManagement(OfferInfo offerInfo, OfferManagementUserJourney offerManagementUserJourney) {
                    return new UnsupportedAppState();
                }

                @Override
                public CheckoutAppState onCheckOut(CheckoutInfo checkoutInfo, CheckoutUserJourney checkoutUserJourney) {
                    return new UnsupportedAppState();
                }

                @Override
                public SmallTalkAppState onSmallTalk(SmallTalkUserJourney.SmallTalkType smallTalkType, SmallTalkUserJourney smallTalkUserJourney) {
                    if (smallTalkType == SmallTalkUserJourney.SmallTalkType.GREETING) {
                        return new GreetingsAppState(CONTINUE);
                    } else if (smallTalkType == SmallTalkUserJourney.SmallTalkType.FAREWELL) {
                        return new FarewellAppState(COMPLETE);
                    }
                    return new UnsupportedAppState();
                }

                @Override
                public void onAssistantError(AssistantError assistantError) {
                    Log.d(TAG, String.valueOf(assistantError));
                    WritableMap params = Arguments.createMap();
                    params.putInt("type", assistantError.getType());
                    sendEvent("onAssistantError", params);
                }
            });
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    public void setLifecycleObserver() {
        try {
            SlangRetailAssistant.setLifecycleObserver(new SlangRetailAssistant.LifecycleObserver() {
                @Override
                public void onAssistantInitSuccess() {
                    sendEvent("onAssistantInitSuccess", Arguments.createMap());
                }

                @Override
                public void onAssistantInitFailure(String s) {
                    Log.d(TAG, s);
                    WritableMap params = Arguments.createMap();
                    params.putString("type", s);
                    sendEvent("onAssistantInitFailure", params);
                }

                @Override
                public void onAssistantInvoked() {
                    sendEvent("onAssistantInvoked", Arguments.createMap());
                }

                @Override
                public void onAssistantClosed(boolean b) {
                    WritableMap params = Arguments.createMap();
                    params.putBoolean("isCancelled", b);
                    sendEvent("onAssistantClosed", params);
                }

                @Override
                public void onAssistantLocaleChanged(Locale locale) {
                    WritableMap localeMap = Arguments.createMap();
                    localeMap.putString("country", locale.getCountry().toUpperCase());
                    localeMap.putString("language", locale.getLanguage());
                    sendEvent("onAssistantLocaleChanged", localeMap);
                }

                @Override
                public boolean onUnrecognisedUtterance(String s) {
                    WritableMap params = Arguments.createMap();
                    params.putString("utterance", s);
                    sendEvent("onUnrecognisedUtterance", params);
                    return false;
                }

                @Override
                public void onUtteranceDetected(String s) {
                    WritableMap params = Arguments.createMap();
                    params.putString("utterance", s);
                    sendEvent("onUtteranceDetected", params);
                }

                @Override
                public void onOnboardingSuccess() {
                    sendEvent("onOnboardingSuccess", Arguments.createMap());
                }

                @Override
                public void onOnboardingFailure() {
                    sendEvent("onOnboardingFailure", Arguments.createMap());
                }

                @Override
                public void onMicPermissionDenied() {
                    sendEvent("onMicPermissionDenied", Arguments.createMap());
                }

                @Override
                public void onMicPermissionGranted() {
                    sendEvent("onMicPermissionGranted", Arguments.createMap());
                }

                @Override
                public void onCoachmarkAction(SlangRetailAssistant.CoachmarkType coachmarkType, SlangRetailAssistant.CoachmarkAction coachmarkAction) {
                    WritableMap params = Arguments.createMap();
                    params.putString("coachmarkType", coachmarkType.name());
                    params.putString("coachmarkAction", coachmarkAction.name());
                    sendEvent("onCoachmarkAction", Arguments.createMap());
                }

            });
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    public void prepareActiveSearchUserJourney() {
        try {
            mSearchUserJourney = SlangRetailAssistant.getActiveSearchJourney();
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    public void setUserId(String userId) {
        if (getCurrentActivity() != null) {
            try {
                SlangRetailAssistant.setUserId(userId);
            } catch (Exception exception) {
                sendAssistantError(exception);
            }
        }
    }

    @ReactMethod
    public void startConversation() {
        if (getCurrentActivity() != null) {
            try {
                SlangRetailAssistant.startConversation();
            } catch (Exception exception) {
                sendAssistantError(exception);
            }
        }
    }

    @ReactMethod
    public void startConversation(String userJourney) {
        if (getCurrentActivity() != null) {
            try {
                SlangRetailAssistant.startConversation(RetailUserJourney.fromString(userJourney),
                        getCurrentActivity());
            } catch (Exception exception) {
                sendAssistantError(exception);
            }
        }
    }

    @ReactMethod
    public void cancelSession() {
        if (getCurrentActivity() != null) {
            try {
                SlangRetailAssistant.cancelSession();
            } catch (Exception exception) {
                sendAssistantError(exception);
            }
        }
    }

    @ReactMethod
    public void setLocale(String locale) {
        if (getCurrentActivity() != null) {
            try {
                Locale selectedLocale = RNSlangLocaleMap.getLocale(locale);
                if (selectedLocale != null) {
                    SlangRetailAssistant.setLocale(selectedLocale);
                }
            } catch (Exception exception) {
                sendAssistantError(exception);
            }
        }
    }

    @ReactMethod
    public void setSearchSuccess() {
        Log.d(TAG, "setSearchSuccess");
        try {
            mSearchAppStateCondition = SearchResultsAppState.SUCCESS;
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    public void setSearchFailure() {
        Log.d(TAG, "setSearchFailure");
        try {
            mSearchAppStateCondition = SearchResultsAppState.FAILURE;
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    public void setItemNotFound() {
        Log.d(TAG, "setItemNotFound");
        try {
            mSearchAppStateCondition = SearchResultsAppState.ITEM_NOT_FOUND;
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    public void setItemOutOfStock() {
        Log.d(TAG, "setItemOutOfStock");
        try {
            mSearchAppStateCondition = SearchResultsAppState.ITEM_OUT_OF_STOCK;
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    public void setItemNotSpecified() {
        Log.d(TAG, "setItemNotSpecified");
        try {
            mSearchAppStateCondition = SearchResultsAppState.ITEM_NOT_SPECIFIED;
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    public void setNeedItemQuantity() {
        Log.d(TAG, "setNeedItemQuantity");
        try {
            mSearchAppStateCondition = AddToCartAppState.ITEM_QUANTITY_REQUIRED;
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    public void setNeedDisambiguation() {
        Log.d(TAG, "setNeedDisambiguation");
        try {
            mSearchAppStateCondition = AddToCartAppState.ITEM_AMBIGUOUS;
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    public void setStopDisambiguation() {
        Log.d(TAG, "setStopDisambiguation");
        try {
            mSearchAppStateCondition = AddToCartAppState.ITEM_AMBIGUOUS_FORCE_UI;
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    public void notifySearchActionCompleted(final String searchAppState) {
        Log.d(TAG, "notifySearchActionCompleted: " + searchAppState);

        prepareActiveSearchUserJourney();

        if (mSearchUserJourney == null || mSearchAppStateCondition == null) {
            return;
        }

        try {
            if (searchAppState.equalsIgnoreCase("ADD_TO_CART")) {
                try {
                    mSearchUserJourney.notifyAppState(
                            new AddToCartAppState(
                                    new AddToCartAppState.Condition(mSearchAppStateCondition.getName()
                                    )
                            )
                    );
                } catch (Exception e) {
                    Log.e(TAG, "" + e.getLocalizedMessage());
                }
            } else {
                try {
                    mSearchUserJourney.notifyAppState(
                            new SearchResultsAppState(
                                    new SearchResultsAppState.Condition(mSearchAppStateCondition.getName()
                                    )
                            )
                    );
                } catch (Exception e) {
                    Log.e(TAG, "" + e.getLocalizedMessage());
                }
            }
        } catch (Exception exception) {
            sendAssistantError(exception);
        }

        mSearchAppStateCondition = null;
    }

    @ReactMethod
    public void prepareActiveOrderManagementUserJourney() {
        try {
            mOrderManagementUserJourney = SlangRetailAssistant.getActiveOrderManagementUserJourney();
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    public void setOrderSuccess() {
        Log.d(TAG, "setOrderSuccess");
        try {
            mOrderMgmtAppStateCondition = OrderViewAppState.SUCCESS;
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    public void setOrderFailure() {
        Log.d(TAG, "setOrderFailure");
        try {
            mOrderMgmtAppStateCondition = OrderViewAppState.FAILURE;
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    public void setOrderNotFound(final int orderNumber) {
        Log.d(TAG, "setOrderNotFound");
        try {
            mOrderMgmtAppStateCondition = OrderViewAppState.ORDER_NOT_FOUND;
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    public void setOrdersEmpty() {
        Log.d(TAG, "setOrdersEmpty");
        try {
            mOrderMgmtAppStateCondition = OrderViewAppState.ORDER_HISTORY_EMPTY;
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    public void setOrderConfirmationRequired() {
        Log.d(TAG, "setOrderConfirmationRequired");
        try {
            mOrderMgmtAppStateCondition = OrderCancelAppState.ORDER_CANCEL_CONFIRMATION_REQUIRED;
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    public void setOrderUserConfirmedCancel() {
        Log.d(TAG, "setUserConfirmedCancel");
        try {
            mOrderMgmtAppStateCondition = OrderCancelAppState.ORDER_CANCEL_USER_CONFIRMED;
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    public void setOrderUserDeniedCancel() {
        Log.d(TAG, "setOrderUserDeniedCancel");
        try {
            mOrderMgmtAppStateCondition = OrderCancelAppState.ORDER_CANCEL_USER_DENIED;
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    public void notifyOrderActionCompleted(final String orderAppState) {
        Log.d(TAG, "notifyOrderActionCompleted: " + orderAppState);

        prepareActiveOrderManagementUserJourney();

        if (mOrderManagementUserJourney == null || mOrderMgmtAppStateCondition == null) {
            return;
        }

        try {
            if (orderAppState.equalsIgnoreCase("CANCEL_ORDER")) {
                try {
                    mOrderManagementUserJourney.notifyAppState(
                            new OrderCancelAppState(
                                    new OrderCancelAppState.Condition(mOrderMgmtAppStateCondition.getName()
                                    )
                            )
                    );
                } catch (Exception e) {
                    Log.e(TAG, "" + e.getLocalizedMessage());
                }
            } else {
                try {
                    mOrderManagementUserJourney.notifyAppState(
                            new OrderViewAppState(
                                    new OrderViewAppState.Condition(mOrderMgmtAppStateCondition.getName()
                                    )
                            )
                    );
                } catch (Exception e) {
                    Log.e(TAG, "" + e.getLocalizedMessage());
                }
            }
        } catch (Exception exception) {
            sendAssistantError(exception);
        }

        mOrderMgmtAppStateCondition = null;
    }

    @ReactMethod
    public void prepareActiveNavigationUserJourney() {
        try {
            mNavigationalUserJourney = SlangRetailAssistant.getActiveNavigationUserJourney();
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    public void setNavigationSuccess() {
        Log.d(TAG, "setNavigationSuccess");
        try {
            mNavigationAppStateCondition = NavigationCompleteAppState.SUCCESS;
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    public void setNavigationFailure() {
        Log.d(TAG, "setNavigationFailure");
        try {
            mNavigationAppStateCondition = NavigationCompleteAppState.FAILURE;
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    public void notifyNavigationActionCompleted(final String navigationAppState) {
        Log.d(TAG, "notifyNavigationActionCompleted: " + navigationAppState);

        prepareActiveNavigationUserJourney();

        if (mNavigationalUserJourney == null || mNavigationAppStateCondition == null) {
            return;
        }

        try {
            mNavigationalUserJourney.notifyAppState(
                    new NavigationCompleteAppState(
                            new NavigationCompleteAppState.Condition(mNavigationAppStateCondition.getName()
                            )
                    )
            );
        } catch (Exception exception) {
            sendAssistantError(exception);
        }

        mNavigationAppStateCondition = null;
    }

    @ReactMethod
    public void setUserJourneyToContinue(String currentUserJourney,
                                         String userJourneyToContinue, boolean isSpoken) {
        Log.d(TAG, "setUserJourneyToContinue");
        try {
            RetailUserJourney assistantUserJourney = RetailUserJourney.fromString(currentUserJourney);
            switch (assistantUserJourney) {
                case SEARCH:
                    if (mSearchUserJourney != null) {
                        mSearchUserJourney.setRetailJourneyToContinue(RetailUserJourney
                                .fromString(userJourneyToContinue), isSpoken);
                    }
                    break;
                case ORDER_MANAGEMENT:
                    if (mOrderManagementUserJourney != null) {
                        mOrderManagementUserJourney.setRetailJourneyToContinue(RetailUserJourney
                                .fromString(userJourneyToContinue), isSpoken);
                    }
                    break;
                case NAVIGATION:
                    if (mNavigationalUserJourney != null) {
                        mNavigationalUserJourney.setRetailJourneyToContinue(RetailUserJourney
                                .fromString(userJourneyToContinue), isSpoken);
                    }
                    break;
                default:
                    break;
            }
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    public void clearUserJourneyToContinue(String currentUserJourney) {
        Log.d(TAG, "clearUserJourneyToContinue");
        try {
            RetailUserJourney assistantUserJourney = RetailUserJourney.fromString(currentUserJourney);
            switch (assistantUserJourney) {
                case SEARCH:
                    if (mSearchUserJourney != null) {
                        mSearchUserJourney.clearRetailJourneyToContinue();
                    }
                    break;
                case ORDER_MANAGEMENT:
                    if (mOrderManagementUserJourney != null) {
                        mOrderManagementUserJourney.clearRetailJourneyToContinue();
                    }
                    break;
                case NAVIGATION:
                    if (mNavigationalUserJourney != null) {
                        mNavigationalUserJourney.clearRetailJourneyToContinue();
                    }
                    break;
                default:
                    break;
            }
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    public void setGlobalTrigger() {
        if (getCurrentActivity() != null) {
            try {
                SlangRetailAssistant.getUI().setGlobalTrigger();
            } catch (Exception exception) {
                sendAssistantError(exception);
            }
        }
    }

    @ReactMethod
    public void showTrigger() {
        if (getCurrentActivity() != null) {
            try {
                SlangRetailAssistant.getUI().showTrigger(getCurrentActivity());
            } catch (Exception exception) {
                sendAssistantError(exception);
            }
        }
    }

    @ReactMethod
    public void hideTrigger() {
        if (getCurrentActivity() != null) {
            try {
                SlangRetailAssistant.getUI().hideTrigger(getCurrentActivity());
            } catch (Exception exception) {
                sendAssistantError(exception);
            }
        }
    }

    @ReactMethod
    public void setUserProperties(ReadableMap rnUserProperties) {
        Log.d(TAG, String.format(
                Locale.ENGLISH, "UserProperties : %s", rnUserProperties));
        try {
            Map<String, String> userPropertiesInfoMap = RNSlangUtils.convertReadableMapToMap(rnUserProperties);
            SlangRetailAssistant.setUserProperties(userPropertiesInfoMap);
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    void notifyTextSearch(String itemName) {
        Log.d(TAG, String.format(
                Locale.ENGLISH, "Search Item Name : %s", itemName));
        try {
            SlangRetailAssistant.notifyNonVoiceSearch(itemName);
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    void notifyNonVoiceUserJourney(String userJourney, ReadableMap userJourneyInfo) {
        Log.d(TAG, String.format(
                Locale.ENGLISH, "User Journey : %s, UserJourneyInfo : %s", userJourney,
                userJourneyInfo));
        try {
            Map<String, String> userJourneyInfoMap = RNSlangUtils.convertReadableMapToMap(userJourneyInfo);
            RetailUserJourney assistantUserJourney = RetailUserJourney.fromString(userJourney);
            SlangRetailAssistant.notifyNonVoiceUserJourney(assistantUserJourney,
                    userJourneyInfoMap);
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    void notifyCTREvent(ReadableMap eventInfo) {
        Log.d(TAG, String.format(
                Locale.ENGLISH, "EventName : %s, EventInfo : %s", "CTREvent",
                eventInfo));
        try {
            Map<String, String> eventInfoMap = RNSlangUtils.convertReadableMapToMap(eventInfo);
            SlangRetailAssistant.trackCTREvent("CTREvent",
                    eventInfoMap);
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    void setAppDefaultUserJourney(String userJourney) {
        try {
            SlangRetailAssistant.setAppDefaultUserJourney(RetailUserJourney.fromString(userJourney));
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    void setAppDefaultSubDomain(String appSubDomain) {
        try {
            SlangRetailAssistant.setAppDefaultSubDomain(AssistantSubDomain.fromString(appSubDomain));
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    public void setBackgroundColorGradient(ReadableArray colors) {
        try {
            String[] backgroundColorGradients = new String[colors.size()];
            for (int i = 0; i < colors.size(); i++) {
                String color = "";
                if (colors.getType(i) == ReadableType.String) {
                    color = colors.getString(i);
                }
                backgroundColorGradients[i] = color;
            }
            SlangRetailAssistant.getUI().setBackgroundColorGradient(backgroundColorGradients);
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    public void setWaveColorGradient(ReadableArray colors) {
        try {
            String[] foregroundColorGradients = new String[colors.size()];
            for (int i = 0; i < colors.size(); i++) {
                String color = "";
                if (colors.getType(i) == ReadableType.String) {
                    color = colors.getString(i);
                }
                foregroundColorGradients[i] = color;
            }
            SlangRetailAssistant.getUI().setForegroundColorGradient(foregroundColorGradients);
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    public void setTextColor(String color) {
        try {
            SlangRetailAssistant.getUI().setTextColor(color);
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    public void setSecondaryTextColor(String color) {
        try {
            SlangRetailAssistant.getUI().setSecondaryTextColor(color);
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    public void setTextHighlightColor(String color) {
        try {
            SlangRetailAssistant.getUI().setTextHighlightColor(color);
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    public void setSettingsButtonBackgroundColor(String color) {
        try {
            SlangRetailAssistant.getUI().setSettingsButtonBackgroundColor(color);
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    public void setControlButtonBackgroundColor(String color) {
        try {
            SlangRetailAssistant.getUI().setControlButtonBackgroundColor(color);
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    public void setTheme(String theme) {
        try {
            BaseUI.BaseTheme baseTheme = AssistantUI.Theme.LIGHT;
            if ("DARK".equals(theme)) {
                baseTheme = AssistantUI.Theme.DARK;
            }
            SlangRetailAssistant.getUI().setTheme(baseTheme);
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    void nudgeUser() {
        Log.d(TAG, String.format(
                Locale.ENGLISH, "nudgeUser"));
        try {
            SlangRetailAssistant.getUI().nudgeUser(getCurrentActivity());
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    void removeNudge() {
        Log.d(TAG, String.format(
                Locale.ENGLISH, "removeNudge"));
        try {
            SlangRetailAssistant.getUI().removeNudge();
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    void nudgeUserWithParameters(ReadableMap title, ReadableMap description) {
        Log.d(TAG, String.format(
                Locale.ENGLISH, "nudgeUser : title : %s, description : %s",
                title, description));
        try {
            SlangRetailAssistant.getUI().nudgeUser(
                    getCurrentActivity(),
                    getLocaleStringMapConfig(title),
                    getLocaleStringMapConfig(description)
            );
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    void setSearchUserJourneyContextItemBrand(String brandName) {
        try {
            SearchUserJourney.getContext().setItemBrand(brandName);
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    void getSearchUserJourneyContextItemBrand(Promise promise) {
        try {
            promise.resolve(SearchUserJourney.getContext().getItemBrand());
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    void setSearchUserJourneyContextItemId(String itemId) {
        try {
            SearchUserJourney.getContext().setItemId(itemId);
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    void getSearchUserJourneyContextItemId(Promise promise) {
        try {
            promise.resolve(SearchUserJourney.getContext().getItemId());
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    void setSearchUserJourneyContextItemDescription(String itemDescription) {
        try {
            SearchUserJourney.getContext().setItemDescription(itemDescription);
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    void setSearchUserJourneyContextSelectedItemDescription(String itemDescription) {
        try {
            SearchUserJourney.getContext().setSelectedDescription(itemDescription);
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    void getSearchUserJourneyContextItemDescription(Promise promise) {
        try {
            promise.resolve(SearchUserJourney.getContext().getItemDescription());
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    void setSearchUserJourneyContextItemCategory(String itemCategory) {
        try {
            SearchUserJourney.getContext().setItemCategory(itemCategory);
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    void getSearchUserJourneyContextItemCategory(Promise promise) {
        try {
            Log.d(TAG, "category is " + SearchUserJourney.getContext().getItemCategory());
            promise.resolve(SearchUserJourney.getContext().getItemCategory());
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    void setSearchUserJourneyContextItemVariant(ReadableArray variants) {
        try {
            String[] variantsArray = new String[variants.size()];
            for (int i = 0; i < variants.size(); i++) {
                String value = "";
                if (variants.getType(i) == ReadableType.String) {
                    value = variants.getString(i);
                }
                variantsArray[i] = value;
            }
            SearchUserJourney.getContext().setItemVariants(variantsArray);
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    void getSearchUserJourneyContextItemVariant(Promise promise) {
        try {
            promise.resolve(RNSlangUtils.mapItemVariantList(SearchUserJourney.getContext().getItemVariants()));
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    void setSearchUserJourneyContextItemPrice(int minAmount, int maxAmount, String currency) {
        try {
            Item.Price.Currency currencyModel = Item.Price.Currency.valueOf(currency);
            Item.Price priceModel = new Item.Price(minAmount, maxAmount, currencyModel);
            SearchUserJourney.getContext().setItemPrice(priceModel);
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    void getSearchUserJourneyContextItemPrice(Promise promise) {
        try {
            promise.resolve(RNSlangUtils.mapItemPrice(SearchUserJourney.getContext().getItemPrice()));
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    void setSearchUserJourneyContextItemQuantity(int amount, String unit) {
        try {
            Item.Quantity.Unit unitModel = Item.Quantity.Unit.valueOf(unit);
            Item.Quantity quantityModel = new Item.Quantity(amount, unitModel);
            SearchUserJourney.getContext().setItemQuantity(quantityModel);
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    void getSearchUserJourneyContextItemQuantity(Promise promise) {
        try {
            promise.resolve(RNSlangUtils.mapItemQuantity(SearchUserJourney.getContext().getItemQuantity()));
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    void setSearchUserJourneyContextItemSize(int amount, String unit) {
        try {
            Item.Size.Unit unitModel = Item.Size.Unit.valueOf(unit);
            Item.Size sizeModel = new Item.Size(amount, unitModel);
            SearchUserJourney.getContext().setItemSize(sizeModel);
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    void getSearchUserJourneyContextItemSize(Promise promise) {
        try {
            promise.resolve(RNSlangUtils.mapItemSize(SearchUserJourney.getContext().getItemSize()));
        } catch (Exception exception) {
            sendAssistantError(exception);
            ;
        }
    }

    @ReactMethod
    void clearSearchUserJourneyContext() {
        try {
            SearchUserJourney.getContext().clear();
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    void disableSearchUserJourneyContext() {
        try {
            SearchUserJourney.disablePreserveContext();
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    void enableSearchUserJourneyContext() {
        try {
            SearchUserJourney.enablePreserveContext();
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    void setOrderManagementUserJourneyContextOrderIndex(int orderIndex) {
        try {
            OrderManagementUserJourney.getContext().setOrderIndex(orderIndex);
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    void getOrderManagementUserJourneyContextOrderIndex(Promise promise) {
        try {
            promise.resolve(OrderManagementUserJourney.getContext().getOrderIndex());
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    void clearOrderManagementUserJourneyContext() {
        try {
            OrderManagementUserJourney.getContext().clear();
        } catch (Exception exception) {
            sendAssistantError(exception);

        }
    }

    @ReactMethod
    void disableOrderManagementUserJourneyContext() {
        try {
            OrderManagementUserJourney.disablePreserveContext();
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    void clearNavigationUserJourneyContext() {
        try {
            NavigationUserJourney.getContext().clear();
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    @ReactMethod
    void disableNavigationUserJourneyContext() {
        try {
            NavigationUserJourney.disablePreserveContext();
        } catch (Exception exception) {
            sendAssistantError(exception);
        }
    }

    void sendAssistantError(Exception exception) {
        WritableMap params = Arguments.createMap();
        params.putString("type", exception.getMessage());
        sendEvent("onAssistantError", params);
    }

    void sendEvent(String name, WritableMap params) {
        mReactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
                .emit(name, params);
    }
}
