package com.infoepoch.pms.agent.platform.agent.adapter.out.submit;

import com.infoepoch.pms.agent.platform.agent.application.dto.AgentSubmitCommand;
import com.infoepoch.pms.agent.platform.agent.application.dto.AgentSubmitResult;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.StreamUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestClient;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.UUID;

/**
 * 统一的智能体提交策略。
 */
@Slf4j
@Component
public class HttpSubmitStrategy {

    /**
     * submitUrl 为空时返回的默认成功文案。
     */
    private static final String BLANK_URL_MESSAGE = "submit url is blank, skipped remote submit";

    private final RestClient restClient;
    private final ObjectMapper objectMapper;

    public HttpSubmitStrategy(RestClient.Builder restClientBuilder, ObjectMapper objectMapper) {
        this.restClient = restClientBuilder.build();
        this.objectMapper = objectMapper;
    }

    /**
     * 根据是否配置 submitUrl 执行日志提交或 HTTP 提交。
     *
     * @param command 提交命令
     * @return 提交结果
     */
    public AgentSubmitResult submit(AgentSubmitCommand command) {
        String requestId = UUID.randomUUID().toString();
        if (!StringUtils.hasText(command.getSubmitUrl())) {
            log.info("Skip remote submit because submitUrl is blank, agentId={}, agentName={}, requestId={}, fields={}",
                    command.getAgentId(), command.getAgentName(), requestId, command.getFields());
            return new AgentSubmitResult(true, BLANK_URL_MESSAGE, 200, requestId, null);
        }

        try {
            return restClient.post()
                    .uri(command.getSubmitUrl())
                    .contentType(MediaType.APPLICATION_JSON)
                    .body(buildRequestBody(command))
                    .exchange((request, response) -> buildSubmitResult(response, requestId));
        } catch (Exception ex) {
            log.error("Agent submit failed, agentId={}, agentName={}, submitUrl={}, requestId={}",
                    command.getAgentId(), command.getAgentName(), command.getSubmitUrl(), requestId, ex);
            return new AgentSubmitResult(false, getExceptionMessage(ex), null, requestId, null);
        }
    }

    /**
     * 构建 HTTP 提交请求体。
     *
     * @param command 提交命令
     * @return 请求体 Map
     */
    private Map<String, Object> buildRequestBody(AgentSubmitCommand command) {
        Map<String, Object> payload = new LinkedHashMap<>();
        payload.put("agentId", command.getAgentId());
        payload.put("agentName", command.getAgentName());
        payload.put("fields", command.getFields());
        return payload;
    }

    /**
     * 从响应体中读取字符串字段，不存在时返回默认值。
     *
     * @param responseBody 响应体
     * @param key          字段名
     * @param defaultValue 默认值
     * @return 读取到的字符串
     */
    private String getTextValue(Map<String, Object> responseBody, String key, String defaultValue) {
        if (responseBody == null) {
            return defaultValue;
        }
        Object value = responseBody.get(key);
        if (value == null) {
            return defaultValue;
        }
        return String.valueOf(value);
    }

    /**
     * 将 HTTP 响应转换为统一提交结果。
     *
     * @param response  HTTP 响应
     * @param requestId 本次提交请求 ID
     * @return 统一提交结果
     * @throws IOException 读取响应体失败时抛出
     */
    private AgentSubmitResult buildSubmitResult(ClientHttpResponse response, String requestId) throws IOException {
        int statusCode = response.getStatusCode().value();
        String responseText = StreamUtils.copyToString(response.getBody(), StandardCharsets.UTF_8);
        Map<String, Object> responseBody = parseResponseBody(responseText);
        String defaultMessage = StringUtils.hasText(responseText)
                ? responseText
                : response.getStatusCode().toString();
        return new AgentSubmitResult(
                response.getStatusCode().is2xxSuccessful(),
                getTextValue(responseBody, "message", defaultMessage),
                statusCode,
                getTextValue(responseBody, "requestId", requestId),
                getTextValue(responseBody, "externalId", null)
        );
    }

    /**
     * 解析响应文本为 Map。
     *
     * @param responseText 响应文本
     * @return 解析后的响应体
     */
    private Map<String, Object> parseResponseBody(String responseText) {
        if (!StringUtils.hasText(responseText)) {
            return Collections.emptyMap();
        }
        try {
            return objectMapper.readValue(responseText, new TypeReference<>() {
            });
        } catch (Exception ex) {
            return Map.of("message", responseText);
        }
    }

    /**
     * 提取异常可读信息。
     *
     * @param ex 异常对象
     * @return 异常消息，消息为空时返回异常类名
     */
    private String getExceptionMessage(Exception ex) {
        if (StringUtils.hasText(ex.getMessage())) {
            return ex.getMessage();
        }
        return ex.getClass().getSimpleName();
    }
}
