package com.onetrust;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.util.Log;
import android.os.Build;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.LifecycleEventListener;
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.ReadableMap;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.common.LifecycleState;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.onetrust.otpublishers.headless.Public.DataModel.OTProfileSyncParams;
import com.onetrust.otpublishers.headless.Public.DataModel.OTSdkParams;
import com.onetrust.otpublishers.headless.Public.DataModel.OTUXParams;
import com.onetrust.otpublishers.headless.Public.Keys.OTBroadcastServiceKeys;
import com.onetrust.otpublishers.headless.Public.OTConsentInteractionType;
import com.onetrust.otpublishers.headless.Public.OTPublishersHeadlessSDK;
import com.onetrust.otpublishers.headless.Public.OTCallback;
import com.onetrust.otpublishers.headless.Public.Response.OTResponse;
import com.onetrust.otpublishers.headless.Public.DataModel.OTConfiguration;
import com.onetrust.otpublishers.headless.Internal.Log.OTLoggerConstant;
import com.onetrust.otpublishers.headless.gcm.consent.OTGoogleConsentModeData;
import com.facebook.react.module.annotations.ReactModule;
import com.onetrust.otpublishers.headless.Public.OTEventListener;
import com.onetrust.otpublishers.headless.Public.OTUIDisplayReason.OTUIDisplayReason;

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

@ReactModule(name= "OneTrust")
public class OneTrust extends ReactContextBaseJavaModule {
    OTPublishersHeadlessSDK ot;
    String LOG_TAG = "OTReactBridge";
    //constructor
    public OneTrust(ReactApplicationContext reactContext) {
        super(reactContext);
        ot = new OTPublishersHeadlessSDK(getReactApplicationContext());
    }

    //Mandatory function getName that specifies the module name
    @Override
    public String getName() {
        return "OneTrust";
    }

    @ReactMethod
    public void startSDK(String storageLocation, String domainIdentifier, String languageCode, ReadableMap params, final Boolean autoShowBanner, final Promise promise) {
        OTProfileSyncParams syncParams = null;
        OTUXParams uxParams = null;
        /*If profileSyncParams are included, we'll add those into the params object later on.
          This does require that the identifier and the JWT are both present, otherwise it will
          fail gracefully.*/
        if(params.hasKey("profileSyncParams")) {
            ReadableMap profileSyncParams = params.getMap("profileSyncParams");
            String identifier = getParamStringValue(profileSyncParams, "identifier");
            String jwt = getParamStringValue(profileSyncParams, "syncProfileAuth");
            if(!identifier.equals("") && !jwt.equals("")) {
                syncParams = OTProfileSyncParams.OTProfileSyncParamsBuilder.newInstance()
                        .setSyncProfileAuth(jwt)
                        .setIdentifier(identifier)
                        .setSyncProfile("true")
                        .build();
            }
        }

        if(params.hasKey("androidUXParams")){
            try {
                JSONObject json = new JSONObject(params.getString("androidUXParams"));
                uxParams = OTUXParams.OTUXParamsBuilder.newInstance()
                        .setUXParams(json)
                        .build();
            } catch (JSONException e) {
                Log.e(LOG_TAG, "Error parsing JSON from UXParamsJSON");
                e.printStackTrace();
            }
        }

        OTSdkParams.SdkParamsBuilder initParamsBuilder = OTSdkParams.SdkParamsBuilder.newInstance()
                .setAPIVersion(getParamStringValue(params, "sdkVersion"))
                .setOTCountryCode(getParamStringValue(params, "countryCode"))
                .setOTRegionCode(getParamStringValue(params, "regionCode"))
                .setUCPurposeFlow(getParamStringValue(params, "setUCPurposeFlow"));

        if(syncParams != null){
            initParamsBuilder.setProfileSyncParams(syncParams)
                .shouldCreateProfile("true");
        }

        if(uxParams != null){
            initParamsBuilder.setOTUXParams(uxParams);
        }

        OTSdkParams initParams = initParamsBuilder.build();

        ot.startSDK(storageLocation, domainIdentifier, languageCode, initParams, new OTCallback() {
            @Override
            public void onSuccess(@NonNull OTResponse otResponse) {
                Log.i(LOG_TAG,"Data Downloaded Successfully");
                if(autoShowBanner){
                    showBannerAutomatically(params);
                }

                WritableMap response = Arguments.createMap();
                response.putBoolean("status", true);
                response.putString("error", otResponse.getResponseMessage());
                response.putString("responseString", otResponse.getResponseData());
                promise.resolve(response);
            }

            @Override
            public void onFailure(@NonNull OTResponse otResponse) {
                Log.e(LOG_TAG,"Data Download Unsuccessful");
                promise.reject(otResponse.getResponseMessage()+" - Error Code: "+otResponse.getResponseCode(),"OT SDK Download Failed");
            }
        });
    }

    private void showBannerAutomatically(ReadableMap params){
        //End function if shouldShowBanner comes back as false
        if(!ot.shouldShowBanner()){ return; };

        /*Check if the app is in resumed state.
          To hit this block, we'll need the app to be resumed AND internal shouldShowBanner
          call to be TRUE. If all of those conditions are met, we'll proceed to show the banner UI.
          There's also a check in showBannerUI to make sure the app is resumed to avoid a crash. */
        if(appIsActive()){
            showBannerUI(params);

        /*If we find that the we should be showing the banner, but the app is not
          active, we'll add a lifecycle callback that only runs once. It will load
          the banner the next time the user resumes the application.*/
        }else{
            Log.i(LOG_TAG, "Adding lifecycle callback to load banner the next time the app resumes.");
            if(getReactApplicationContext() == null) { return; }
            getReactApplicationContext().addLifecycleEventListener(new LifecycleEventListener() {
                /*We already know that shouldShowBanner and autoShowBanner are true, so we
                can assume that the user isn't going to be able to give consent outside of
                the app before they put it back in the foreground, so we'll show
                the banner on resume*/
                @Override
                public void onHostResume() {
                    /*Null check to make sure the user didn't background the application and
                    then clear the cache. If they did, you would get an in-app warning
                    to download data.*/
                    if(ot.getBannerData()!= null){
                        showBannerUI(params);
                    }

                /*Get rid of this listener so it doesn't run every time the app enters
                the foreground*/
                    getReactApplicationContext().removeLifecycleEventListener(this);
                }

                @Override
                public void onHostPause() { }

                @Override
                public void onHostDestroy() { }
            });

        }
    }

    private String getParamStringValue(ReadableMap params, String key){
        String value = "";
        if(params.hasKey(key)){
            value = params.getString(key);
        }
        return value;
    }

    private Boolean appIsActive(){
        return getReactApplicationContext().getLifecycleState() == LifecycleState.RESUMED;
    }

    @ReactMethod
    public void showBannerUI(ReadableMap params){
      final Activity currentActivity = getCurrentActivity();
        if(currentActivity == null || !appIsActive()) { return; }
        OTConfiguration.OTConfigurationBuilder otConfigurationBuilder = OTConfiguration.OTConfigurationBuilder.newInstance();
        if (null != params) {
            if (params.hasKey("enableDarkMode")) {
                String isEnableDarkMode = getParamStringValue(params, "enableDarkMode");
                if (null != isEnableDarkMode && "" != isEnableDarkMode) {
                otConfigurationBuilder = otConfigurationBuilder.shouldEnableDarkMode(isEnableDarkMode);
                }

            }
        }
        final OTConfiguration otConfiguration = otConfigurationBuilder.build();
        
        currentActivity.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                ot.showBannerUI((AppCompatActivity) currentActivity, otConfiguration);
            }
        });
    }

    @ReactMethod
    public void showPreferenceCenterUI(ReadableMap params){
        final Activity currentActivity = getCurrentActivity();
        if(currentActivity == null || !appIsActive()) { return; }
        OTConfiguration.OTConfigurationBuilder otConfigurationBuilder = OTConfiguration.OTConfigurationBuilder.newInstance();
        if (null != params) {
            if (params.hasKey("enableDarkMode")) {
                String isEnableDarkMode = getParamStringValue(params, "enableDarkMode");
                if (null != isEnableDarkMode && "" != isEnableDarkMode) {
                otConfigurationBuilder = otConfigurationBuilder.shouldEnableDarkMode(isEnableDarkMode);
                }

            }
        }
        final OTConfiguration otConfiguration = otConfigurationBuilder.build();
        currentActivity.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                ot.showPreferenceCenterUI((AppCompatActivity) currentActivity, otConfiguration);
            }
        });


    }

    // @ReactMethod
    // public void setDataSubjectIdentifier(String identifier){
    //     ot.overrideDataSubjectIdentifier(identifier);
    // }

    @ReactMethod
    public void getConsentStatusForCategory(String categoryId, Promise promise){
        Integer consentValue = ot.getConsentStatusForGroupId(categoryId);
        promise.resolve(consentValue);
    }

    @ReactMethod
    public void getConsentStatusForSDKId(String SDKId, Promise promise){
        Integer consentValue = ot.getConsentStatusForSDKId(SDKId);
        promise.resolve(consentValue);
    }

    @ReactMethod
    public void shouldShowBanner(Promise promise){
        promise.resolve(ot.shouldShowBanner());
    }

    @ReactMethod
    public void listenForConsentChanges(String category){
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            getReactApplicationContext().registerReceiver(actionConsent, new IntentFilter(category), Context.RECEIVER_NOT_EXPORTED);
        } else {
            getReactApplicationContext().registerReceiver(actionConsent, new IntentFilter(category));
        }
    }

    @ReactMethod
    public void stopListeningForConsentChanges(){
        try{
            getReactApplicationContext().unregisterReceiver(actionConsent);
        } catch (Exception e) {
            Log.e(LOG_TAG, "Error when trying to unregister receiver. See StackTrace for more details.");
            e.printStackTrace();
        }
    }

    BroadcastReceiver actionConsent = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            getReactApplicationContext().getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
                    .emit(intent.getAction(),intent.getIntExtra(OTBroadcastServiceKeys.EVENT_STATUS,-1));
        }
    };

    @ReactMethod
    public void getOTConsentJSForWebView(Promise promise){
        promise.resolve(ot.getOTConsentJSForWebView());
    }

    @ReactMethod
    public void getDomainInfo(Promise promise){
        promise.resolve(ot.getDomainInfo().toString());
    }

    @ReactMethod
    public void getOTGoogleConsentModeData(Promise promise){
        OTGoogleConsentModeData otGoogleConsentModeData = ot.getOTGoogleConsentModeData();
        JSONObject googleConsentData = new JSONObject();
        if(otGoogleConsentModeData != null){
            try {
                googleConsentData.put("analytics_storage", otGoogleConsentModeData.getConsentType().getAnalyticsStorage());
                googleConsentData.put("ad_storage", otGoogleConsentModeData.getConsentType().getAdStorage());
                googleConsentData.put("ad_user_data", otGoogleConsentModeData.getConsentType().getAdUserData());
                googleConsentData.put("ad_personalization", otGoogleConsentModeData.getConsentType().getAdPersonalization());
                googleConsentData.put("functionality_storage", otGoogleConsentModeData.getConsentType().getFunctionalityStorage());
                googleConsentData.put("personalization_storage", otGoogleConsentModeData.getConsentType().getPersonalizationStorage());
                googleConsentData.put("security_storage", otGoogleConsentModeData.getConsentType().getSecurityStorage());
            } catch (Exception e) {
                
            }
        }
        promise.resolve(googleConsentData.toString());
    }

    @ReactMethod
    public void showConsentPurposesUI(){
        final Activity currentActivity = getCurrentActivity();
        if(currentActivity == null || !appIsActive()) { return; }
        currentActivity.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                ot.showConsentPurposesUI((AppCompatActivity) currentActivity);
            }
        });

    }

    @ReactMethod
    public void getUCPurposeConsent(String purposeId, Promise promise){
        int consent = ot.getUCPurposeConsent(purposeId);
        promise.resolve(consent);
    }

    @ReactMethod
    public void getUCCustomPreferenceConsent(String customPreferenceOptionId, String customPreferenceId, String purposeId, Promise promise){
        int consent = ot.getUCPurposeConsent(customPreferenceOptionId, customPreferenceId, purposeId);
        promise.resolve(consent);
    }

    @ReactMethod
    public void getUCTopicConsent(String topicId, String purposeId, Promise promise){
        int consent = ot.getUCPurposeConsent(topicId, purposeId);
        promise.resolve(consent);
    }

    @ReactMethod
    public void updateUCPurposeConsent(String purposeId, Boolean consent){
        ot.updateUCPurposeConsent(purposeId, consent);
    }

    @ReactMethod
    public void updateUCCustomPreferenceConsent(String customPreferenceOptionId, String customPreferenceId, String purposeId, Boolean consent){
        ot.updateUCPurposeConsent(customPreferenceOptionId, customPreferenceId, purposeId, consent);
    }

    @ReactMethod
    public void updateUCTopicConsent(String topicOptionId, String purposeId, Boolean consent){
        ot.updateUCPurposeConsent(topicOptionId, purposeId, consent);
    }

    @ReactMethod
    public void saveUCConsent(){
        ot.saveConsent(OTConsentInteractionType.UC_PC_CONFIRM);
    }

    @ReactMethod
    public void updatePurposeConsent(String categoryId, Boolean consentValue){
        ot.updatePurposeConsent(categoryId, consentValue);
    }

    @ReactMethod
    public void resetUpdatedConsent(){
                       ot.resetUpdatedConsent();
    }

    @ReactMethod
    public void clearOTSDKData(){
        Log.i(LOG_TAG, "Clear OT SDK data method called.");
        ot.clearOTSDKData();
    }

    @ReactMethod
    public void fetchBannerCmpApiData(final Promise promise) {
        ot.fetchBannerCmpApiData(new OTCallback() {
            @Override
            public void onSuccess(@NonNull OTResponse otSuccessResponse) {
                promise.resolve(otSuccessResponse.getResponseData());
            }

            @Override
            public void onFailure(@NonNull OTResponse otErrorResponse) {
                promise.reject("FETCH_ERROR", otErrorResponse.getResponseMessage());
            }
        });
    }

    @ReactMethod
    public void fetchPreferencesCmpApiData(final Promise promise) {
        ot.fetchPreferencesCmpApiData(new OTCallback() {
            @Override
            public void onSuccess(@NonNull OTResponse otSuccessResponse) {
                promise.resolve(otSuccessResponse.getResponseData());
            }

            @Override
            public void onFailure(@NonNull OTResponse otErrorResponse) {
                promise.reject("FETCH_ERROR", otErrorResponse.getResponseMessage());
            }
        });
    }

    @ReactMethod
    public void fetchVendorsCmpApiData(final Promise promise) {
        ot.fetchVendorsCmpApiData(new OTCallback() {
            @Override
            public void onSuccess(@NonNull OTResponse otSuccessResponse) {
                promise.resolve(otSuccessResponse.getResponseData());
            }

            @Override
            public void onFailure(@NonNull OTResponse otErrorResponse) {
                promise.reject("FETCH_ERROR", otErrorResponse.getResponseMessage());
            }
        });
    }

    @ReactMethod
    public void saveConsent(int consentType, Promise promise ){
        String interactionType;
        switch (consentType) {
            case 1:
                interactionType = OTConsentInteractionType.BANNER_ALLOW_ALL;
                break;
            case 2:
                interactionType = OTConsentInteractionType.BANNER_REJECT_ALL;
                break;
            case 3:
                interactionType = OTConsentInteractionType.BANNER_CLOSE;
                break;
            case 4:
                interactionType = OTConsentInteractionType.BANNER_CONTINUE_WITHOUT_ACCEPTING;
                break;
            case 5:
                interactionType = OTConsentInteractionType.PC_ALLOW_ALL;
                break;
            case 6:
                interactionType = OTConsentInteractionType.PC_REJECT_ALL;
                break;
            case 7:
                interactionType = OTConsentInteractionType.PC_CONFIRM;
                break;
            case 8:
                interactionType = OTConsentInteractionType.PC_CLOSE;
                break;
            default:
                promise.reject("Interaction type not recognized","Only pass in enum OTConsentInteractionType");
                return;
        }

        ot.saveConsent(interactionType);
        promise.resolve(null);

    }


    @ReactMethod
    public void enableOTSDKLog(int logType){
        int otLogType;
        switch (logType) {
            case 1:
                otLogType = OTLoggerConstant.NO_OT_SDK_LOG;
                break;
            case 2:
                otLogType = Log.VERBOSE;
                break;
            case 3:
                otLogType = Log.DEBUG;
                break;
            case 4:
                otLogType = Log.INFO;
                break;
            case 5:
                otLogType = Log.WARN;
                break;
            case 6:
                otLogType = Log.ERROR;
                break;
            default:
                return;
        }
        OTPublishersHeadlessSDK.enableOTSDKLog(otLogType);
    }

    @ReactMethod
    public void getCurrentActiveProfile(Promise promise){
        promise.resolve(ot.getOTCache().getDataSubjectIdentifier());
    }

    @ReactMethod
    public void renameProfile(String fromIdentifier, String toIdentifier, Promise promise) {
        ot.renameProfile(fromIdentifier, toIdentifier, new OTCallback() {
            @Override
            public void onSuccess(@NonNull OTResponse otResponse) {
                promise.resolve(true);
            }

            @Override
            public void onFailure(@NonNull OTResponse otResponse) {
                promise.reject("RENAME_PROFILE_ERROR", otResponse.getResponseMessage());
            }
        });
    }

    @ReactMethod
    public void addEventListener(){
        Activity activity = getCurrentActivity();
        if(activity == null){
            Log.e(LOG_TAG, "Activity is null, cannot add event listener");
            return;
        }
        
        OTEventListener eventListener = new OTEventListener() {
            @Override
            public void onShowBanner(OTUIDisplayReason otuiDisplayReason) {
                Log.i(LOG_TAG, "onShowBanner called.");
                Log.i(LOG_TAG, "Log banner shown reason : " + otuiDisplayReason.logReason());
                sendEvent("onShowBanner", otuiDisplayReason.logReason());
            }

            @Override
            public void onHideBanner() {
                Log.i(LOG_TAG, "onHideBanner called.");
                sendEvent("onHideBanner", null);
            }

            @Override
            public void onBannerClickedAcceptAll() {
                Log.i(LOG_TAG, "onBannerClickedAcceptAll called.");
                sendEvent("onBannerClickedAcceptAll", null);
            }

            @Override
            public void onBannerClickedRejectAll() {
                Log.i(LOG_TAG, "onBannerClickedRejectAll called.");
                sendEvent("onBannerClickedRejectAll", null);
            }

            @Override
            public void onShowPreferenceCenter(OTUIDisplayReason otuiDisplayReason) {
                Log.i(LOG_TAG, "onShowPreferenceCenter called.");
                Log.i(LOG_TAG, "Log PC shown reason : " + otuiDisplayReason.logReason());
                sendEvent("onShowPreferenceCenter", otuiDisplayReason.logReason());
            }

            @Override
            public void onHidePreferenceCenter() {
                Log.i(LOG_TAG, "onHidePreferenceCenter called.");
                sendEvent("onHidePreferenceCenter", null);
            }

            @Override
            public void onPreferenceCenterAcceptAll() {
                Log.i(LOG_TAG, "onPreferenceCenterAcceptAll called.");
                sendEvent("onPreferenceCenterAcceptAll", null);
            }

            @Override
            public void onPreferenceCenterRejectAll() {
                Log.i(LOG_TAG, "onPreferenceCenterRejectAll called.");
                sendEvent("onPreferenceCenterRejectAll", null);
            }

            @Override
            public void onPreferenceCenterConfirmChoices() {
                Log.i(LOG_TAG, "onPreferenceCenterConfirmChoices called.");
                sendEvent("onPreferenceCenterConfirmChoices", null);
            }

            @Override
            public void onShowVendorList() {
                Log.i(LOG_TAG, "onShowVendorList called.");
                sendEvent("onShowVendorList", null);
            }

            @Override
            public void onHideVendorList() {
                Log.i(LOG_TAG, "onHideVendorList called.");
                sendEvent("onHideVendorList", null);
            }

            @Override
            public void onVendorConfirmChoices() {
                Log.i(LOG_TAG, "onVendorConfirmChoices called.");
                sendEvent("onVendorConfirmChoices", null);
            }

            @Override
            public void allSDKViewsDismissed(String interactionType) {
                Log.i(LOG_TAG, "All SDK views dismissed called. Interaction type : " + interactionType);
                if (OTConsentInteractionType.BANNER_BACK.equalsIgnoreCase(interactionType)) {
                    Log.i(LOG_TAG, "Banner back button clicked," +
                            " inform the user app will close if consent not given.");
                }
                sendEvent("allSDKViewsDismissed", interactionType);
            }

            @Override
            public void onSDKNoAction(String interactionType) {
                Log.i(LOG_TAG, "On SDK NoAction called. Interaction type : " + interactionType);
                sendEvent("onSDKNoAction", interactionType);
            }

            @Override
            public void onVendorListVendorConsentChanged(String vendorId, int consentStatus) {
                Log.i(LOG_TAG, "onVendorListVendorConsentChanged called."
                        + " vendorId = " + vendorId + " consentStatus = " + consentStatus);
                WritableMap params = Arguments.createMap();
                params.putString("vendorId", vendorId);
                params.putInt("consentStatus", consentStatus);
                sendEvent("onVendorListVendorConsentChanged", params);
            }

            @Override
            public void onVendorListVendorLegitimateInterestChanged(String vendorId,
                                                                    int legitInterest) {
                Log.i(LOG_TAG, "onVendorListVendorLegitimateInterestChanged called."
                        + " vendorId = " + vendorId + " legitInterest = " + legitInterest);
                WritableMap params = Arguments.createMap();
                params.putString("vendorId", vendorId);
                params.putInt("legitInterest", legitInterest);
                sendEvent("onVendorListVendorLegitimateInterestChanged", params);
            }

            @Override
            public void onPreferenceCenterPurposeConsentChanged(String purposeId,
                                                                int consentStatus) {
                Log.i(LOG_TAG, "onPreferenceCenterPurposeConsentChanged called. " +
                        " PurposeID = " + purposeId + " consentStatus = " + consentStatus);
                WritableMap params = Arguments.createMap();
                params.putString("purposeId", purposeId);
                params.putInt("consentStatus", consentStatus);
                sendEvent("onPreferenceCenterPurposeConsentChanged", params);
            }

            @Override
            public void onPreferenceCenterPurposeLegitimateInterestChanged(String purposeId,
                                                                            int legitInterest) {
                Log.i(LOG_TAG, "onPreferenceCenterPurposeLegitimateInterestChanged called."
                        + " PurposeID = " + purposeId + " legitInterest = " + legitInterest);
                WritableMap params = Arguments.createMap();
                params.putString("purposeId", purposeId);
                params.putInt("legitInterest", legitInterest);
                sendEvent("onPreferenceCenterPurposeLegitimateInterestChanged", params);
            }

            @Override
            public void onVendorListVendorConsentChanged(String vendorListMode, String vendorId,
                                                        int consentStatus) {
                Log.i(LOG_TAG,
                    "onVendorListVendorConsentChanged called."+ "vendorList type = "+vendorListMode+" " +
                            " vendorId = "+ vendorId +
                            " consentStatus = " + consentStatus);
                WritableMap params = Arguments.createMap();
                params.putString("vendorListMode", vendorListMode);
                params.putString("vendorId", vendorId);
                params.putInt("consentStatus", consentStatus);
                sendEvent("onVendorListVendorConsentChanged", params);
            }
        };
        ot.addEventListener(eventListener);
    }

    // Helper method to send events to JavaScript
    private void sendEvent(String eventName, Object params) {
        getReactApplicationContext()
            .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
            .emit(eventName, params);
    }
}