package com.helpshift.react;

import android.app.Application;
import androidx.annotation.NonNull;

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.ReactMethod;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.helpshift.HSDebugLog;
import com.helpshift.Helpshift;
import com.helpshift.HelpshiftAuthenticationFailureReason;
import com.helpshift.HelpshiftEventsListener;
import com.helpshift.HelpshiftUserLoginEventsListener;
import com.helpshift.log.HSLogger;
import com.helpshift.proactive.HelpshiftProactiveAPIConfigCollector;
import com.helpshift.util.Utils;
import com.helpshift.util.JsonUtils;
import com.helpshift.react.utils.InstallUtils;
import com.helpshift.react.storage.HelpshiftReactNativeStorage;


import org.json.JSONObject;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

import com.facebook.react.bridge.Promise;

@ReactModule(name = HelpshiftModule.NAME)
public class HelpshiftModule extends HelpshiftPluginSdkxReactNativeSpec {

    public static final String NAME = "HelpshiftPluginSdkxReactNative";
    public static final String TAG = "HelpshiftReactNativeModule";
    private final ReactContext reactApplicationContext;

    HelpshiftModule(ReactApplicationContext context) {
        super(context);
        this.reactApplicationContext = context;

    }

    @ReactMethod
    public void pauseDisplayOfInAppNotification(boolean shouldPauseInAppNotification) {
        Helpshift.shouldPauseInAppNotification(shouldPauseInAppNotification);
    }

    private final HelpshiftEventsListener helpshiftEventListener = new HelpshiftEventsListener() {
        @Override
        public void onEventOccurred(String eventName, Map<String, Object> map) {
            HSLogger.d(TAG, eventName + " " + map);
            sendEvent("onEventOccurred", eventName, map);
        }

        @Override
        public void onUserAuthenticationFailure(HelpshiftAuthenticationFailureReason helpshiftAuthenticationFailureReason) {
            HSLogger.d(TAG, "Error " + helpshiftAuthenticationFailureReason.name());
            sendEvent("onUserAuthenticationFailure", helpshiftAuthenticationFailureReason.name(), new HashMap<>());
        }
    };

    @NonNull
    @Override
    public String getName() {
        return NAME;
    }

    @ReactMethod
    public void install(String appid, String domain, ReadableMap config) {
        HSLogger.d(TAG, "Install config " + config);
        try {
            Map<String, Object> configMap = config.toHashMap();
            Application application = (Application) reactApplicationContext.getApplicationContext();
            HelpshiftReactNativeStorage storage = HelpshiftReactNativeStorage.getInstance(application);
            storage.putJsonStringOfMap(HelpshiftReactNativeStorage.INSTALL_CONFIG, JsonUtils.mapToJsonString(configMap));
            storage.put(HelpshiftReactNativeStorage.DOMAIN_NAME, domain);
            storage.put(HelpshiftReactNativeStorage.PLATFORM_ID, appid);
            
            InstallUtils.commonInstall(application, appid, domain, configMap);

            Helpshift.setHelpshiftEventsListener(helpshiftEventListener);
        } catch (Exception e) {
            HSLogger.e(TAG, "Failed to install Helpshift SDK. Ensure the app meets the min API level requirement. Exception: ", e);
        }
    }

    @ReactMethod
    public void showConversation(ReadableMap config) {
        HSLogger.d(TAG, "showConversation called with config " + config);
        Helpshift.showConversation(Objects.requireNonNull(getCurrentActivity()), config.toHashMap());
    }

    @ReactMethod
    public void showFAQsWithConfig(ReadableMap config) {
        HSLogger.d(TAG, "showFAQs called with config " + config);
        Helpshift.showFAQs(Objects.requireNonNull(getCurrentActivity()), config.toHashMap());
    }

    @ReactMethod
    public void showSingleFaqWithConfig(String faqPublishID, ReadableMap config) {
        HSLogger.d(TAG, "showSingleFAQ called with config " + config + " publish id " + faqPublishID);
        Helpshift.showSingleFAQ(Objects.requireNonNull(getCurrentActivity()), faqPublishID, config.toHashMap());
    }

    @ReactMethod
    public void showFAQSectionWithConfig(String faqSectionPublishID, ReadableMap config) {
        HSLogger.d(TAG, "showFAQSection called with config " + config + " section id " + faqSectionPublishID);
        Helpshift.showFAQSection(Objects.requireNonNull(getCurrentActivity()), faqSectionPublishID, config.toHashMap());
    }

    @ReactMethod
    public void addUserTrail(String userTrail) {
        HSLogger.d(TAG, "userTrail called with trail " + userTrail);
        Helpshift.addUserTrail(userTrail);
    }

    @ReactMethod
    public void clearAnonymousUserOnLogin(boolean shouldClear) {
        HSLogger.d(TAG, "clearAnonymousUserOnLogin called with param " + shouldClear);
        Helpshift.clearAnonymousUserOnLogin(shouldClear);
    }

    @ReactMethod
    public void login(ReadableMap data, Callback callback) {
        HSLogger.d(TAG, "login called with data " + data);
        Map<String, String> loginMap = Utils.getStringMapFromObjectMap(data.toHashMap());
        callback.invoke(Helpshift.login(loginMap));
    }

    @ReactMethod
    public void logout() {
        HSLogger.d(TAG, "logout called");
        Helpshift.logout();
    }

    @ReactMethod
    public void setLanguage(String languageCode) {
        HSLogger.d(TAG, "setLanguage called with language code " + languageCode);
        Helpshift.setLanguage(languageCode);
    }

    @ReactMethod
    public void closeSDKSession() {
        HSLogger.d(TAG, "closeSession called ");
        Helpshift.closeSession();
    }

    @ReactMethod
    public void clearBreadcrumbs() {
        HSLogger.d(TAG, "clearBreadCrumbs called");
        Helpshift.clearBreadCrumbs();
    }

    @ReactMethod
    public void leaveBreadCrumb(String breadCrumb) {
        HSLogger.d(TAG, "leaveBreadCrumb called with value " + breadCrumb);
        Helpshift.leaveBreadCrumb(breadCrumb);
    }

    @ReactMethod
    public void handleProactive(String proactiveLink, ReadableMap config) {
        HSLogger.d(TAG, "handleProactiveLink called with handleProactiveLink " + proactiveLink);
        HSLogger.d(TAG, "handleProactiveLink called with localConfig " + config);
        HelpshiftProactiveAPIConfigCollector helpshiftProactiveAPIConfigCollector = new HelpshiftProactiveAPIConfigCollector() {
            @Override
            public Map<String, Object> getAPIConfig() {
                return config.toHashMap();
            }
        };
        Helpshift.setHelpshiftProactiveConfigCollector(helpshiftProactiveAPIConfigCollector);
        Helpshift.handleProactiveLink(proactiveLink);
    }

    @ReactMethod
    public void registerPushToken(String deviceToken) {
        HSLogger.d(TAG, "registerPushToken called with token " + deviceToken);
        Helpshift.registerPushToken(deviceToken);
    }

    @ReactMethod
    public void handlePush(ReadableMap userInfo, boolean isAppLaunch) {
        HSLogger.d(TAG, "handlePush called with token " + userInfo);
        Map<String, String> userMap = Utils.getStringMapFromObjectMap(userInfo.toHashMap());
        Helpshift.handlePush(userMap);
    }

    @ReactMethod
    public void handleIOSForegroundNotification(ReadableMap userInfo, Callback callback) {
        // iOS-specific API: No operation required on Android
    }

    @ReactMethod
    public void handleIOSNotificationClick(ReadableMap userInfo) {
        // iOS-specific API: No operation required on Android
    }

    @ReactMethod
    public void setProactiveConfig(ReadableMap config) {
        HSLogger.d(TAG, "setProactiveConfig called with config " + config);
        final Map<String, Object> proactiveConfig = config.toHashMap();
        HelpshiftProactiveAPIConfigCollector helpshiftProactiveAPIConfigCollector = new HelpshiftProactiveAPIConfigCollector() {
            @Override
            public Map<String, Object> getAPIConfig() {
                return proactiveConfig;
            }
        };
        Helpshift.setHelpshiftProactiveConfigCollector(helpshiftProactiveAPIConfigCollector);
    }

    @ReactMethod
    public void handleIOSSilentNotification(ReadableMap userInfo, Callback callback) {
        // iOS-specific API: No operation required on Android
    }

    @ReactMethod
    public void onAppBackground() {
        HSLogger.d(TAG, "onAppBackground called");
        Helpshift.onAppBackground();
    }

    @ReactMethod
    public void onAppForeground() {
        HSLogger.d(TAG, "onAppForeground called");
        Helpshift.onAppForeground();
    }

    @ReactMethod
    public void requestUnreadMessageCount(boolean shouldFetchFromServer) {
        HSLogger.d(TAG, "requestUnreadMessageCount called with fetchFromServerFlag " + shouldFetchFromServer);
        Helpshift.requestUnreadMessageCount(shouldFetchFromServer);
    }

    @ReactMethod
    public void addDebugLog(String level, String tag, String message) {
        switch (level) {
            case "Verbose":
                HSDebugLog.v(tag, message);
                break;
            case "Debug":
                HSDebugLog.d(tag, message);
                break;
            case "Info":
                HSDebugLog.i(tag, message);
                break;
            case "Warn":
                HSDebugLog.w(tag, message);
                break;
            case "Error":
                HSDebugLog.e(tag, message);
                break;
        }
    }

    @ReactMethod
    public void loginWithIdentity(String jwt, ReadableMap config, Promise promise) {
        try {

            Helpshift.loginWithIdentity(jwt, config.toHashMap(), new HelpshiftUserLoginEventsListener() {
                @Override
                public void onLoginSuccess() {
                    promise.resolve(true);
                }

                @Override
                public void onLoginFailure(String reason, Map<String, String> errorData) {
                    WritableMap errorMap = Arguments.createMap();

                    if (errorData != null) {
                        for (Map.Entry<String, String> entry : errorData.entrySet()) {
                            errorMap.putString(entry.getKey(), entry.getValue());
                        }
                    }

                    promise.reject("LOGIN_FAILED", reason, errorMap);
                }
            });
        } catch (Exception e) {
            // In case an exception is thrown, ensure that the promise is rejected
            WritableMap errorMap = Arguments.createMap();
            errorMap.putString("exception", e.getMessage());
            promise.reject("LOGIN_FAILED", e.getMessage(), errorMap);
        }
    }

    @ReactMethod
    public void addUserIdentities(String identitiesJwt) {
        Helpshift.addUserIdentities(identitiesJwt);
    }

    @ReactMethod
    public void updateMasterAttributes(ReadableMap attributes) {
        Helpshift.updateMasterAttributes(attributes.toHashMap());
    }

    @ReactMethod
    public void updateAppAttributes(ReadableMap attributes) {
        Helpshift.updateAppAttributes(attributes.toHashMap());
    }

    @ReactMethod
    public void setAndroidProactivePushNotificationDefaults(ReadableMap config) {
        Helpshift.setProactivePushNotificationDefaults(config.toHashMap());
    }

    @ReactMethod
    public void addListener(String eventName) {
        // Keep: Required for RN built in Event Emitter Calls.
    }

    @ReactMethod
    public void removeListeners(Integer count) {
        // Keep: Required for RN built in Event Emitter Calls.
    }
}
