
package in.slanglabs;

import android.os.Bundle;
import android.util.Log;

import com.facebook.react.bridge.Arguments;
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.ReadableMapKeySetIterator;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import in.slanglabs.assistants.pharmacy.AssistantConfiguration;
import in.slanglabs.assistants.pharmacy.AssistantError;
import in.slanglabs.assistants.pharmacy.PharmacyItem;
import in.slanglabs.assistants.pharmacy.PharmacyItem.StrengthUnit;
import in.slanglabs.assistants.pharmacy.PharmacyItem.Type;
import in.slanglabs.assistants.pharmacy.SlangPharmacyAssistant;
import in.slanglabs.platform.SlangSession;
import in.slanglabs.platform.action.SlangAction;

public class RNSlangPharmacyAssistant extends ReactContextBaseJavaModule {

    private static final String TAG = "RNSlangBuddy";
    private static final String CONFIG_BUDDY_ID = "buddyId";
    private static final String CONFIG_API_KEY = "apiKey";
    private static final String CONFIG_LOCALE = "locale";
    private static final String CONFIG_REQUESTED_LOCALES = "requestedLocales";

    private ReadableMap mConfigOptions;
    private SlangSession mCurrentSession;

    private ReactApplicationContext mReactContext;

    RNSlangPharmacyAssistant(ReactApplicationContext reactContext) {
        super(reactContext);
        mReactContext = reactContext;
    }

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

    /**
     * Initialize Slang
     *
     * @param configOptions Configurations
     */
    @ReactMethod
    public void initialize(ReadableMap configOptions) {
        Log.e(TAG, "initialize");
        mConfigOptions = configOptions;

        // Initialize SlangBuddy
        AssistantConfiguration config = new AssistantConfiguration.Builder()
                .setBuddyId(getStringConfig(CONFIG_BUDDY_ID))
                .setAPIKey(getStringConfig(CONFIG_API_KEY))
                .setDefaultLocale(RNSlangLocaleMap.getDefaultLocale(getStringConfig(CONFIG_LOCALE)))
                .setRequestedLocales(RNSlangLocaleMap.getRequestedLocales(getStringListConfig(CONFIG_REQUESTED_LOCALES)))
                .build();

        SlangPharmacyAssistant.initialize(getCurrentActivity().getApplication(), config);
        SlangPharmacyAssistant.setAction(new SlangPharmacyAssistant.Action() {
            @Override
            public Status onPharmacyItemSearched(PharmacyItem pharmacyItem, SlangSession slangSession, Bundle bundle) {
                mCurrentSession = slangSession;
                mCurrentSession.waitForActionCompletion();
                WritableMap map = mapPharmacyItem(pharmacyItem);
                sendEvent("onPharmacyItemSearched", map);
                return Status.SUCCESS;
            }

            @Override
            public void onAssistantError(AssistantError assistantError) {
                WritableMap params = Arguments.createMap();
                params.putInt("type", assistantError.getType());
                sendEvent("onError", params);
            }
        });
    }

    @ReactMethod
    public void startSearchConversation() {
        Log.e(TAG, "startSearchConversation");

        SlangPharmacyAssistant.startSearchConversation();
    }

    /**
     * Notify Slang of a action's success/failure status for Slang to show
     * completion statement
     *
     * @param isSuccess To indicate success/failure.
     */
    @ReactMethod
    public void notifyActionCompleted(boolean isSuccess) {
        if (mCurrentSession != null) {
            mCurrentSession.notifyActionCompleted(
                    isSuccess ? SlangAction.Status.SUCCESS : SlangAction.Status.FAILURE);
        }
    }

    @ReactMethod
    public void show() {
        SlangPharmacyAssistant.getUI().show(getCurrentActivity());
    }

    @ReactMethod
    public void hide() {
        SlangPharmacyAssistant.getUI().hide(getCurrentActivity());
    }

    @ReactMethod void notifyNonVoiceSearch(ReadableMap pharmacyItemDetails) {
        try {
            PharmacyItem.Type type = PharmacyItem.Type.UNKNOWN;
            String rnType = pharmacyItemDetails.getString("type");
            switch (rnType.toLowerCase()) {
                case "syrup":
                case "tonic":
                    type = PharmacyItem.Type.SYRUP;
                    break;
                case "tablet":
                case "tablets":
                case "pills":
                    type = PharmacyItem.Type.TABLET;
                    break;
                case "capsule":
                case "capsules":
                    type = PharmacyItem.Type.CAPSULE;
                    break;
                case "injection":
                    type = PharmacyItem.Type.INJECTION;
                    break;
            }

            PharmacyItem.StrengthUnit unitStrength = StrengthUnit.UNKNOWN;
            String rnUnitStrength = pharmacyItemDetails.getString("unitStrength");
            switch (rnUnitStrength.toLowerCase()) {
                case "gram":
                case "grams":
                case "gm":
                    unitStrength = StrengthUnit.GRAM;
                    break;
                case "milligram":
                case "milligrams":
                case "mg":
                    unitStrength = StrengthUnit.MILLIGRAM;
                    break;
                case "millilitre":
                case "milliliter":
                case "ml":
                case "milliliters":
                case "millilitres":
                    unitStrength = StrengthUnit.MILLILITRE;
                    break;
            }

            final PharmacyItem item = new PharmacyItem.Builder()
                    .setName(pharmacyItemDetails.getString("name"))
                    .setStrength(pharmacyItemDetails.getInt("strength"))
                    .setPackSize(pharmacyItemDetails.getInt("packSize"))
                    .setStrengthUnit(unitStrength)
                    .setType(type)
                    .build();
            SlangPharmacyAssistant.notifyNonVoiceSearch(item);
        } catch (Exception ex) {
            Log.e("SlangPharmacyAssistant", "Failed to record typed search:" + ex.getMessage());
        }
    }

    @ReactMethod
    public void setUserProperties(String userId, ReadableMap rnUserProperties) {
        try {
            Map<String, String> userProperties = new HashMap<>();
            if (null != rnUserProperties && rnUserProperties.toHashMap().size() > 0) {
                for (ReadableMapKeySetIterator it = rnUserProperties.keySetIterator(); it.hasNextKey(); ) {
                    String key = it.nextKey();
                    userProperties.put(key, rnUserProperties.getString(key));
                }
            }
            SlangPharmacyAssistant.setUserProperties(userId, userProperties);
        } catch (Exception ex) {
            Log.e("SlangPharmacyAssistant", "Failed to record user properties:" + ex.getMessage());
        }
    }

    private String getStringConfig(String key) {
        try {
            return mConfigOptions != null && mConfigOptions.hasKey(key) ? mConfigOptions.getString(key) : null;
        } catch (Exception e) {
            Log.d(TAG, "mConfigOptions encountered an error");
        }

        return null;
    }

    private List<String> getStringListConfig(String key) {
        if (mConfigOptions == null && !mConfigOptions.hasKey(key)) return null;

        try {
            ReadableArray stringArray = mConfigOptions.getArray(key);
            if (null == stringArray || stringArray.size() == 0) return null;

            List<String> stringList = new ArrayList<>(stringArray.size());
            for (int i = 0; i < stringArray.size(); i++) {
                stringList.add(stringArray.getString(i));
            }
            return stringList;
        } catch (Exception e) {
            Log.d(TAG, "mConfigOptions encountered an error");
        }

        return null;
    }

    private WritableMap mapPharmacyItem(PharmacyItem item) {
        WritableMap pharmacyItem = Arguments.createMap();
        if (item != null) {
            pharmacyItem.putString("name", item.getName());
            pharmacyItem.putString("strength", String.valueOf(item.getStrength()));
            pharmacyItem.putString("unitStrength", item.getStrengthUnit().name());
            pharmacyItem.putInt("packSize", item.getPackSize());
            pharmacyItem.putString("type", item.getType().name());
        }

        return pharmacyItem;
    }

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