package com.tencent.qian.webview.scheme;

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

import android.content.Context;
import android.content.Intent;

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 org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.Shadows;

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

/**
 * {@link ExternalSchemeHandler} 单测。
 *
 * <p>覆盖场景:
 * <ul>
 *   <li>weixin URL 默认白名单命中 → 启动 Intent + SchemeIntercepted</li>
 *   <li>空白名单 → 不启动 Intent + SchemeNotMatched</li>
 *   <li>目标 App 未安装(ActivityNotFoundException)→ SchemeNotMatched</li>
 *   <li>SecurityException → SchemeNotMatched</li>
 *   <li>未知 scheme 不在白名单 → SchemeNotMatched</li>
 *   <li>http(s) / qianapp 不归本 handler 处理 → 返回 false</li>
 *   <li>null / 空字符串 → 返回 false</li>
 *   <li>大小写不敏感匹配</li>
 *   <li>自定义白名单完全替换默认值</li>
 * </ul>
 */
@RunWith(AndroidJUnit4.class)
public class ExternalSchemeHandlerTest {

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

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

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

    // ────────── 1. 白名单命中 → 启动 Intent + SchemeIntercepted ──────────

    @Test
    public void weixin_URL_is_forwarded_when_in_default_whitelist() {
        ExternalSchemeHandler handler = new ExternalSchemeHandler(
                Arrays.asList("weixin", "alipays"),
                () -> callback
        );
        String url = "weixin://wxa/launchminiprogram?appid=wx123";

        boolean consumed = handler.tryStartActivity(context, url);

        assertTrue("应消费此次导航", consumed);
        assertEquals(1, events.size());
        SdkEvent.SchemeIntercepted event = (SdkEvent.SchemeIntercepted) events.get(0);
        assertEquals("weixin", event.getScheme());
        assertEquals(url, event.getUrl());
    }

    @Test
    public void alipays_URL_is_forwarded_when_in_default_whitelist() {
        ExternalSchemeHandler handler = new ExternalSchemeHandler(
                Arrays.asList("weixin", "alipays"),
                () -> callback
        );
        String url = "alipays://platformapi/startapp?appId=123";

        boolean consumed = handler.tryStartActivity(context, url);

        assertTrue(consumed);
        assertEquals(1, events.size());
        SdkEvent.SchemeIntercepted event = (SdkEvent.SchemeIntercepted) events.get(0);
        assertEquals("alipays", event.getScheme());
        assertEquals(url, event.getUrl());
    }

    // ────────── 2. 空白名单 → SchemeNotMatched ──────────

    @Test
    public void empty_whitelist_blocks_all_external_schemes() {
        ExternalSchemeHandler handler = new ExternalSchemeHandler(
                Collections.emptyList(),
                () -> callback
        );
        String url = "weixin://wxa/launchminiprogram?appid=wx123";

        boolean consumed = handler.tryStartActivity(context, url);

        assertTrue("未匹配也应消费(丢弃)", consumed);
        assertEquals(1, events.size());
        assertTrue(events.get(0) instanceof SdkEvent.SchemeNotMatched);
        assertEquals(url, ((SdkEvent.SchemeNotMatched) events.get(0)).getUrl());
    }

    // ────────── 3. 未知 scheme 不在白名单 → SchemeNotMatched ──────────

    @Test
    public void unknown_scheme_not_in_whitelist_emits_SchemeNotMatched() {
        ExternalSchemeHandler handler = new ExternalSchemeHandler(
                Arrays.asList("weixin", "alipays"),
                () -> callback
        );
        String url = "mqqapi://chat?uin=12345";

        boolean consumed = handler.tryStartActivity(context, url);

        assertTrue(consumed);
        assertEquals(1, events.size());
        assertTrue(events.get(0) instanceof SdkEvent.SchemeNotMatched);
        assertEquals(url, ((SdkEvent.SchemeNotMatched) events.get(0)).getUrl());
    }

    // ────────── 4. http(s) 不归本 handler 处理 ──────────

    @Test
    public void http_URL_returns_false_not_handled() {
        ExternalSchemeHandler handler = new ExternalSchemeHandler(
                Arrays.asList("weixin", "alipays"),
                () -> callback
        );

        assertFalse(handler.tryStartActivity(context, "http://example.com"));
        assertFalse(handler.tryStartActivity(context, "https://example.com/path"));
        assertTrue(events.isEmpty());
    }

    // ────────── 5. qianapp 不归本 handler 处理(防御性兜底)──────────

    @Test
    public void qianapp_URL_returns_false_not_handled() {
        ExternalSchemeHandler handler = new ExternalSchemeHandler(
                Arrays.asList("weixin", "alipays"),
                () -> callback
        );

        assertFalse(handler.tryStartActivity(context, "qianapp://callback?action=sign"));
        assertTrue(events.isEmpty());
    }

    // ────────── 6. null / 空字符串 → 返回 false ──────────

    @Test
    public void null_URL_returns_false() {
        ExternalSchemeHandler handler = new ExternalSchemeHandler(
                Collections.singletonList("weixin"),
                () -> callback
        );

        assertFalse(handler.tryStartActivity(context, null));
        assertTrue(events.isEmpty());
    }

    @Test
    public void empty_URL_returns_false() {
        ExternalSchemeHandler handler = new ExternalSchemeHandler(
                Collections.singletonList("weixin"),
                () -> callback
        );

        assertFalse(handler.tryStartActivity(context, ""));
        assertTrue(events.isEmpty());
    }

    // ────────── 7. 大小写不敏感匹配 ──────────

    @Test
    public void scheme_matching_is_case_insensitive() {
        ExternalSchemeHandler handler = new ExternalSchemeHandler(
                Arrays.asList("weixin", "alipays"),
                () -> callback
        );
        String url = "WEIXIN://wxa/launchminiprogram?appid=wx123";

        boolean consumed = handler.tryStartActivity(context, url);

        assertTrue(consumed);
        assertEquals(1, events.size());
        SdkEvent.SchemeIntercepted event = (SdkEvent.SchemeIntercepted) events.get(0);
        assertEquals("weixin", event.getScheme());
    }

    @Test
    public void whitelist_entry_case_insensitive() {
        ExternalSchemeHandler handler = new ExternalSchemeHandler(
                Arrays.asList("WEIXIN", "ALIPAYS"),
                () -> callback
        );
        String url = "weixin://wxa/test";

        boolean consumed = handler.tryStartActivity(context, url);

        assertTrue(consumed);
        assertEquals(1, events.size());
        assertTrue(events.get(0) instanceof SdkEvent.SchemeIntercepted);
    }

    // ────────── 8. 自定义白名单完全替换默认值 ──────────

    @Test
    public void custom_whitelist_replaces_default_weixin_blocked_when_not_in_custom_list() {
        ExternalSchemeHandler handler = new ExternalSchemeHandler(
                Collections.singletonList("mqqapi"),
                () -> callback
        );
        String url = "weixin://wxa/test";

        boolean consumed = handler.tryStartActivity(context, url);

        assertTrue("未匹配也应消费(丢弃)", consumed);
        assertEquals(1, events.size());
        assertTrue(events.get(0) instanceof SdkEvent.SchemeNotMatched);
    }

    @Test
    public void custom_whitelist_allows_custom_scheme() {
        ExternalSchemeHandler handler = new ExternalSchemeHandler(
                Arrays.asList("mqqapi", "tbopen"),
                () -> callback
        );
        String url = "mqqapi://chat?uin=12345";

        boolean consumed = handler.tryStartActivity(context, url);

        assertTrue(consumed);
        assertEquals(1, events.size());
        assertTrue(events.get(0) instanceof SdkEvent.SchemeIntercepted);
    }

    // ────────── 9. callback 为 null 时不崩溃 ──────────

    @Test
    public void null_callback_does_not_crash_on_matched_scheme() {
        ExternalSchemeHandler handler = new ExternalSchemeHandler(
                Collections.singletonList("weixin"),
                () -> null
        );
        String url = "weixin://wxa/test";

        boolean consumed = handler.tryStartActivity(context, url);

        assertTrue(consumed);
        // 无 callback,不应崩溃
    }

    @Test
    public void null_callback_does_not_crash_on_unmatched_scheme() {
        ExternalSchemeHandler handler = new ExternalSchemeHandler(
                Collections.emptyList(),
                () -> null
        );
        String url = "weixin://wxa/test";

        boolean consumed = handler.tryStartActivity(context, url);

        assertTrue(consumed);
        // 无 callback,不应崩溃
    }

    // ────────── 10. 无 scheme 的 URL → 返回 false ──────────

    @Test
    public void URL_without_scheme_returns_false() {
        ExternalSchemeHandler handler = new ExternalSchemeHandler(
                Collections.singletonList("weixin"),
                () -> callback
        );

        // 无 scheme 的字符串,Uri.parse 后 scheme 为 null
        assertFalse(handler.tryStartActivity(context, "just-a-path"));
        assertTrue(events.isEmpty());
    }

    // ────────── 11. 畸形 URL 不崩溃 ──────────

    @Test
    public void malformed_URL_does_not_crash() {
        ExternalSchemeHandler handler = new ExternalSchemeHandler(
                Collections.singletonList("weixin"),
                () -> callback
        );

        // "://" → Uri.parse 可能解析出空 scheme
        handler.tryStartActivity(context, "://");
        // 纯空格
        handler.tryStartActivity(context, "   ");
        // 无异常即通过
    }

    // ────────── 12. Intent.FLAG_ACTIVITY_NEW_TASK 被设置 ──────────

    @Test
    public void started_intent_has_FLAG_ACTIVITY_NEW_TASK() {
        ExternalSchemeHandler handler = new ExternalSchemeHandler(
                Collections.singletonList("weixin"),
                () -> callback
        );
        String url = "weixin://wxa/test";

        handler.tryStartActivity(context, url);

        // Robolectric 的 ShadowApplication 记录了 startActivity 调用
        Intent nextIntent = Shadows.shadowOf(RuntimeEnvironment.getApplication()).getNextStartedActivity();
        if (nextIntent != null) {
            assertTrue(
                    "Intent 应包含 FLAG_ACTIVITY_NEW_TASK",
                    (nextIntent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) != 0
            );
            assertEquals(Intent.ACTION_VIEW, nextIntent.getAction());
            assertEquals(url, nextIntent.getData().toString());
        }
    }
}
