package com.tencent.qian.webview;

import android.app.Activity;
import android.os.Build;
import android.view.ViewGroup;
import android.webkit.RenderProcessGoneDetail;
import android.webkit.WebView;
import android.webkit.WebViewClient;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.core.util.Consumer;

import com.tencent.qian.webview.chrome.QianWebChromeClient;
import com.tencent.qian.webview.prewarm.WebViewPool;
import com.tencent.qian.webview.client.QianWebViewClient;
import com.tencent.qian.webview.filechooser.FileChooserLauncher;
import com.tencent.qian.webview.internal.DownloadListenerInstaller;
import com.tencent.qian.webview.internal.WebSettingsApplier;
import com.tencent.qian.webview.model.SignCallback;
import com.tencent.qian.webview.model.TencentQianWebViewConfig;
import com.tencent.qian.webview.permission.RuntimePermissionRequester;
import com.tencent.qian.webview.scheme.ExternalSchemeHandler;
import com.tencent.qian.webview.scheme.SchemeInterceptor;

/**
 * <b>配置模式</b>：让客户已有 WebView 获得 SDK 全部能力，无需替换容器。
 */
public final class TencentQianWebViewConfigurator {

    private TencentQianWebViewConfigurator() {}

    /**
     * 在 webView 上套用 SDK 全部能力。
     */
    @NonNull
    public static Handle apply(
            @NonNull Activity activity,
            @NonNull WebView webView,
            @NonNull TencentQianWebViewConfig config,
            @Nullable SignCallback callback,
            @Nullable Consumer<Integer> progressListener
    ) {
        return apply(activity, webView, config, callback, progressListener, null, null);
    }

    /**
     * 在 webView 上套用 SDK 全部能力（可复用外部预注册的权限申请器 / 文件选择器）。
     *
     * @param permissionRequester 可选。传入<b>已在 Activity {@code onCreate} 前注册</b>的实例则复用,
     *                            不再内部注册; 为 {@code null}（配置模式客户用法）时内部调
     *                            {@link RuntimePermissionRequester#register}。
     *                            <b>崩溃重建等 Activity 已 STARTED/RESUMED 后二次 apply 的场景必须传入,</b>
     *                            否则内部 register 会抛 {@code IllegalStateException}
     *                            （"must call register before STARTED"）。
     * @param fileChooserLauncher 可选。语义同 {@code permissionRequester}; 为 {@code null} 时内部注册。
     */
    @NonNull
    public static Handle apply(
            @NonNull Activity activity,
            @NonNull WebView webView,
            @NonNull TencentQianWebViewConfig config,
            @Nullable SignCallback callback,
            @Nullable Consumer<Integer> progressListener,
            @Nullable RuntimePermissionRequester permissionRequester,
            @Nullable FileChooserLauncher fileChooserLauncher
    ) {
        if (!(activity instanceof androidx.activity.ComponentActivity)) {
            throw new IllegalArgumentException(
                    "TencentQianWebViewConfigurator.apply requires an androidx.activity.ComponentActivity " +
                            "(e.g. AppCompatActivity or FragmentActivity); got " + activity.getClass().getName());
        }
        androidx.activity.ComponentActivity componentActivity = (androidx.activity.ComponentActivity) activity;

        // 卸载之前的 SDK 注入（幂等）
        detachIfTagged(webView);

        // 1. WebSettings
        WebSettingsApplier.apply(webView, config);

        // 2. 回调 holder
        CallbackHolder holder = new CallbackHolder(callback);

        // 3. 拦截器
        SchemeInterceptor schemeInterceptor = new SchemeInterceptor(holder::get);
        ExternalSchemeHandler externalSchemeHandler = new ExternalSchemeHandler(config.getExternalSchemes(), holder::get);

        // 4. ChromeClient 依赖
        // registerForActivityResult 必须在 Activity STARTED 之前注册; 崩溃重建时 Activity 已 RESUMED,
        // 只能复用调用方在 onCreate 期间预注册好的实例, 不能在此内部再 register(否则抛 IllegalStateException)。
        RuntimePermissionRequester resolvedPermissionRequester = permissionRequester != null
                ? permissionRequester
                : RuntimePermissionRequester.register(componentActivity);
        FileChooserLauncher resolvedFileChooserLauncher = fileChooserLauncher != null
                ? fileChooserLauncher
                : FileChooserLauncher.register(componentActivity, resolvedPermissionRequester, () -> activity);

        QianWebChromeClient chromeClient = new QianWebChromeClient(
                config, resolvedPermissionRequester, resolvedFileChooserLauncher,
                holder::get, () -> activity, progressListener);

        QianWebViewClient webViewClient = new QianWebViewClient(
                schemeInterceptor, externalSchemeHandler, holder::get, () -> activity);

        webView.setWebChromeClient(chromeClient);
        webView.setWebViewClient(webViewClient);

        // 5. 下载监听
        DownloadListenerInstaller.install(webView, activity, resolvedPermissionRequester, () -> activity, holder::get, config);

        // 6. 标记
        webView.setTag(R.id.qian_sdk_injected, holder);

        return new Handle(webView, holder);
    }

    /**
     * 简化重载（无 progressListener）。
     */
    @NonNull
    public static Handle apply(
            @NonNull Activity activity,
            @NonNull WebView webView,
            @NonNull TencentQianWebViewConfig config,
            @Nullable SignCallback callback
    ) {
        return apply(activity, webView, config, callback, null);
    }

    /**
     * SDK 注入句柄。
     */
    public static class Handle {
        private final WebView webView;
        private final CallbackHolder holder;

        Handle(@NonNull WebView webView, @NonNull CallbackHolder holder) {
            this.webView = webView;
            this.holder = holder;
        }

        /**
         * 解绑 SDK 注入的 client/listener/callback，但不销毁 WebView。
         */
        public void detach() {
            holder.clear();
            try {
                webView.setWebChromeClient(null);
                webView.setWebViewClient(QianFallbackWebViewClient.INSTANCE);
                webView.setDownloadListener(null);
                webView.setTag(R.id.qian_sdk_injected, null);
            } catch (Throwable ignored) {}
        }

        /**
         * 解绑 SDK 并销毁 WebView，释放全部资源。
         */
        public void detachAndDestroy() {
            detach();
            try {
                if (webView.getParent() instanceof ViewGroup) {
                    ((ViewGroup) webView.getParent()).removeView(webView);
                }
                webView.stopLoading();
                webView.loadUrl("about:blank");
                webView.destroy();
            } catch (Throwable ignored) {}
        }
    }

    /**
     * 回调持有器（volatile 保证线程安全）。
     */
    public static class CallbackHolder {
        @Nullable
        private volatile SignCallback ref;

        public CallbackHolder(@Nullable SignCallback initial) { this.ref = initial; }

        @Nullable
        public SignCallback get() { return ref; }
        public void set(@Nullable SignCallback callback) { ref = callback; }
        public void clear() { ref = null; }
    }

    /**
     * detach 后的兜底 client（复用实例归还态 / 客户 detach 后继续使用的实例）。
     *
     * <p><b>关键</b>：必须处理 {@link WebViewClient#onRenderProcessGone} 返回 {@code true}——否则走系统默认
     * {@code false}，归还态池实例的渲染进程一旦被系统回收，会把整个进程的 WebView 子系统拖垮
     * （退出重进、release 都救不回，唯有杀进程），表现为持久白屏。
     * 同时作废可能命中的池实例，避免僵尸实例被后续 open 复用。
     */
    private static final class QianFallbackWebViewClient extends WebViewClient {
        private static final QianFallbackWebViewClient INSTANCE = new QianFallbackWebViewClient();

        @RequiresApi(Build.VERSION_CODES.O)
        @Override
        public boolean onRenderProcessGone(WebView view, RenderProcessGoneDetail detail) {
            WebViewPool.invalidateIfPooled(view);
            return true;
        }
    }

    private static void detachIfTagged(@NonNull WebView webView) {
        Object prev = webView.getTag(R.id.qian_sdk_injected);
        if (prev instanceof CallbackHolder) {
            ((CallbackHolder) prev).clear();
            webView.setTag(R.id.qian_sdk_injected, null);
        }
    }
}
