package io.hansel.react;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.WritableNativeArray;
import com.facebook.react.bridge.WritableNativeMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

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

import io.hansel.core.logger.HSLLogger;
import io.hansel.hanselsdk.Hansel;
import io.hansel.hanselsdk.HanselActionListener;
import io.hansel.hanselsdk.HanselDeepLinkListener;
import io.hansel.ujmtracker.HanselInternalEventsListener;
import io.hansel.ujmtracker.HanselTracker;



public class HanselTrackerRn {

    private ReactApplicationContext reactContext;

    public HanselTrackerRn(ReactApplicationContext reactContext) {
        this.reactContext = reactContext;
    }
    private static final String HanselInternalEventIdentifier = "HanselInternalEvent";
    private static final String HanselActionPerformedIdentifier = "HanselActionPerformed";
    private static final String HanselDeepLinkListenerIdentifier = "HanselDeepLinkListener";

    public static void logEvent(String eventName, String vendor, ReadableMap hanselProperties, Callback callback) {
        HSLLogger.d("Bridge log called :::");
        HashMap<String, Object> result = HanselTracker.logEvent(eventName, vendor, hanselProperties.toHashMap());
        WritableMap map = Arguments.createMap();
        int n = result.size();
        List<String> keyList = new ArrayList<>(result.keySet());
        for (int i = 0; i < n; i++) {

            String key = keyList.get(i);
            Object value = result.get(key);
            try {
                if (value instanceof Double || value instanceof Integer || value instanceof Float || value instanceof Long) {
                    map.putDouble(key, Double.parseDouble(value + ""));
                } else if (value instanceof Boolean) {
                    map.putBoolean(key, (Boolean) value);
                } else if (value instanceof String) {
                    map.putString(key, (String) value);
                }

            } catch (Throwable e) {
                HSLLogger.printStackTrace(e);
            }

        }
        if (callback != null) {
            callback.invoke(map);
        }
    }

    // Register tracker listener on show/dismiss nudge events...
    public void registerHanselTrackerListener() {
        HanselInternalEventsListener hanselInternalEventsListener = new HanselInternalEventsListener() {
            @Override
            public void onEvent(String eventName, HashMap dataFromHansel) {
                ReactContext currentContext = reactContext;
                JSONObject jsonObjectFromMap = new JSONObject(dataFromHansel);
                JSONObject jsonObject = new JSONObject();
                try {
                    jsonObject.put("eventName", eventName);
                    jsonObject.put("properties",jsonObjectFromMap);
                    WritableMap deeplinkPayload = jsonToWritableMap(jsonObject);
                    sendEvent(currentContext, HanselInternalEventIdentifier, deeplinkPayload);
                } catch (JSONException e) {
                    e.printStackTrace();
                }
            }
        };
        HanselTracker.registerListener(hanselInternalEventsListener);
    }

    // Register action listener for invoke actions...
    public void registerHanselActionListenerWithActionName(String actionName) {
        //Create an instance of HanselActionListener
        HanselActionListener hanselActionListener = new HanselActionListener() {
            @Override
            public void onActionPerformed(String action) {
                //Perform task based on action
                ReactContext currentContext = reactContext;
                WritableMap params = Arguments.createMap();
                params.putString("action", action);
                sendEvent(currentContext, HanselActionPerformedIdentifier, params);
            }
        };
        //Register the instance with this line:
        Hansel.registerHanselActionListener(actionName, hanselActionListener);
    }

    // Register deeplink action listener for invoke actions...
    public void registerHanselDeeplinkListener() {
        Hansel.registerHanselDeeplinkListener(new HanselDeepLinkListener() {
            @Override
            public void onLaunchUrl(String url) {
                //Perform task based on action
                ReactContext currentContext = reactContext;
                WritableMap params = Arguments.createMap();
                params.putString("deeplink", url);
                sendEvent(currentContext, HanselDeepLinkListenerIdentifier, params);
            }
        });
    }

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

    public void deRegisterListener() {
        HanselTracker.deRegisterListener(null);
    }

    // This is empty method used in iOS only....
    public void removeHanselActionListenerForAction(String actionName) {
        HanselTracker.deRegisterListener(null);
    }

    public static WritableMap jsonToWritableMap(JSONObject jsonObject) {
        WritableMap writableMap = new WritableNativeMap();

        if (jsonObject == null) {
            return null;
        }


        Iterator<String> iterator = jsonObject.keys();
        if (!iterator.hasNext()) {
            return null;
        }

        while (iterator.hasNext()) {
            String key = iterator.next();

            try {
                Object value = jsonObject.get(key);

                if (value == null) {
                    writableMap.putNull(key);
                } else if (value instanceof Boolean) {
                    writableMap.putBoolean(key, (Boolean) value);
                } else if (value instanceof Integer) {
                    writableMap.putInt(key, (Integer) value);
                } else if (value instanceof Double || value instanceof Long || value instanceof Float) {
                    String str = String.valueOf(value);
                    writableMap.putDouble(key, Double.parseDouble(str));
                } else if (value instanceof String) {
                    writableMap.putString(key, value.toString());
                } else if (value instanceof JSONObject) {
                    writableMap.putMap(key, jsonToWritableMap((JSONObject) value));
                } else if (value instanceof JSONArray) {
                    writableMap.putArray(key, jsonArrayToWritableArray((JSONArray) value));
                } else if (value.getClass().isEnum()) {
                    writableMap.putString(key, value.toString());
                }
            } catch (Throwable e) {
                e.printStackTrace();
            }
        }

        return writableMap;
    }

    public static WritableArray jsonArrayToWritableArray(JSONArray jsonArray) {
        WritableArray writableArray = new WritableNativeArray();

        if (jsonArray == null) {
            return null;
        }

        if (jsonArray.length() <= 0) {
            return null;
        }

        for (int i = 0 ; i < jsonArray.length(); i++) {
            try {
                Object value = jsonArray.get(i);

                if (value == null) {
                    writableArray.pushNull();
                } else if (value instanceof Boolean) {
                    writableArray.pushBoolean((Boolean) value);
                } else if (value instanceof Integer) {
                    writableArray.pushInt((Integer) value);
                } else if (value instanceof Double || value instanceof Long || value instanceof Float) {
                    String str = String.valueOf(value);
                    writableArray.pushDouble(Double.parseDouble(str));
                } else if (value instanceof String) {
                    writableArray.pushString(value.toString());
                } else if (value instanceof JSONObject) {
                    writableArray.pushMap(jsonToWritableMap((JSONObject) value));
                } else if (value instanceof JSONArray) {
                    writableArray.pushArray(jsonArrayToWritableArray((JSONArray) value));
                } else if (value.getClass().isEnum()) {
                    writableArray.pushString(value.toString());
                }
            } catch (Throwable e) {
                e.printStackTrace();
            }
        }

        return writableArray;
    }
}

