package com.malk.utils;

import org.junit.Test;

import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

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

/**
 * 单元测试：{@link UtilHttpAudit}
 *
 * <p>覆盖 sanitize 脱敏规则 / endpoint URL 解析 / vendor 识别。
 * BACKLOG B2.1 + mjava-baseline §3.5 审计日志能力。</p>
 */
public class UtilHttpAuditTest {

    // ---------- sanitize ----------

    @Test
    public void sanitize_null_returns_null() {
        assertNull(UtilHttpAudit.sanitize(null));
    }

    @Test
    public void sanitize_empty_returns_empty() {
        assertTrue(UtilHttpAudit.sanitize(new HashMap<>()).isEmpty());
    }

    @Test
    public void sanitize_token_field_masked() {
        Map<String, Object> in = new HashMap<>();
        in.put("access_token", "abc123");
        in.put("name", "alice");
        Map<String, Object> out = UtilHttpAudit.sanitize(in);
        assertEquals("***", out.get("access_token"));
        assertEquals("alice", out.get("name"));
    }

    @Test
    public void sanitize_appSecret_password_aesKey_privateKey_all_masked() {
        Map<String, Object> in = new HashMap<>();
        in.put("appSecret", "s1");
        in.put("password", "p1");
        in.put("aesKey", "k1");
        in.put("privateKey", "k2");
        in.put("clientSecret", "s2");
        in.put("safeField", "ok");
        Map<String, Object> out = UtilHttpAudit.sanitize(in);
        assertEquals("***", out.get("appSecret"));
        assertEquals("***", out.get("password"));
        assertEquals("***", out.get("aesKey"));
        assertEquals("***", out.get("privateKey"));
        assertEquals("***", out.get("clientSecret"));
        assertEquals("ok", out.get("safeField"));
    }

    @Test
    public void sanitize_case_insensitive() {
        Map<String, Object> in = new HashMap<>();
        in.put("ACCESS_TOKEN", "x");
        in.put("Password", "y");
        in.put("appSECRET", "z");
        Map<String, Object> out = UtilHttpAudit.sanitize(in);
        assertEquals("***", out.get("ACCESS_TOKEN"));
        assertEquals("***", out.get("Password"));
        assertEquals("***", out.get("appSECRET"));
    }

    @Test
    public void sanitize_recurses_into_nested_map() {
        Map<String, Object> inner = new LinkedHashMap<>();
        inner.put("token", "secret-val");
        inner.put("desc", "ok");
        Map<String, Object> in = new HashMap<>();
        in.put("config", inner);
        in.put("name", "alice");

        Map<String, Object> out = UtilHttpAudit.sanitize(in);
        Map<String, Object> outInner = (Map<String, Object>) out.get("config");
        assertEquals("***", outInner.get("token"));
        assertEquals("ok", outInner.get("desc"));
        assertEquals("alice", out.get("name"));
    }

    @Test
    public void sanitize_preserves_non_sensitive_values() {
        Map<String, Object> in = new HashMap<>();
        in.put("count", 42);
        in.put("active", true);
        in.put("tags", Arrays.asList("a", "b"));
        Map<String, Object> out = UtilHttpAudit.sanitize(in);
        assertEquals(42, out.get("count"));
        assertEquals(true, out.get("active"));
        assertEquals(Arrays.asList("a", "b"), out.get("tags"));
    }

    @Test
    public void sanitize_does_not_mutate_input() {
        Map<String, Object> in = new HashMap<>();
        in.put("token", "raw");
        UtilHttpAudit.sanitize(in);
        assertEquals("源 Map 不应被改写", "raw", in.get("token"));
    }

    // ---------- isSensitiveKey ----------

    @Test
    public void isSensitiveKey_null_or_empty_returns_false() {
        assertFalse(UtilHttpAudit.isSensitiveKey(null));
        assertFalse(UtilHttpAudit.isSensitiveKey(""));
    }

    @Test
    public void isSensitiveKey_safe_keys_return_false() {
        assertFalse(UtilHttpAudit.isSensitiveKey("name"));
        assertFalse(UtilHttpAudit.isSensitiveKey("user_id"));
        assertFalse(UtilHttpAudit.isSensitiveKey("dept_id_list"));
    }

    // ---------- endpoint ----------

    @Test
    public void endpoint_full_url_extracts_path() {
        assertEquals("/v1.0/yida/forms/instances",
                UtilHttpAudit.endpoint("https://api.dingtalk.com/v1.0/yida/forms/instances"));
    }

    @Test
    public void endpoint_strips_query_string() {
        assertEquals("/topapi/v2/user/get",
                UtilHttpAudit.endpoint("https://oapi.dingtalk.com/topapi/v2/user/get?access_token=xxx"));
    }

    @Test
    public void endpoint_no_path_returns_slash() {
        assertEquals("/", UtilHttpAudit.endpoint("https://api.example.com"));
    }

    @Test
    public void endpoint_relative_url_returns_as_is() {
        assertEquals("/iam/api/users", UtilHttpAudit.endpoint("/iam/api/users"));
    }

    @Test
    public void endpoint_null_or_empty_returns_empty() {
        assertEquals("", UtilHttpAudit.endpoint(null));
        assertEquals("", UtilHttpAudit.endpoint(""));
    }

    // ---------- vendor ----------

    @Test
    public void vendor_dingtalk_recognized() {
        assertEquals("dingtalk", UtilHttpAudit.vendor("https://oapi.dingtalk.com/gettoken"));
        assertEquals("dingtalk", UtilHttpAudit.vendor("https://api.dingtalk.com/v1.0/yida/forms/instances"));
    }

    @Test
    public void vendor_other_vendors_recognized() {
        assertEquals("fxiaoke", UtilHttpAudit.vendor("https://open.fxiaoke.com/cgi/user/getByMobile"));
        assertEquals("teambition", UtilHttpAudit.vendor("https://open.teambition.com/api/v3/project/query"));
        assertEquals("feishu", UtilHttpAudit.vendor("https://open.feishu.cn/open-apis/auth"));
    }

    @Test
    public void vendor_unknown_returns_other() {
        assertEquals("other", UtilHttpAudit.vendor("https://example.com/api"));
        assertEquals("other", UtilHttpAudit.vendor(null));
    }
}
