package com.contentsquare.rn.externalbridge;

import android.util.Log;

import androidx.annotation.Nullable;

import com.contentsquare.android.api.bridge.xpf.ExternalBridgeInterface;
import com.contentsquare.android.api.bridge.xpf.XpfInterface;
import com.contentsquare.android.api.model.CustomVar;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.List;

public interface XpfInterfaceBridge {

    /**
     * Register an external bridge.
     *
     * @param externalBridge the external bridge to register.
     */
    void registerExternalBridge(ExternalBridgeInterface externalBridge);

    /**
     * Unregister an external bridge.
     *
     * @param externalBridge the external bridge to unregister.
     */
    void unregisterExternalBridge(ExternalBridgeInterface externalBridge);

    /**
     * Track a screen view with custom variables and source info.
     *
     * @param screenName the name of the screen.
     * @param customVars the custom variables.
     * @param sourceName the source name (nullable).
     * @param sourceVersion the source version (nullable).
     * @param sourcePlatform the source platform (nullable).
     */
    void trackScreenView(
            String screenName,
            List<CustomVar> customVars,
            @Nullable String sourceName,
            @Nullable String sourceVersion,
            @Nullable String sourcePlatform
    );

    class Factory {

        /**
         * Creates a new instance of the default implementation of [XpfInterfaceBridge].
         */
        public static XpfInterfaceBridge defaultInstance() {
            return new XpfInterfaceBridgeImpl();
        }
    }
}

/**
 * Channel of method in the contentsquare sdk interfaces for xpf.
 */
enum XpfInterfaceChannel {
    /** channel/method called to register an external bridge */
    REGISTER_EXTERNAL_BRIDGE("registerExternalBridge"),

    /** channel/method called to unregister an external bridge */
    UNREGISTER_EXTERNAL_BRIDGE("unregisterExternalBridge"),

    /** channel/method called to track a screen view */
    TRACK_SCREEN_VIEW("trackScreenView");

    private final String value;

    XpfInterfaceChannel(String value) {
        this.value = value;
    }

    public String getValue() {
        return value;
    }
}

/**
 * Implementation of [XpfInterfaceBridge].
 *
 * @see XpfInterfaceBridge
 */
class XpfInterfaceBridgeImpl implements XpfInterfaceBridge {
    private final String objectInstanceName = "INSTANCE";
    private static final String HEAP_SOURCE_INFO_CLASS = "com.contentsquare.android.core.communication.heap.HeapInterface$HeapSourceInfo";

    @Override
    public void registerExternalBridge(ExternalBridgeInterface externalBridge) {
        Class<XpfInterface> xpfInterface = XpfInterface.class;
        try {
            Object instance = xpfInterface.getDeclaredField(objectInstanceName).get(null);
            Method method = xpfInterface.getDeclaredMethod(
                    XpfInterfaceChannel.REGISTER_EXTERNAL_BRIDGE.getValue(),
                    ExternalBridgeInterface.class);
            // Ensure that the method we are trying to invoke is public
            method.setAccessible(true);
            method.invoke(instance, externalBridge);
        } catch (Exception e) {
            Log.e("CSLIB", "Error invoking method", e);
        }
    }

    @Override
    public void unregisterExternalBridge(ExternalBridgeInterface externalBridge) {
        Class<XpfInterface> xpfInterface = XpfInterface.class;
        try {
            Object instance = xpfInterface.getDeclaredField(objectInstanceName).get(null);
            Method method = xpfInterface.getDeclaredMethod(
                    XpfInterfaceChannel.UNREGISTER_EXTERNAL_BRIDGE.getValue(),
                    ExternalBridgeInterface.class);
            method.setAccessible(true);
            method.invoke(instance, externalBridge);
        } catch (Exception e) {
            Log.e("CSLIB", "Error invoking method", e);
        }
    }

    @Override
    public void trackScreenView(
            String screenName,
            List<CustomVar> customVars,
            @Nullable String sourceName,
            @Nullable String sourceVersion,
            @Nullable String sourcePlatform
    ) {
        Class<XpfInterface> xpfInterface = XpfInterface.class;
        try {
            Object instance = xpfInterface.getDeclaredField(objectInstanceName).get(null);

            // Create HeapSourceInfo via reflection
            Object heapSourceInfo = null;
            if (sourceName != null && sourceVersion != null && sourcePlatform != null) {
                Class<?> heapSourceInfoClass = Class.forName(HEAP_SOURCE_INFO_CLASS);
                Constructor<?> constructor = heapSourceInfoClass.getConstructor(
                        String.class, String.class, String.class);
                heapSourceInfo = constructor.newInstance(sourceName, sourceVersion, sourcePlatform);
            }

            // Get HeapSourceInfo class for method signature
            Class<?> heapSourceInfoClass = Class.forName(HEAP_SOURCE_INFO_CLASS);
            Method method = xpfInterface.getDeclaredMethod(
                    XpfInterfaceChannel.TRACK_SCREEN_VIEW.getValue(),
                    String.class,
                    List.class,
                    heapSourceInfoClass);
            method.setAccessible(true);
            method.invoke(instance, screenName, customVars, heapSourceInfo);
        } catch (Exception e) {
            Log.e("CSLIB", "Error invoking trackScreenView method", e);
        }
    }
}