package com.tencent.qian.webview.client;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import android.content.Context;
import android.webkit.WebView;

import androidx.core.util.Supplier;
import androidx.test.ext.junit.runners.AndroidJUnit4;

import com.tencent.qian.webview.model.SdkEvent;
import com.tencent.qian.webview.model.SignCallback;
import com.tencent.qian.webview.model.SignResult;
import com.tencent.qian.webview.scheme.ExternalSchemeHandler;
import com.tencent.qian.webview.scheme.SchemeInterceptor;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RuntimeEnvironment;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * {@link QianWebViewClient} 的 URL 路由优先级单测。
 *
 * <p>覆盖 {@code overrideUrl()} 的 4 级优先级调度逻辑:
 * <ol>
 *   <li>qianapp:// → SchemeInterceptor 拦截 → return true</li>
 *   <li>http(s):// → 放行 → return false</li>
 *   <li>externalSchemes 白名单命中 → Intent 启动 → return true</li>
 *   <li>未知 scheme → SchemeNotMatched → return true</li>
 * </ol>
 * + null/空 → return false<br>
 * + context 为 null → return false
 */
@RunWith(AndroidJUnit4.class)
public class QianWebViewClientOverrideUrlTest {

    private Context context;
    private final List<SdkEvent> events = new ArrayList<>();
    private final List<SignResult> signResults = new ArrayList<>();
    private final SignCallback callback = new SignCallback() {
        @Override
        public void onSignResult(SignResult result) {
            signResults.add(result);
        }

        @Override
        public void onEvent(SdkEvent event) {
            events.add(event);
        }
    };

    @Before
    public void setUp() {
        context = RuntimeEnvironment.getApplication();
        events.clear();
        signResults.clear();
    }

    private QianWebViewClient createClient(List<String> externalSchemes, Supplier<Context> contextProvider) {
        SchemeInterceptor schemeInterceptor = new SchemeInterceptor(() -> callback);
        ExternalSchemeHandler externalSchemeHandler = new ExternalSchemeHandler(externalSchemes, () -> callback);
        return new QianWebViewClient(
                schemeInterceptor,
                externalSchemeHandler,
                () -> callback,
                contextProvider
        );
    }

    private QianWebViewClient createClient(List<String> externalSchemes) {
        return createClient(externalSchemes, () -> context);
    }

    private QianWebViewClient createClient() {
        return createClient(Arrays.asList("weixin", "alipays"));
    }

    // ────────── 优先级 1: qianapp:// → 拦截 ──────────

    @Test
    public void qianapp_URL_is_intercepted_and_returns_true() {
        QianWebViewClient client = createClient();
        @SuppressWarnings("deprecation")
        boolean result = client.shouldOverrideUrlLoading((WebView) null, "qianapp://callback/sign?action=sign&result=success&flowId=abc");

        assertTrue("qianapp 应被拦截", result);
        // SchemeInterceptor 会派发 onSignResult
        assertTrue(!signResults.isEmpty());
    }

    @Test
    public void qianapp_with_uppercase_scheme_is_intercepted() {
        QianWebViewClient client = createClient();
        @SuppressWarnings("deprecation")
        boolean result = client.shouldOverrideUrlLoading((WebView) null, "QIANAPP://x?action=fill");

        assertTrue(result);
    }

    // ────────── 优先级 2: http(s) → 放行 ──────────

    @Test
    public void http_URL_returns_false_allows_default_navigation() {
        QianWebViewClient client = createClient();
        @SuppressWarnings("deprecation")
        boolean result = client.shouldOverrideUrlLoading((WebView) null, "http://example.com/page");

        assertFalse("http 应放行", result);
        assertTrue(events.isEmpty());
    }

    @Test
    public void https_URL_returns_false_allows_default_navigation() {
        QianWebViewClient client = createClient();
        @SuppressWarnings("deprecation")
        boolean result = client.shouldOverrideUrlLoading((WebView) null, "https://quick.qian.tencent.cn/sign");

        assertFalse("https 应放行", result);
        assertTrue(events.isEmpty());
    }

    @Test
    public void HTTPS_uppercase_returns_false() {
        QianWebViewClient client = createClient();
        @SuppressWarnings("deprecation")
        boolean result = client.shouldOverrideUrlLoading((WebView) null, "HTTPS://EXAMPLE.COM/PATH");

        assertFalse(result);
    }

    // ────────── 优先级 3: externalSchemes 白名单 → 消费 ──────────

    @Test
    public void weixin_URL_in_whitelist_is_consumed() {
        QianWebViewClient client = createClient(Arrays.asList("weixin", "alipays"));
        @SuppressWarnings("deprecation")
        boolean result = client.shouldOverrideUrlLoading((WebView) null, "weixin://wxa/launchminiprogram?appid=wx123");

        assertTrue("weixin 应被消费", result);
        assertEquals(1, events.size());
        assertTrue(events.get(0) instanceof SdkEvent.SchemeIntercepted);
    }

    @Test
    public void alipays_URL_in_whitelist_is_consumed() {
        QianWebViewClient client = createClient(Arrays.asList("weixin", "alipays"));
        @SuppressWarnings("deprecation")
        boolean result = client.shouldOverrideUrlLoading((WebView) null, "alipays://platformapi/startapp?appId=123");

        assertTrue(result);
        assertTrue(events.stream().anyMatch(e -> e instanceof SdkEvent.SchemeIntercepted));
    }

    // ────────── 优先级 4: 未知 scheme → SchemeNotMatched ──────────

    @Test
    public void unknown_scheme_not_in_whitelist_emits_SchemeNotMatched() {
        QianWebViewClient client = createClient(Arrays.asList("weixin", "alipays"));
        @SuppressWarnings("deprecation")
        boolean result = client.shouldOverrideUrlLoading((WebView) null, "mqqapi://chat?uin=12345");

        assertTrue("未知 scheme 应被消费(丢弃)", result);
        assertEquals(1, events.size());
        assertTrue(events.get(0) instanceof SdkEvent.SchemeNotMatched);
    }

    // ────────── 边界: null / 空 → return false ──────────

    @Test
    public void null_URL_returns_false() {
        QianWebViewClient client = createClient();
        @SuppressWarnings("deprecation")
        boolean result = client.shouldOverrideUrlLoading((WebView) null, (String) null);

        assertFalse(result);
        assertTrue(events.isEmpty());
    }

    @Test
    public void empty_URL_returns_false() {
        QianWebViewClient client = createClient();
        @SuppressWarnings("deprecation")
        boolean result = client.shouldOverrideUrlLoading((WebView) null, "");

        assertFalse(result);
        assertTrue(events.isEmpty());
    }

    // ────────── 边界: context 为 null → return false ──────────

    @Test
    public void null_context_returns_false_for_external_scheme() {
        QianWebViewClient client = createClient(
                Arrays.asList("weixin"),
                () -> null
        );
        @SuppressWarnings("deprecation")
        boolean result = client.shouldOverrideUrlLoading((WebView) null, "weixin://wxa/test");

        // context 为 null,无法启动 Intent,返回 false
        assertFalse(result);
    }

    // ────────── 优先级验证: qianapp 优先于 externalSchemes ──────────

    @Test
    public void qianapp_takes_priority_even_if_also_in_externalSchemes() {
        // 即使 externalSchemes 包含 qianapp(不应该,但防御性测试)
        QianWebViewClient client = createClient(Arrays.asList("qianapp", "weixin"));
        @SuppressWarnings("deprecation")
        boolean result = client.shouldOverrideUrlLoading((WebView) null, "qianapp://callback?action=sign&result=success");

        assertTrue(result);
        // 应该走 SchemeInterceptor 而非 ExternalSchemeHandler
        assertTrue(!signResults.isEmpty());
    }

    // ────────── http 优先于 externalSchemes ──────────

    @Test
    public void http_takes_priority_even_if_also_in_externalSchemes() {
        // 即使 externalSchemes 包含 http(不应该,但防御性测试)
        QianWebViewClient client = createClient(Arrays.asList("http", "weixin"));
        @SuppressWarnings("deprecation")
        boolean result = client.shouldOverrideUrlLoading((WebView) null, "http://example.com");

        assertFalse("http 应放行,不走 externalSchemes", result);
    }
}
