package com.tencent.qian.webview.scheme;

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

import com.tencent.qian.webview.model.SignAction;
import com.tencent.qian.webview.model.SignResult;
import com.tencent.qian.webview.model.SignResultStatus;

import org.junit.Test;

import java.util.HashMap;
import java.util.Map;

/**
 * {@link SignResultParser} 单测。
 *
 * <p>覆盖场景:
 * <ul>
 *   <li>标准</li>
 *   <li>空字段</li>
 *   <li>未知 action 原样透出</li>
 *   <li>重复 key</li>
 *   <li>非法 URL</li>
 *   <li>extras 透传</li>
 *   <li>errorCode 落 extras</li>
 *   <li>中文 percent decode</li>
 *   <li>SignAction.SIGN 常量比较</li>
 * </ul>
 *
 * <p>同一组用例在 iOS {@code SignResultParserTests.swift} 同步覆盖。
 */
public class SignResultParserTest {

    // ────────── 1. 标准签署成功 ──────────

    @Test
    public void parses_standard_sign_success() {
        String url = "qianapp://callback/sign"
                + "?flowId=abc-123&action=sign&result=success&from=tencent_ess";
        SignResult r = SignResultParser.parse(url);

        assertEquals(url, r.getRawUrl());
        assertEquals("callback/sign", r.getCustomPath());
        assertEquals("abc-123", r.getFlowId());
        assertEquals(SignAction.SIGN, r.getAction());
        assertEquals(SignResultStatus.SUCCESS, r.getResult());
        assertEquals("tencent_ess", r.getFrom());
        assertTrue(r.getExtras().isEmpty());
    }

    // ────────── 2. 空字段(?action= 保留为空字符串)──────────

    @Test
    public void preserves_empty_value_as_empty_string() {
        String url = "qianapp://x?flowId=&action=&result=&from=";
        SignResult r = SignResultParser.parse(url);

        assertEquals("", r.getFlowId());
        assertEquals("", r.getAction());
        assertEquals("", r.getResult());
        assertEquals("", r.getFrom());
    }

    // ────────── 3. 未知 action 原样透出(不映射 UNKNOWN)──────────

    @Test
    public void preserves_unknown_action_value_verbatim() {
        String url = "qianapp://x?action=futureBrandNewAction&result=mystery";
        SignResult r = SignResultParser.parse(url);

        assertEquals("futureBrandNewAction", r.getAction());
        assertEquals("mystery", r.getResult());
        // 客户在 if-else 的 else 分支可以拿到原值
        assertFalse(r.getAction().equals(SignAction.SIGN));
    }

    // ────────── 4. 重复 key 取首个 ──────────

    @Test
    public void keeps_first_value_when_query_key_duplicates() {
        String url = "qianapp://x?flowId=first&flowId=second&action=sign";
        SignResult r = SignResultParser.parse(url);

        assertEquals("first", r.getFlowId());
        assertEquals(SignAction.SIGN, r.getAction());
    }

    // ────────── 5. 非法 URL → EMPTY ──────────

    @Test
    public void returns_EMPTY_for_null_or_empty_or_wrong_scheme() {
        assertSame(SignResult.EMPTY, SignResultParser.parse(null));
        assertSame(SignResult.EMPTY, SignResultParser.parse(""));
        assertSame(SignResult.EMPTY, SignResultParser.parse("https://example.com/x"));
        assertSame(SignResult.EMPTY, SignResultParser.parse("weixin://wap/pay"));
    }

    // ────────── 6. extras 透传 ──────────

    @Test
    public void transfers_unknown_query_keys_to_extras() {
        String url = "qianapp://done"
                + "?flowId=f1&action=sign&result=success"
                + "&customer_id=u-42&channel=ad-banner";
        SignResult r = SignResultParser.parse(url);

        Map<String, String> expected = new HashMap<>();
        expected.put("customer_id", "u-42");
        expected.put("channel", "ad-banner");
        assertEquals(expected, r.getExtras());
        assertEquals("f1", r.getFlowId()); // recognized field 不重复进 extras
        assertFalse(r.getExtras().containsKey("flowId"));
    }

    // ────────── 7. errorCode 历史字段落 extras(向前兼容)──────────

    @Test
    public void keeps_errorCode_and_errorMsg_in_extras() {
        String url = "qianapp://x"
                + "?action=sign&result=fail"
                + "&errorCode=10086&errorMsg=user_canceled";
        SignResult r = SignResultParser.parse(url);

        assertEquals(SignResultStatus.FAIL, r.getResult());
        assertEquals("10086", r.getExtras().get("errorCode"));
        assertEquals("user_canceled", r.getExtras().get("errorMsg"));
    }

    // ────────── 8. 中文 percent-decode ──────────

    @Test
    public void decodes_utf8_percent_encoded_chinese() {
        // "签署成功" UTF-8 percent-encoded
        String url = "qianapp://x?action=sign&note=%E7%AD%BE%E7%BD%B2%E6%88%90%E5%8A%9F";
        SignResult r = SignResultParser.parse(url);

        assertEquals("签署成功", r.getExtras().get("note"));
    }

    // ────────── 9. customPath = host + path ──────────

    @Test
    public void customPath_combines_host_and_path() {
        SignResult r1 = SignResultParser.parse("qianapp://callback/sign?flowId=1");
        assertEquals("callback/sign", r1.getCustomPath());

        SignResult r2 = SignResultParser.parse("qianapp://result?flowId=1");
        assertEquals("result", r2.getCustomPath());

        SignResult r3 = SignResultParser.parse("qianapp://done/");
        assertEquals("done", r3.getCustomPath());
    }

    // ────────── 10. 无 query ──────────

    @Test
    public void parses_url_without_query() {
        SignResult r = SignResultParser.parse("qianapp://done");
        assertEquals("done", r.getCustomPath());
        assertEquals("", r.getFlowId());
        assertTrue(r.getExtras().isEmpty());
    }

    // ────────── 11. 等号 in value ──────────

    @Test
    public void preserves_equal_sign_inside_value() {
        // base64 之类 value 含等号
        String url = "qianapp://x?token=abc==&action=sign";
        SignResult r = SignResultParser.parse(url);

        assertEquals("abc==", r.getExtras().get("token"));
        assertEquals(SignAction.SIGN, r.getAction());
    }

    // ────────── 12. 大小写 scheme ──────────

    @Test
    public void accepts_uppercase_scheme() {
        SignResult r = SignResultParser.parse("QIANAPP://x?action=sign");
        assertEquals(SignAction.SIGN, r.getAction());
    }

    // ────────── 13. SignAction / SignResultStatus 常量比对(防漂移)──────────

    @Test
    public void constants_match_protocol_wire_values() {
        assertEquals("fill", SignAction.FILL);
        assertEquals("sign", SignAction.SIGN);
        assertEquals("reject_fill", SignAction.REJECT_FILL);
        assertEquals("reject_sign", SignAction.REJECT_SIGN);
        assertEquals("view", SignAction.VIEW);

        assertEquals("success", SignResultStatus.SUCCESS);
        assertEquals("fail", SignResultStatus.FAIL);
    }
}
