package tech.easyflow.agent.runtime.hitl;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Pattern;

/**
 * 将工具审批输入转换为可展示、可持久化的脱敏副本。
 */
public final class ToolApprovalInputProjection {

    private static final String REDACTED = "[已隐藏]";
    private static final Pattern SENSITIVE_KEY = Pattern.compile(
            ".*(token|secret|password|passwd|apikey|authorization|auth|cookie|credential|privatekey|accesskey|header|environment|env).*",
            Pattern.CASE_INSENSITIVE);
    private static final Pattern SENSITIVE_QUERY = Pattern.compile(
            "(?i)([?&](?:token|secret|password|passwd|api[_-]?key|authorization|access[_-]?key)=)[^&#\\s]*");
    private static final Pattern URL_USER_INFO = Pattern.compile(
            "(?i)([a-z][a-z0-9+.-]*://)[^/@\\s]+:[^/@\\s]+@");

    private ToolApprovalInputProjection() {
    }

    /**
     * 投影工具输入，保留普通业务参数并递归遮蔽敏感字段。
     *
     * @param value 原始工具输入
     * @return 不修改原对象的脱敏 Map；输入不是 Map 时返回空 Map
     */
    public static Map<String, Object> project(Object value) {
        if (!(value instanceof Map<?, ?> source)) {
            return Map.of();
        }
        return projectMap(source);
    }

    /**
     * 递归投影 Map。
     *
     * @param source 原始 Map
     * @return 保持字段顺序的脱敏 Map
     */
    private static Map<String, Object> projectMap(Map<?, ?> source) {
        Map<String, Object> projected = new LinkedHashMap<>();
        source.forEach((rawKey, rawValue) -> {
            String key = String.valueOf(rawKey);
            projected.put(key, isSensitiveKey(key) ? REDACTED : projectValue(rawValue));
        });
        return projected;
    }

    /**
     * 递归投影集合、数组、Map 与字符串值。
     *
     * @param value 原始值
     * @return 脱敏副本
     */
    private static Object projectValue(Object value) {
        if (value instanceof Map<?, ?> map) {
            return projectMap(map);
        }
        if (value instanceof Collection<?> collection) {
            List<Object> projected = new ArrayList<>(collection.size());
            collection.forEach(item -> projected.add(projectValue(item)));
            return projected;
        }
        if (value != null && value.getClass().isArray()) {
            int length = Array.getLength(value);
            List<Object> projected = new ArrayList<>(length);
            for (int index = 0; index < length; index++) {
                projected.add(projectValue(Array.get(value, index)));
            }
            return projected;
        }
        if (value instanceof String text) {
            String withoutUserInfo = URL_USER_INFO.matcher(text).replaceAll("$1" + REDACTED + "@");
            return SENSITIVE_QUERY.matcher(withoutUserInfo).replaceAll("$1" + REDACTED);
        }
        return value;
    }

    /**
     * 判断字段名是否表达凭据、认证头或环境配置。
     *
     * @param key 原始字段名
     * @return 需要整值遮蔽时为 true
     */
    private static boolean isSensitiveKey(String key) {
        String normalized = key == null ? "" : key
                .replaceAll("[^A-Za-z0-9]", "")
                .toLowerCase(Locale.ROOT);
        return SENSITIVE_KEY.matcher(normalized).matches();
    }
}
