package com.contentsquare.rn.utils;

import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.WebView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;

import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.UIManager;
import com.facebook.react.uimanager.IllegalViewOperationException;
import com.facebook.react.uimanager.UIManagerHelper;
import com.facebook.react.uimanager.common.UIManagerType;

/**
 * Find the native instance of a React Native view.
 */
public class ReactNativeViewFinder {

    @NonNull
    private final ReactNativeUiThreadUtil mReactNativeUiThreadUtil;

    public ReactNativeViewFinder(@NonNull ReactNativeUiThreadUtil reactNativeUiThreadUtil) {
        this.mReactNativeUiThreadUtil = reactNativeUiThreadUtil;
    }

    /**
     * A listener interface used while looking for a webview.
     */
    public interface OnWebViewFoundListener {

        /**
         * Triggered when a webview has been found.
         *
         * @param webView found
         */
        void onWebViewFound(@NonNull WebView webView);
    }

    public interface OnViewFoundListener {

        /**
         * Triggered when a view has been found.
         *
         * @param view found
         */
        void onViewFound(@NonNull View view);
    }

    /**
     * Find a React Native View from its React Tag.
     * Must be called from the UI thread
     *
     * @param reactContext        React Native context.
     * @param viewTag             the view's react tag.
     * @param onViewFoundListener listener used to communicate back the view
     *                            found
     */
    public void findView(@NonNull final ReactApplicationContext reactContext, final int viewTag,
            @NonNull final OnViewFoundListener onViewFoundListener) {
        if (mReactNativeUiThreadUtil.isOnUiThread()) {
            View foundView = resolveView(reactContext, viewTag);

            if (foundView != null) {
                onViewFoundListener.onViewFound(foundView);
            } else {
                Log.w("CSLIB", "could not resolve view with tag @" + viewTag);
            }
        } else {
            Log.w("CSLIB", "findView should be run from the UI Thread.");
        }
    }

    /**
     * Find a React Native WebView by its React Tag.
     * Must be called from the UI thread
     *
     * @param reactContext           React Native context.
     * @param webViewTag             the webView's react tag.
     * @param onWebViewFoundListener listener used to communicate back the webview
     *                               found
     */
    public void findWebView(@NonNull final ReactApplicationContext reactContext, final int webViewTag,
            @NonNull final OnWebViewFoundListener onWebViewFoundListener) {
        if (mReactNativeUiThreadUtil.isOnUiThread()) {
            View foundView = resolveView(reactContext, webViewTag);
            if (foundView instanceof WebView) {
                onWebViewFoundListener.onWebViewFound((WebView) foundView);
            } else if (foundView instanceof ViewGroup) {
                WebView webView = findWebViewInChildren((ViewGroup) foundView);
                if (webView != null) {
                    onWebViewFoundListener.onWebViewFound(webView);
                } else {
                    Log.w("CSLIB", "Could not resolve WebView in children.");
                }
            } else {
                Log.w("CSLIB", "Resolved view is not a WebView.");
            }
        } else {
            Log.w("CSLIB", "findWebView should be run from the UI Thread.");
        }
    }

    @Nullable
    private View resolveView(@NonNull final ReactApplicationContext reactContext, final int viewTag) {
        try {
            UIManager uiManager = UIManagerHelper.getUIManager(reactContext, UIManagerType.DEFAULT);
            if (uiManager != null) {
                View foundView = uiManager.resolveView(viewTag);
                Log.d("CSLIB", "UIManager resolved view with tag @" + viewTag);
                return foundView;
            }
        } catch (IllegalViewOperationException | NullPointerException e) {
            Log.w("CSLIB", "UIManager could not resolve view with tag @" + viewTag);
        }
        return null;
    }

    @Nullable
    @VisibleForTesting
    protected WebView findWebViewInChildren(@NonNull final ViewGroup parent) {
        for (int i = 0; i < parent.getChildCount(); i++) {
            final View child = parent.getChildAt(i);
            if (child instanceof WebView) {
                return (WebView) child;
            } else if (child instanceof ViewGroup && ((ViewGroup) child).getChildCount() > 0) {
                WebView foundWebView = findWebViewInChildren((ViewGroup) child);
                if (foundWebView != null) {
                    return foundWebView;
                }
            }
        }
        return null;
    }
}