package tech.easyflow.ai.mcp;

import com.easyagents.agent.runtime.mcp.McpSpec;
import com.easyagents.agent.runtime.mcp.McpTransportType;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.stereotype.Component;
import tech.easyflow.ai.entity.Mcp;
import tech.easyflow.common.web.exceptions.BusinessException;

import java.time.Duration;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 将 EasyFlow MCP 配置映射为无业务状态的运行时连接声明。
 */
@Component
public class McpRuntimeSpecFactory {

    private static final Pattern INPUT_PATTERN = Pattern.compile("\\$\\{input:([A-Za-z0-9_.-]+)}");
    private static final TypeReference<Map<String, Object>> MAP_TYPE = new TypeReference<>() { };

    private final ObjectMapper objectMapper;

    /**
     * 创建 MCP 运行声明工厂。
     *
     * @param objectMapper JSON 映射器
     */
    public McpRuntimeSpecFactory(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
    }

    /**
     * 构建 MCP 运行连接声明。
     *
     * @param mcp MCP 资源
     * @param requireUniqueServer 是否要求配置中只有一个服务
     * @return MCP 运行连接声明
     * @throws BusinessException 配置为空、格式错误、多服务或输入变量未解析时抛出
     */
    public McpSpec build(Mcp mcp, boolean requireUniqueServer) {
        if (mcp == null || mcp.getId() == null) {
            throw new BusinessException("MCP 资源不能为空");
        }
        Map<String, Object> config = parseConfig(mcp.getConfigJson());
        Map<String, Object> servers = mapValue(config, "mcpServers");
        if (servers.isEmpty()) {
            throw new BusinessException("MCP 配置 JSON 中没有找到任何 MCP 服务");
        }
        if (requireUniqueServer && servers.size() != 1) {
            throw new BusinessException(409, 4092, "MCP 配置必须且只能包含一个服务，请拆分后重试");
        }
        Map.Entry<String, Object> server = servers.entrySet().iterator().next();
        if (!(server.getValue() instanceof Map<?, ?> rawServer)) {
            throw new BusinessException("MCP 服务配置必须是对象：" + server.getKey());
        }
        Map<String, Object> serverConfig = new LinkedHashMap<>();
        rawServer.forEach((key, value) -> serverConfig.put(String.valueOf(key), value));

        McpSpec spec = new McpSpec();
        spec.setName("mcp_" + safeSegment(mcp.getId().toString()));
        spec.setDescription(firstNonBlank(mcp.getDescription(), mcp.getTitle()));
        spec.setTransportType(McpTransportType.from(firstNonBlank(
                mcp.getTransportType(), stringValue(serverConfig, "transport", null))));
        spec.setCommand(resolveInput(stringValue(serverConfig, "command", null)));
        spec.setArgs(resolveInputs(stringListValue(serverConfig, "args")));
        spec.setEnv(resolveInputMap(stringMapValue(serverConfig, "env")));
        spec.setUrl(resolveInput(stringValue(serverConfig, "url", null)));
        spec.setHeaders(resolveInputMap(stringMapValue(serverConfig, "headers")));
        spec.setQueryParams(resolveInputMap(stringMapValue(serverConfig, "queryParams")));
        Duration timeout = durationValue(serverConfig, "timeout");
        if (timeout != null) {
            spec.setTimeout(timeout);
        }
        Duration initializationTimeout = durationValue(serverConfig, "initializationTimeout");
        if (initializationTimeout != null) {
            spec.setInitializationTimeout(initializationTimeout);
        }
        spec.getMetadata().put("mcpId", mcp.getId().toString());
        spec.getMetadata().put("mcpTitle", mcp.getTitle());
        spec.getMetadata().put("serverName", server.getKey());
        return spec;
    }

    /**
     * 解析 MCP JSON。
     *
     * @param configJson MCP JSON
     * @return 配置 Map
     */
    private Map<String, Object> parseConfig(String configJson) {
        if (configJson == null || configJson.isBlank()) {
            throw new BusinessException("MCP 配置 JSON 不能为空");
        }
        try {
            return objectMapper.readValue(configJson, MAP_TYPE);
        } catch (Exception exception) {
            throw new BusinessException("MCP 配置 JSON 格式错误");
        }
    }

    /**
     * 读取对象字段。
     *
     * @param source 配置 Map
     * @param key 字段名
     * @return 对象 Map
     */
    private Map<String, Object> mapValue(Map<String, Object> source, String key) {
        Object value = source == null ? null : source.get(key);
        if (value == null) {
            return new LinkedHashMap<>();
        }
        if (!(value instanceof Map<?, ?> raw)) {
            throw new BusinessException("MCP 配置字段必须是对象：" + key);
        }
        Map<String, Object> result = new LinkedHashMap<>();
        raw.forEach((rawKey, rawValue) -> result.put(String.valueOf(rawKey), rawValue));
        return result;
    }

    /**
     * 读取字符串数组字段。
     *
     * @param source 配置 Map
     * @param key 字段名
     * @return 字符串数组
     */
    private List<String> stringListValue(Map<String, Object> source, String key) {
        Object value = source == null ? null : source.get(key);
        if (value == null) {
            return new ArrayList<>();
        }
        if (!(value instanceof Collection<?> collection)) {
            throw new BusinessException("MCP 配置字段必须是数组：" + key);
        }
        List<String> result = new ArrayList<>();
        collection.stream().filter(item -> item != null).forEach(item -> result.add(String.valueOf(item)));
        return result;
    }

    /**
     * 读取字符串 Map 字段。
     *
     * @param source 配置 Map
     * @param key 字段名
     * @return 字符串 Map
     */
    private Map<String, String> stringMapValue(Map<String, Object> source, String key) {
        Map<String, Object> raw = mapValue(source, key);
        Map<String, String> result = new LinkedHashMap<>();
        raw.forEach((name, value) -> {
            if (value != null) {
                result.put(name, String.valueOf(value));
            }
        });
        return result;
    }

    /**
     * 读取字符串字段。
     *
     * @param source 配置 Map
     * @param key 字段名
     * @param fallback 默认值
     * @return 字符串值
     */
    private String stringValue(Map<String, Object> source, String key, String fallback) {
        Object value = source == null ? null : source.get(key);
        if (value == null || String.valueOf(value).isBlank()) {
            return fallback;
        }
        return String.valueOf(value);
    }

    /**
     * 读取秒数或 ISO-8601 Duration。
     *
     * @param source 配置 Map
     * @param key 字段名
     * @return Duration 或 null
     */
    private Duration durationValue(Map<String, Object> source, String key) {
        Object value = source == null ? null : source.get(key);
        if (value == null || String.valueOf(value).isBlank()) {
            return null;
        }
        if (value instanceof Number number) {
            return Duration.ofSeconds(number.longValue());
        }
        try {
            return Duration.parse(String.valueOf(value).trim());
        } catch (Exception ignored) {
            try {
                return Duration.ofSeconds(Long.parseLong(String.valueOf(value).trim()));
            } catch (NumberFormatException exception) {
                throw new BusinessException("MCP 配置字段必须是秒数或 Duration：" + key);
            }
        }
    }

    /**
     * 解析数组中的 MCP 输入变量。
     *
     * @param values 原值
     * @return 已解析值
     */
    private List<String> resolveInputs(List<String> values) {
        List<String> result = new ArrayList<>();
        values.forEach(value -> result.add(resolveInput(value)));
        return result;
    }

    /**
     * 解析 Map 中的 MCP 输入变量。
     *
     * @param values 原值
     * @return 已解析值
     */
    private Map<String, String> resolveInputMap(Map<String, String> values) {
        Map<String, String> result = new LinkedHashMap<>();
        values.forEach((key, value) -> result.put(key, resolveInput(value)));
        return result;
    }

    /**
     * 从系统属性解析 MCP 输入变量。
     *
     * @param value 原值
     * @return 已解析值
     */
    private String resolveInput(String value) {
        if (value == null || value.isBlank()) {
            return value;
        }
        Matcher matcher = INPUT_PATTERN.matcher(value);
        StringBuffer result = new StringBuffer();
        while (matcher.find()) {
            String key = matcher.group(1);
            String replacement = System.getProperty("mcp.input." + key);
            if (replacement == null || replacement.isBlank()) {
                throw new BusinessException("MCP 输入变量未解析：" + key);
            }
            matcher.appendReplacement(result, Matcher.quoteReplacement(replacement));
        }
        matcher.appendTail(result);
        return result.toString();
    }

    /**
     * 生成安全名称片段。
     *
     * @param value 原值
     * @return 安全片段
     */
    private String safeSegment(String value) {
        String normalized = value.trim().replaceAll("[^A-Za-z0-9_-]", "_").replaceAll("_+", "_");
        return normalized.isBlank() ? "resource" : normalized;
    }

    /**
     * 获取首个非空文本。
     *
     * @param first 首选值
     * @param second 备选值
     * @return 非空文本
     */
    private String firstNonBlank(String first, String second) {
        return first == null || first.isBlank() ? second : first;
    }
}
