package tech.easyflow.ai.plugin;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.stereotype.Component;
import tech.easyflow.ai.entity.Plugin;
import tech.easyflow.common.web.exceptions.BusinessException;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;

/**
 * 构建可持久化的插件受控连接快照。
 */
@Component
public class PluginConnectionSnapshotFactory {

    private static final TypeReference<List<Map<String, Object>>> HEADER_LIST_TYPE = new TypeReference<>() { };
    private static final Pattern INPUT_REFERENCE =
            Pattern.compile("^\\$\\{input:[A-Za-z0-9_.-]+}$");
    private static final Set<String> PUBLIC_HEADERS = Set.of(
            "accept", "accept-language", "content-type", "user-agent");

    private final ObjectMapper objectMapper;

    /**
     * 创建插件连接快照工厂。
     *
     * @param objectMapper JSON 映射器
     */
    public PluginConnectionSnapshotFactory(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
    }

    /**
     * 构建字段白名单快照，并拒绝把插件凭据明文复制到发布快照。
     *
     * @param plugin 插件资源
     * @return 服务端 Runtime 使用的连接快照
     * @throws BusinessException 配置缺失或包含明文凭据时抛出
     */
    public Map<String, Object> snapshot(Plugin plugin) {
        if (plugin == null || plugin.getId() == null) {
            throw new BusinessException("插件资源不能为空");
        }
        validateBaseUrl(plugin.getBaseUrl());
        validateHeaders(plugin.getHeaders());
        if ("apiKey".equalsIgnoreCase(plugin.getAuthType())
                && !isInputReference(plugin.getTokenValue())) {
            throw new BusinessException("插件鉴权值必须使用 ${input:key} 引用");
        }
        Map<String, Object> result = new LinkedHashMap<>();
        result.put("id", plugin.getId());
        result.put("alias", plugin.getAlias());
        result.put("name", plugin.getName());
        result.put("description", plugin.getDescription());
        result.put("baseUrl", plugin.getBaseUrl());
        result.put("authType", plugin.getAuthType());
        result.put("position", plugin.getPosition());
        result.put("headers", plugin.getHeaders());
        result.put("tokenKey", plugin.getTokenKey());
        result.put("tokenValue", plugin.getTokenValue());
        return result;
    }

    private void validateBaseUrl(String value) {
        if (value == null || value.isBlank()) {
            throw new BusinessException("插件基础地址不能为空");
        }
        String lower = value.toLowerCase(Locale.ROOT);
        if (lower.matches("^[a-z][a-z0-9+.-]*://[^/@]+:[^/@]+@.*")
                || lower.matches(".*[?&](token|secret|password|api[_-]?key|authorization)=[^&]+.*")) {
            throw new BusinessException("插件基础地址不能包含明文凭据");
        }
    }

    private void validateHeaders(String headersJson) {
        if (headersJson == null || headersJson.isBlank()) {
            return;
        }
        List<Map<String, Object>> headers;
        try {
            headers = objectMapper.readValue(headersJson, HEADER_LIST_TYPE);
        } catch (Exception exception) {
            throw new BusinessException("插件请求头格式错误");
        }
        for (Map<String, Object> header : headers) {
            String name = text(header.get("label"));
            String value = text(header.get("value"));
            if (name == null || name.isBlank()) {
                throw new BusinessException("插件请求头名称不能为空");
            }
            if (!PUBLIC_HEADERS.contains(name.toLowerCase(Locale.ROOT))
                    && value != null && !value.isBlank() && !isInputReference(value)) {
                throw new BusinessException("插件请求头凭据必须使用 ${input:key} 引用：" + name);
            }
        }
    }

    private boolean isInputReference(String value) {
        return value != null && INPUT_REFERENCE.matcher(value.trim()).matches();
    }

    private String text(Object value) {
        return value == null ? null : String.valueOf(value).trim();
    }
}
