package com.contentsquare.plugins.capacitor;

import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.webkit.WebView;

import androidx.annotation.NonNull;

import com.contentsquare.android.Contentsquare;
import com.contentsquare.android.api.CsWebViewManager;
import com.contentsquare.android.api.bridge.xpf.ExternalBridgeInterface;
import com.contentsquare.android.api.bridge.xpf.ExternalBridgeType;
import com.contentsquare.android.api.bridge.xpf.SDKState;
import com.contentsquare.android.api.bridge.xpf.SDKStateChangeType;
import com.contentsquare.android.api.bridge.xpf.XpfInterface;
import com.contentsquare.android.api.model.Transaction;
import com.getcapacitor.JSObject;
import com.getcapacitor.Logger;
import com.getcapacitor.Plugin;
import com.getcapacitor.PluginCall;
import com.getcapacitor.PluginMethod;
import com.getcapacitor.annotation.CapacitorPlugin;

import java.lang.reflect.Method;
/**
 * The main Capacitor plugin class.
 * The plugin methods will be called by the javascript bridge created by the Capacitor framework
 */
@CapacitorPlugin(name = "ContentsquareCAP")
public class ContentsquareCAPPlugin extends Plugin {

    private ContentsquareCAPTelemetry telemetry;
    private ExternalBridgeInterface xpfBridge;
    private ContentSquareCAPJSInjector jsInjector;

    /**
     * Called by Capacitor framework by the initialization lifecycle's method
     */
    @Override
    public void load() {
        try {
            WebView cWebview = getBridge().getWebView();
            Contentsquare.unMask(cWebview);
            initJSInjector();

            xpfBridge = new ExternalBridgeInterface() {

                @Override
                public void notifyShouldSendInitialInsertion() {
                    // Do nothing
                }

                @Override
                public void updateBridgeConfig(@NonNull String s) {
                    // Do nothing
                }

                @Override
                public void notifySrMaskingHasChanged(boolean b) {
                    // Do nothing
                }

                @Override
                public void notifySDKStateChanges(@NonNull SDKStateChangeType sdkStateChangeType, @NonNull SDKState sdkState) {
                    // Do nothing
                }

                @Override
                public void notifyCsInAppEnabled(boolean b) {
                    // Do nothing
                }

                @Override
                public void notifySessionReplayEnabled(boolean b) {
                    // Do nothing
                }

                @NonNull
                @Override
                public ExternalBridgeType getBridgeType() {
                    return null;
                }

                @Override
                public void notifySrMaskingIndicatorHasChanged(boolean value) {
                    // Do nothing
                }

            };

            cWebview.post(() -> {
                CsWebViewManager.INSTANCE.injectEventTrackingInterface(cWebview);
                cWebview.reload();
                Log.d("CSLIBCAP", "WebView injected !Register Xpf Bridge");
                registerXpfBridge();
            });

            initTelemetry();
            telemetry.setXPFType();

        } catch (Exception e) {
            Log.e("CSLIBCAP", e.getMessage());
        }

    }

    /**
     * Send Screen name
     *
     * @param call call payload
     */
    @PluginMethod(returnType = PluginMethod.RETURN_PROMISE)
    public void sendScreenName(PluginCall call) {
        try {
            String name = call.getString("name");
            String js = "window._uxa = window._uxa || [];\n" + "window._uxa.push([\"trackPageview\", \"" + name + "\"]);\n";

            jsInjector.addToJSQueue("TrackPageview: " + name, js, false);
            JSObject ret = new JSObject();
            ret.put("result", "sendScreenName " + name);
            call.resolve(ret);
        } catch (Exception e) {
            Log.e("sendScreenName ", e.getMessage());
            call.reject("sendScreenName Failed ", e);
        }
    }

    /**
     * Opt in
     *
     * @param call call payload
     */
    @PluginMethod(returnType = PluginMethod.RETURN_NONE)
    public void optIn(PluginCall call) {
        Handler handler = new Handler(Looper.getMainLooper());
        handler.post(() -> Contentsquare.optIn(getContext()));
    }

    /**
     * Opt out
     *
     * @param call call payload
     */
    @PluginMethod(returnType = PluginMethod.RETURN_NONE)
    public void optOut(PluginCall call) {
        Handler handler = new Handler(Looper.getMainLooper());
        handler.post(() -> Contentsquare.optOut(getContext()));
    }

    /**
     * Send a transaction
     *
     * @param call call payload
     */
    @PluginMethod(returnType = PluginMethod.RETURN_PROMISE)
    public void sendTransaction(PluginCall call) {
        try {
            JSObject data = call.getData();
            Float price = call.getFloat("transactionValue");
            String currency = call.getString("transactionCurrency");
            String id = call.getString("transactionId");
            if (id == null || id.isEmpty()) {
                Contentsquare.send(Transaction.Companion.builder(price,currency).build());
            } else {
                Contentsquare.send(Transaction.Companion.builder(price, currency).id(id).build());
            }
            JSObject ret = new JSObject();
            ret.put("result", "sendTransaction " + data);
            call.resolve(ret);

        } catch (Exception e) {
            Log.e("sendTransaction ", e.getMessage());
            call.reject("sendTransaction Failed ", e);
        }

    }

    /**
     * Send Dynamic var with int values
     *
     * @param call call payload
     */
    @PluginMethod(returnType = PluginMethod.RETURN_PROMISE)
    public void sendDynamicVarWithIntValue(PluginCall call) {
        try {
            String dynVarKey = call.getString("dynVarKey");
            Integer dynVarValue = call.getInt("dynVarValue", 0);
            Contentsquare.send(dynVarKey, dynVarValue);

            JSObject ret = new JSObject();
            ret.put("result", "sendDynamicVarWithIntValue " + call.getData());
            call.resolve(ret);
        } catch (Exception e) {
            Log.e("dynVarWithIntValue ", e.getMessage());
            call.reject("sendDynamicVarWithIntValue Failed ", e);
        }
    }

    /**
     * Send Dynamic var with string values
     *
     * @param call call payload
     */
    @PluginMethod(returnType = PluginMethod.RETURN_PROMISE)
    public void sendDynamicVarWithStringValue(PluginCall call) {
        try {
            String dynVarKey = call.getString("dynVarKey");
            String dynVarValue = call.getString("dynVarValue", "");
            Contentsquare.send(dynVarKey, dynVarValue);

            JSObject ret = new JSObject();
            ret.put("result", "sendDynamicVarWithStringValue " + call.getData());
            call.resolve(ret);
        } catch (Exception e) {
            Log.e("dynVarWithStringValue ", e.getMessage());
            call.reject("sendDynamicVarWithStringValue Failed ", e);
        }
    }

    /**
     * Has to be called by the frontend when the DOM is ready
     *
     * @param call call payload
     */
    @PluginMethod(returnType = PluginMethod.RETURN_NONE)
    public void onReady(PluginCall call) {
        Logger.debug("CSLIBCAP", "onReady() DOM is ready");
    }

    /**
     * SR Masking - Capture elements in a masked URL
     *
     * @param call call payload
     */
    @PluginMethod(returnType = PluginMethod.RETURN_NONE)
    public void setCapturedElementsSelector(PluginCall call) {
        String elements = call.getString("elements");
        String js = "window._uxa = window._uxa || [];\n" + "window._uxa.push([\"setCapturedElementsSelector\", \"" + elements + "\"]);\n";
        jsInjector.addToJSQueue("setCapturedElementsSelector: " + elements, js, false);
    }

    /**
     * SR Masking - Exclude an URL
     *
     * @param call call payload
     */
    @PluginMethod(returnType = PluginMethod.RETURN_NONE)
    public void excludeURLForReplay(PluginCall call) {
        String urlRegExPattern = call.getString("url");
        String js = "window._uxa = window._uxa || [];\n" + "window._uxa.push([\"excludeURLforReplay\", " + urlRegExPattern + "]);\n";
        jsInjector.addToJSQueue("excludeURLForReplay: " + urlRegExPattern, js, false);
    }

    /**
     * SR Masking - Remove content/Personal Data from the collected HTML
     *
     * @param call call payload
     */
    @PluginMethod(returnType = PluginMethod.RETURN_NONE)
    public void setPIISelectors(PluginCall call) {
        JSObject data = call.getData();
        String js = "window._uxa = window._uxa || [];\n" + "window._uxa.push([\"setPIISelectors\", " + data.toString() + "]);\n";
        jsInjector.addToJSQueue("setPIISelectors: " + data, js, false);
    }


    @PluginMethod(returnType = PluginMethod.RETURN_NONE)
    public void collect(PluginCall call) {
        try {
            String name = call.getString("name");
            String value = call.getString("value", "");
            telemetry.collect(name, value);
            call.resolve();
        } catch (Exception e) {
            Log.e("Telemetry collect ", e.getMessage());
            call.reject("Telemetry collect Failed ", e);
        }
    }

    private void initTelemetry() throws Exception {
        telemetry = new ContentsquareCAPTelemetry();
    }

    private void initJSInjector() {
         jsInjector = new ContentSquareCAPJSInjector(getBridge().getWebView());
    }

    private void registerXpfBridge() {
        try {
            Method registerMethod = XpfInterface.class.getDeclaredMethod("registerExternalBridge", ExternalBridgeInterface.class);
            registerMethod.setAccessible(true);
            Object xpfInterfaceInstance = XpfInterface.class.getDeclaredField("INSTANCE").get(null);
            registerMethod.invoke(xpfInterfaceInstance, xpfBridge);
        } catch (Exception exception) {
            Log.e("CSLIBCAP", "registerXpfBridge: " + exception.getMessage());

        }
    }

    private void unregisterXpfBridge() {
        try {
            Method unregisterMethod = XpfInterface.class.getDeclaredMethod("unregisterExternalBridge", ExternalBridgeInterface.class);
            unregisterMethod.setAccessible(true);
            Object xpfInterfaceInstance = XpfInterface.class.getDeclaredField("INSTANCE").get(null);
            unregisterMethod.invoke(xpfInterfaceInstance, xpfBridge);
        } catch (Exception exception) {
            Log.e("CSLIBCAP", "unregisterXpfBridge: " + exception.getMessage());
        }
    }

}
