package com.tencent.qian.webview.prewarm;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.MutableContextWrapper;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.view.ViewGroup;
import android.webkit.RenderProcessGoneDetail;
import android.webkit.WebView;
import android.webkit.WebViewClient;

import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;

import com.tencent.qian.webview.internal.WebSettingsApplier;

/**
 * WebView 复用池（容量 = 1，<b>借还池</b>，与鸿蒙规格一致，与 Kotlin 版 1:1）。
 *
 * <p>关键技术：池实例用 {@link MutableContextWrapper} 承载——预热阶段以 ApplicationContext 创建
 * （避免 Activity 泄漏），{@link #obtain} 时把 baseContext 换成真实 Activity（恢复文件选择 / JS 弹窗 /
 * 权限申请等能力），{@link #recycle} 归还时再换回 ApplicationContext（避免归还后仍持有 Activity 泄漏）。
 *
 * <p><b>生命周期（借还池）</b>：LIGHT {@link #preCreate} 离屏预建空实例；HEAVY {@link #warm} 同一实例
 * 加载 warmupUrl；{@code open()} → {@link #obtain} 借出（UA 配置匹配才复用）；关闭 → {@link #recycle}
 * （about:blank + clearHistory + rebind appContext，<b>不销毁</b>）；客户主动 {@link #release} 才销毁。
 *
 * <p><b>UA 门控</b>：预热按 PrewarmConfig 的 injectUserAgent/userAgentExtra 建实例并热身 warmupUrl；
 * {@link #obtain} 仅在与 {@code open()} config UA 一致时才返回实例，否则 {@code null}（容器自建）。
 * 所有 WebView 操作均在主线程执行。
 */
public final class WebViewPool {

    private static final String TAG = "QianWebViewPool";

    private static WebView pooled = null;
    @Nullable
    private static Context appContextRef = null;
    private static String warmedForBizType = null;
    private static String warmedUrl = "";
    /** 是否已被容器借出（借出中不可再 obtain）。 */
    private static boolean inUse = false;
    /** 预热时的 UA 签名，供复用门控比对。 */
    private static String uaSig = "";
    private static volatile boolean enabled = true;

    private static final Handler MAIN = new Handler(Looper.getMainLooper());

    private WebViewPool() {}

    public static void setEnabled(boolean value) {
        enabled = value;
        if (!value) {
            release();
        }
    }

    /** LIGHT：离屏预建空实例（省创建时间 + 触发内核加载），按 UA 配置建。 */
    public static void preCreate(Context appContext, boolean injectUA, String uaExtra) {
        if (!enabled) {
            return;
        }
        runOnMain(() -> ensureInstance(appContext, injectUA, uaExtra));
    }

    /** HEAVY：确保实例存在（按 UA 配置建）并加载 warmup.html（L4 网络预热）。 */
    @SuppressLint("SetJavaScriptEnabled")
    public static void warm(Context appContext, String warmupUrl, String bizType,
                            boolean injectUA, String uaExtra) {
        if (!enabled || warmupUrl == null || warmupUrl.isEmpty()) {
            return;
        }
        runOnMain(() -> {
            WebView wv = ensureInstance(appContext, injectUA, uaExtra);
            if (wv == null) {
                return;
            }
            // 借出中不切换 URL（避免打断正在使用的页面）
            if (inUse) {
                return;
            }
            if (!warmupUrl.equals(warmedUrl)) {
                try {
                    wv.loadUrl(warmupUrl);
                } catch (Throwable ignored) {
                }
            }
            warmedForBizType = bizType;
            warmedUrl = warmupUrl;
        });
    }

    /**
     * 借出池中预热态实例供 Activity 使用（借还池）。会把 baseContext 换成传入的 Activity；
     * 未就绪 / 借出中 / UA 配置不匹配时返回 {@code null}（容器自建）。<b>必须在主线程调用</b>。
     */
    @Nullable
    public static WebView obtain(Context activity, boolean injectUA, String uaExtra) {
        if (!enabled || inUse || pooled == null) {
            return null;
        }
        if (!uaSig.equals(uaSignatureOf(injectUA, uaExtra))) {
            return null;
        }
        try {
            Context base = pooled.getContext();
            if (base instanceof MutableContextWrapper) {
                ((MutableContextWrapper) base).setBaseContext(activity);
                inUse = true;
                return pooled;
            }
            return null;
        } catch (Throwable t) {
            return null;
        }
    }

    /**
     * 归还（借还池，<b>不销毁</b>）：从父容器移除 + about:blank + clearHistory，并 rebind baseContext
     * 回 appContext（避免归还后仍持有 Activity 泄漏），保留实例待下次复用。<b>必须在主线程调用</b>。
     */
    public static void recycle(WebView wv) {
        runOnMain(() -> {
            try {
                if (wv.getParent() instanceof ViewGroup) {
                    ((ViewGroup) wv.getParent()).removeView(wv);
                }
                wv.stopLoading();
                wv.loadUrl("about:blank");
                wv.clearHistory();
                Context base = wv.getContext();
                if (base instanceof MutableContextWrapper && appContextRef != null) {
                    ((MutableContextWrapper) base).setBaseContext(appContextRef);
                }
            } catch (Throwable ignored) {
            }
            warmedUrl = "about:blank";
            warmedForBizType = null;
            inUse = false;
        });
    }

    /** 客户主动释放池实例（销毁）。签署流程结束、不再需要时调用。 */
    public static void release() {
        runOnMain(WebViewPool::destroyPooledLocked);
    }

    /**
     * 内存压力回收：<b>仅在实例未被借出（{@code !inUse}）且存在时</b>才销毁，避免打断正在使用的签署容器。
     *
     * <p>借出中（{@code inUse == true}）则跳过——等容器关闭 {@link #recycle} 归还回空闲态后，
     * 下次内存告警再回收。
     *
     * @param onReleased 真正销毁池实例后在主线程回调（供上层重置去重状态等，使后续 prewarm 可重新预热）；
     *                   未销毁（借出中 / 无实例）时不回调，可为 {@code null}。
     */
    public static void releaseIfIdle(@Nullable Runnable onReleased) {
        runOnMain(() -> {
            if (!inUse && pooled != null) {
                destroyPooledLocked();
                if (onReleased != null) {
                    onReleased.run();
                }
            }
        });
    }

    /**
     * 作废池实例（渲染进程崩溃等场景）：仅当传入实例正是当前池实例时才销毁并置空，
     * 避免"渲染进程已死的僵尸实例"被后续 {@link #obtain} 反复借出导致持久白屏。
     *
     * <p>借出态 / 归还态 / 预热态均适用（三态的实例都是同一个 {@link #pooled} 引用）。
     * <b>必须在主线程调用</b>（内部 {@link #runOnMain} 兜底）。
     */
    public static void invalidateIfPooled(@Nullable WebView view) {
        runOnMain(() -> {
            if (view != null && view == pooled) {
                destroyPooledLocked();
            }
        });
    }

    /** 销毁池实例并清空全部池状态（主线程内调用）。 */
    private static void destroyPooledLocked() {
        try {
            if (pooled != null) {
                if (pooled.getParent() instanceof ViewGroup) {
                    ((ViewGroup) pooled.getParent()).removeView(pooled);
                }
                pooled.stopLoading();
                pooled.loadUrl("about:blank");
                pooled.destroy();
            }
        } catch (Throwable ignored) {
        }
        pooled = null;
        warmedForBizType = null;
        warmedUrl = "";
        uaSig = "";
        inUse = false;
    }

    @SuppressLint("SetJavaScriptEnabled")
    @Nullable
    private static WebView ensureInstance(Context appContext, boolean injectUA, String uaExtra) {
        if (pooled == null) {
            try {
                Context app = appContext.getApplicationContext();
                appContextRef = app;
                WebView wv = new WebView(new MutableContextWrapper(app));
                wv.getSettings().setJavaScriptEnabled(true);
                wv.getSettings().setDomStorageEnabled(true);
                // 预热态守卫：渲染进程崩溃时作废池实例（避免僵尸实例被后续 obtain 复用），
                // 并返回 true 阻止系统杀进程。借出态会被业务 client 覆盖，归还态被 fallback 覆盖，
                // 三态均处理 onRenderProcessGone。
                wv.setWebViewClient(PoolGuardClient.INSTANCE);
                // 按 UA 配置注入，保证 warmupUrl 请求 UA 与 open() 一致（缓存命中 + 复用门控）
                if (injectUA) {
                    try {
                        String systemUa = wv.getSettings().getUserAgentString();
                        wv.getSettings().setUserAgentString(
                                WebSettingsApplier.buildUserAgent(systemUa != null ? systemUa : "", uaExtra));
                    } catch (Throwable ignored) {
                    }
                }
                pooled = wv;
                uaSig = uaSignatureOf(injectUA, uaExtra);
            } catch (Throwable t) {
                Log.w(TAG, "pool pre-create failed: " + t.getMessage());
                pooled = null;
            }
        }
        return pooled;
    }

    /** UA 签名：复用门控——预热 UA 配置需与 {@code open()} config UA 一致，否则不复用。 */
    private static String uaSignatureOf(boolean injectUA, String uaExtra) {
        return (injectUA ? "1" : "0") + "|" + (uaExtra != null ? uaExtra : "");
    }

    /**
     * 池实例预热态 / 空闲态守卫 client。
     *
     * <p>渲染进程崩溃时：作废池实例（{@link #invalidateIfPooled}）+ 返回 {@code true} 阻止系统杀死进程。
     * 若不处理（走系统默认 {@code return false}），共享渲染进程崩溃会把整个进程的 WebView 子系统
     * 拖入不可用状态，表现为"退出重进、release 都救不回，唯有杀进程"的持久白屏。
     */
    private static final class PoolGuardClient extends WebViewClient {
        private static final PoolGuardClient INSTANCE = new PoolGuardClient();

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

    private static void runOnMain(Runnable block) {
        if (Looper.myLooper() == Looper.getMainLooper()) {
            block.run();
        } else {
            MAIN.post(block);
        }
    }
}
