package in.slanglabs;

import android.util.Log;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.ReadableMapKeySetIterator;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;

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

import in.slanglabs.assistants.base.AssistantUIPosition;
import in.slanglabs.assistants.fitness.AssistantSubDomain;
import in.slanglabs.assistants.fitness.AssistantSurfaceStyle;
import in.slanglabs.assistants.fitness.AssistantTriggerStyle;
import in.slanglabs.assistants.fitness.FitnessUIPosition;
import in.slanglabs.assistants.fitness.Food;
import in.slanglabs.assistants.fitness.FoodInfo;
import in.slanglabs.assistants.fitness.NavigationInfo;
import in.slanglabs.assistants.fitness.SlangFitnessAssistant;
import in.slanglabs.assistants.fitness.SugarInfo;
import in.slanglabs.assistants.fitness.WeightInfo;

public class RNSlangUtils {

    private static String TAG = "RNSlangUtils";
    private static ReadableMap mConfigOptions;

    static void setConfigOptions(ReadableMap configOptions) {
        mConfigOptions = configOptions;
    }

    static WritableMap mapNavigationInfo(NavigationInfo navigationInfo) {
        WritableMap navigationInfoMap = Arguments.createMap();
        if (navigationInfo != null) {
            navigationInfoMap.putString("target", navigationInfo.getTarget());
        }
        return navigationInfoMap;
    }

    static WritableMap mapFoodInfo(FoodInfo foodInfo) {
        WritableMap foodInfoMap = Arguments.createMap();
        if (foodInfo != null) {
            if (foodInfo.getLogDate() != null) {
                foodInfoMap.putInt("logDate", dateToInt(foodInfo.getLogDate()));
            }
            if (foodInfo.getLogTime() != null) {
                foodInfoMap.putInt("logTime", dateToInt(foodInfo.getLogTime()));
            }
            if (foodInfo.getMealType() != null) {
                foodInfoMap.putString("mealType", foodInfo.getMealType().name());
            }
            if(foodInfo.getLogConfirmationStatus()!=null){
                foodInfoMap.putString("confirmationStatus", foodInfo.getLogConfirmationStatus().name());
            }
            foodInfoMap.putArray("foods", mapFoods(foodInfo.getFoods()));
        }
        return foodInfoMap;
    }

    static WritableMap mapSugarInfo(SugarInfo sugarInfo) {
        WritableMap sugarInfoMap = Arguments.createMap();
        if (sugarInfo != null) {
            if (sugarInfo.getLogDate() != null) {
                sugarInfoMap.putInt("logDate", dateToInt(sugarInfo.getLogDate()));
            }
            if (sugarInfo.getLogTime() != null) {
                sugarInfoMap.putInt("logTime", dateToInt(sugarInfo.getLogTime()));
            }
            if (sugarInfo.getLogPeriod() != null) {
                sugarInfoMap.putString("logPeriod", sugarInfo.getLogPeriod().name());
            }
            if (sugarInfo.getSugarValue() != null) {
                sugarInfoMap.putDouble("sugarValue", sugarInfo.getSugarValue());
            }
            if(sugarInfo.getConfirmationStatus() != null){
                sugarInfoMap.putString("confirmationStatus", sugarInfo.getConfirmationStatus().name());
            }
        }
        return sugarInfoMap;
    }

    static WritableMap mapWeightInfo(WeightInfo weightInfo) {
        WritableMap weightInfoMap = Arguments.createMap();
        if (weightInfo != null) {
            if (weightInfo.getLogDate() != null) {
                weightInfoMap.putInt("logDate", dateToInt(weightInfo.getLogDate()));
            }
            if (weightInfo.getLogTime() != null) {
                weightInfoMap.putInt("logTime", dateToInt(weightInfo.getLogTime()));
            }
            if(weightInfo.getConfirmationStatus() != null){
                weightInfoMap.putString("confirmationStatus", weightInfo.getConfirmationStatus().name());
            }
            if(weightInfo.getWeightValue().getValue() != null) {
                weightInfoMap.putDouble("weightValue", weightInfo.getWeightValue().getValue());
            }
            if(weightInfo.getWeightValue().getUnit() != null) {
                weightInfoMap.putString("weightUnit", weightInfo.getWeightValue().getUnit().name());
            }
        }
        return weightInfoMap;
    }

    static WritableArray mapFoods(List<Food> foods) {
        WritableArray foodsArray = Arguments.createArray();
        if (foods != null) {
            for (Food food : foods) {
                foodsArray.pushMap(mapFood(food));
            }
        }
        return foodsArray;
    }

    static WritableMap mapFood(Food food) {
        WritableMap foodMap = Arguments.createMap();
        if (food != null) {
            foodMap.putString("foodName", food.getFoodName());
            foodMap.putMap("foodQuantity", mapQuantity(food.getFoodQuantity()));
            foodMap.putBoolean("isRecognized", food.getIsRecognized());
        }
        return foodMap;
    }

    static Food mapToFood(ReadableMap foodMap) {
        Food.Builder food = new Food.Builder();
        if (foodMap != null) {
            food.setName(foodMap.getString("name"));
            ReadableMap quantityMap = foodMap.getMap("quantity");
            if (quantityMap != null) {
                Food.Quantity.Unit unit = Food.Quantity.Unit.NUMBER;
                float amount = 1;
                if (quantityMap.getString("amount") != null) {
                    amount = Float.parseFloat(quantityMap.getString("amount"));
                }
                food.setName(foodMap.getString("name"));
                food.setQuantity(new Food.Quantity(amount, unit));
            }
        }
        return food.build();
    }

    static WritableMap mapQuantity(Food.Quantity quantity) {
        WritableMap foodMap = Arguments.createMap();
        if (quantity != null) {
            foodMap.putDouble("amount", quantity.getAmount());
            foodMap.putString("unit", quantity.getUnit().name());
        }
        return foodMap;
    }

    private static Integer dateToInt(Date date) {
        if (date != null) return (int) (date.getTime() / 1000);
        return null;
    }

    private static Date getDateFromInt(Integer date) {
        if (date != null) return new Date(date * 1000);
        return null;
    }

    static 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;
    }

    static boolean getBooleanConfig(String key) {
        try {
            return (mConfigOptions != null && mConfigOptions.hasKey(key)) &&
                    mConfigOptions.getBoolean(key);
        } catch (Exception e) {
            Log.d(TAG, "mConfigOptions encountered an error");
        }

        return false;
    }

    static float getFloatConfig(String key) {
        try {
            return mConfigOptions != null && mConfigOptions.hasKey(key) ?
                    (float) (mConfigOptions.getDouble(key)) : -1;
        } catch (Exception e) {
            Log.d(TAG, "mConfigOptions encountered an error");
        }

        return -1;
    }

    static int getIntConfig(String key) {
        try {
            return mConfigOptions != null && mConfigOptions.hasKey(key) ?
                    (int) (mConfigOptions.getInt(key)) : -1;
        } catch (Exception e) {
            Log.d(TAG, "mConfigOptions encountered an error");
        }

        return -1;
    }

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

        return null;
    }

    static 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 = getStringListFromArray(stringArray);
            return stringList;
        } catch (Exception e) {
            Log.d(TAG, "mConfigOptions encountered an error");
        }

        return null;
    }

    static List<String> getStringListFromArray(ReadableArray readableArray) {
        List<String> stringList = new ArrayList<>(readableArray.size());
        for (int i = 0; i < readableArray.size(); i++) {
            stringList.add(readableArray.getString(i));
        }
        return stringList;
    }

    static boolean configHasKey(String key) {
        return mConfigOptions != null && mConfigOptions.hasKey(key);
    }

    static Map<Locale, String> getLocaleStringMapConfig(String key) {
        if (mConfigOptions == null || !mConfigOptions.hasKey(key))
            return null;

        ReadableMap stringMap = mConfigOptions.getMap(key);
        if (null == stringMap) return null;

        ReadableMapKeySetIterator iterator = stringMap.keySetIterator();
        HashMap<Locale, String> localeStringMap = new HashMap<>();
        while (iterator.hasNextKey()) {
            String currentKey = iterator.nextKey();
            Locale locale = RNSlangLocaleMap.getLocale(currentKey);
            if (locale != null) {
                String value = stringMap.getString(currentKey);
                if (value != null) {
                    localeStringMap.put(locale, value);
                }
            }
        }

        return localeStringMap;
    }

    static AssistantUIPosition getAssistantUIConfig(String key) {
        FitnessUIPosition.UIPosition uiPosition
                = FitnessUIPosition.UIPosition.BOTTOM_CENTER;
        FitnessUIPosition assistantUIPosition
                = new FitnessUIPosition.Builder()
                .setBaseUIPosition(uiPosition)
                .setIsDraggable(false)
                .setIsForcedAtStartup(false)
                .build();

        if (mConfigOptions == null || !mConfigOptions.hasKey(key))
            return assistantUIPosition;

        try {
            ReadableMap assistantUIMap = mConfigOptions.getMap(key);
            if (null == assistantUIMap)
                return assistantUIPosition;
            String baseUIPosition = assistantUIMap.getString("baseUIPosition");
            switch (baseUIPosition != null ? baseUIPosition : "") {
                case "BOTTOM_LEFT":
                    uiPosition = FitnessUIPosition.UIPosition.BOTTOM_LEFT;
                    break;
                case "CENTER_LEFT":
                    uiPosition = FitnessUIPosition.UIPosition.CENTER_LEFT;
                    break;
                case "CENTER":
                    uiPosition = FitnessUIPosition.UIPosition.CENTER;
                    break;
                case "CENTER_RIGHT":
                    uiPosition = FitnessUIPosition.UIPosition.CENTER_RIGHT;
                    break;
                case "TOP_LEFT":
                    uiPosition = FitnessUIPosition.UIPosition.TOP_LEFT;
                    break;
                case "TOP_CENTER":
                    uiPosition = FitnessUIPosition.UIPosition.TOP_CENTER;
                    break;
                case "TOP_RIGHT":
                    uiPosition = FitnessUIPosition.UIPosition.TOP_RIGHT;
                    break;
                case "BOTTOM_RIGHT":
                    uiPosition = FitnessUIPosition.UIPosition.BOTTOM_RIGHT;
                    break;
                default:
                    uiPosition = FitnessUIPosition.UIPosition.BOTTOM_CENTER;
            }
            int offsetX = assistantUIMap.getInt("offsetX");
            int offsetY = assistantUIMap.getInt("offsetY");
            boolean draggable = assistantUIMap.getBoolean("draggable");
            boolean forcedAtStartup = assistantUIMap.getBoolean("forcedAtStartup");
            assistantUIPosition = new FitnessUIPosition.Builder()
                    .setBaseUIPosition(uiPosition)
                    .setOffsetXDp(offsetX)
                    .setOffsetYDp(offsetY)
                    .setIsDraggable(draggable)
                    .setIsForcedAtStartup(forcedAtStartup)
                    .build();
            return assistantUIPosition;
        } catch (Exception e) {
            Log.d(TAG, "mConfigOptions encountered an error");
            return assistantUIPosition;
        }
    }

    static AssistantTriggerStyle getTriggerStyle(String key) {
        AssistantTriggerStyle assistantTriggerStyle = AssistantTriggerStyle.FLAT;
        if(mConfigOptions == null || !mConfigOptions.hasKey(key)) {
            return  assistantTriggerStyle;
        }
        String triggerStyleString = getStringConfig(key);
        if("default".equals((triggerStyleString != null ? triggerStyleString : ""))) {
            return AssistantTriggerStyle.DEFAULT;
        }
        return AssistantTriggerStyle.FLAT;
    }

    static AssistantSurfaceStyle getSurfaceStyle(String key) {
        AssistantSurfaceStyle assistantSurfaceStyle = AssistantSurfaceStyle.CARD;
        if(mConfigOptions == null || !mConfigOptions.hasKey(key)) {
            return assistantSurfaceStyle;
        }
        String surfaceStyleString = getStringConfig(key);
        switch (surfaceStyleString != null ? surfaceStyleString : "") {
            case "gradient" :
                return AssistantSurfaceStyle.GRADIENT;
            case "minimal_card" :
                return AssistantSurfaceStyle.MINIMAL_CARD;
            default:
                return AssistantSurfaceStyle.CARD;
        }
    }

    // Dynamically convert ReadableMap to Object.
    //  Recursive solution available: https://github.com/facebook/react-native/issues/4655
    /*
    static Map<String, Object> getConfigOverridesMapFromConfig(String key) {
        if (mConfigOptions == null || !mConfigOptions.hasKey(key))
            return null;

        ReadableMap map = mConfigOptions.getMap(key);
        if (null == map) return null;

        ReadableMapKeySetIterator iterator = map.keySetIterator();
        HashMap<String, Object> configOverridesMap = new HashMap<>();
        while (iterator.hasNextKey()) {
            String currentKey = iterator.nextKey();
            ReadableMap objectMap = map.getMap(currentKey);
            // dynamically convert this map to object
        }
        return configOverridesMap;
    }
     */

    static Map<String, String> convertReadableMapToMap(ReadableMap map) {
        Map<String, String> userJourneyInfoMap = new HashMap<>();
        if (null != map && map.toHashMap().size() > 0) {
            for (ReadableMapKeySetIterator it = map.keySetIterator(); it.hasNextKey(); ) {
                String key = it.nextKey();
                String value;
                switch (map.getType(key)) {
                    case Number:
                        value = String.valueOf(map.getDouble(key));
                        break;
                    case String:
                        value = map.getString(key);
                        break;
                    case Boolean:
                        value = Boolean.toString(map.getBoolean(key));
                        break;
                    default:
                        value = "";
                }
                userJourneyInfoMap.put(key, value != null ? value : "");
            }
        }
        return userJourneyInfoMap;
    }

    static SlangFitnessAssistant.Environment getEnvironmentFromString(String value){
        if (null != value && !value.isEmpty()) {
            for (SlangFitnessAssistant.Environment environment : SlangFitnessAssistant.Environment.values()) {
                if (value.equalsIgnoreCase(environment.name())) return environment;
            }
        }
        return null;
    }
}