package com.malk.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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

/**
 * 第三方 HTTP 调用审计日志工具
 *
 * <p>实施 {@code mjava-baseline §3.5} 请求审计要求：vendor / endpoint / latencyMs / success / errorMsg，
 * 敏感字段（token / secret / password / aesKey / privateKey 等）在日志中替换为 {@code ***}。</p>
 *
 * <p>由 {@link UtilHttp#doRequest} 单点调用；其他 do* 重载经它代理，无需重复埋点。</p>
 *
 * <p>日志格式（logger name = {@code audit}）：</p>
 * <pre>
 * INFO  [http] vendor=dingtalk method=POST endpoint=/topapi/v2/user/get latencyMs=87 status=success respSize=412
 * WARN  [http] vendor=aliwork method=PUT endpoint=/v1.0/yida/forms/instances latencyMs=2103 status=error msg=Connect timed out
 * </pre>
 */
public abstract class UtilHttpAudit {

    private static final Logger log = LoggerFactory.getLogger("audit");

    /**
     * 敏感 key 关键字（子串匹配，case-insensitive）。
     * <p>覆盖 mjava-baseline §3.5 列出的 token/appSecret/password/aesKey/privateKey 及常见变体。</p>
     */
    private static final String[] MASK_KEYS = {
            "token", "secret", "password", "passwd",
            "aeskey", "privatekey", "publickey",
            "authorization", "auth_token", "accesstoken",
            "client_secret", "client_id", "appsecret", "appkey"
    };

    /**
     * 递归脱敏 Map：key 名包含敏感关键字（不分大小写）的 value 替换为 {@code ***}。
     *
     * <p>规则：</p>
     * <ul>
     *   <li>嵌套 Map 递归处理</li>
     *   <li>非敏感 value 原样保留（包括 List / 数字 / 字符串）</li>
     *   <li>List 内嵌 Map 不展开递归（性能考虑；调用方需要时自行预处理）</li>
     *   <li>null 输入返回 null，不抛异常</li>
     * </ul>
     */
    public static Map<String, Object> sanitize(Map<?, ?> source) {
        if (source == null) return null;
        Map<String, Object> out = new HashMap<>(source.size());
        for (Map.Entry<?, ?> e : source.entrySet()) {
            String key = String.valueOf(e.getKey());
            Object val = e.getValue();
            if (isSensitiveKey(key)) {
                out.put(key, "***");
            } else if (val instanceof Map) {
                out.put(key, sanitize((Map<?, ?>) val));
            } else {
                out.put(key, val);
            }
        }
        return out;
    }

    /**
     * 判断 key 是否敏感
     */
    public static boolean isSensitiveKey(String key) {
        if (key == null || key.isEmpty()) return false;
        String lower = key.toLowerCase();
        for (String mk : MASK_KEYS) {
            if (lower.contains(mk)) return true;
        }
        return false;
    }

    /**
     * 从 URL 提取 path 部分（去 scheme + host + query）
     *
     * <p>典型场景：</p>
     * <pre>
     * "https://api.dingtalk.com/v1.0/yida/forms/instances?x=1" → "/v1.0/yida/forms/instances"
     * "https://oapi.dingtalk.com/gettoken"                     → "/gettoken"
     * "/relative/path"                                          → "/relative/path"
     * null / 空                                                 → ""
     * </pre>
     */
    public static String endpoint(String url) {
        if (url == null || url.isEmpty()) return "";
        int hostStart = url.indexOf("://");
        int pathStart = url.indexOf('/', hostStart < 0 ? 0 : hostStart + 3);
        if (pathStart < 0) return "/";
        int queryStart = url.indexOf('?', pathStart);
        return queryStart < 0 ? url.substring(pathStart) : url.substring(pathStart, queryStart);
    }

    /**
     * 从 URL host 推断 vendor 名（用于审计聚合）
     *
     * <p>识别清单与基座现有 vendor 模块对应。未识别返回 {@code "other"}。</p>
     */
    public static String vendor(String url) {
        if (url == null) return "other";
        if (url.contains("dingtalk.com")) return "dingtalk";
        if (url.contains("fxiaoke.com")) return "fxiaoke";
        if (url.contains("teambition.com")) return "teambition";
        if (url.contains("ekuaibao.com")) return "ekuaibao";
        if (url.contains("beisen.com")) return "beisen";
        if (url.contains("vika.cn")) return "vika";
        if (url.contains("xbongbong.com")) return "xbongbong";
        if (url.contains("feishu.cn") || url.contains("larksuite.com")) return "feishu";
        if (url.contains("h3yun.com")) return "h3yun";
        return "other";
    }

    /**
     * 记录成功调用
     */
    public static void logSuccess(String vendor, String method, String endpoint, long latencyMs, int responseSize) {
        log.info("[http] vendor={} method={} endpoint={} latencyMs={} status=success respSize={}",
                vendor, method, endpoint, latencyMs, responseSize);
    }

    /**
     * 记录失败调用（异常 / 解析失败）
     */
    public static void logError(String vendor, String method, String endpoint, long latencyMs, String errorMsg) {
        log.warn("[http] vendor={} method={} endpoint={} latencyMs={} status=error msg={}",
                vendor, method, endpoint, latencyMs, errorMsg);
    }
}
