package cn.nianxx.thhotel.integrations.ai.superagent.service.impl;

import cn.nianxx.thhotel.integrations.ai.superagent.common.request.SuperAgentMailDebugRequest;
import cn.nianxx.thhotel.integrations.ai.superagent.common.request.SuperAgentOpenApiMessageRequest;
import cn.nianxx.thhotel.integrations.ai.superagent.common.result.SuperAgentOpenApiResult;
import cn.nianxx.thhotel.integrations.ai.superagent.common.result.SuperAgentOpenApiTraceEvent;
import cn.nianxx.thhotel.integrations.ai.superagent.service.SuperAgentOpenApiClient;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.Base64;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.Consumer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * SuperAgent Open API HTTP 客户端实现。负责创建 session、发送 SSE 消息和解析最终回答。
 */
@Service
public class SuperAgentOpenApiClientImpl implements SuperAgentOpenApiClient {

    private static final String CSRF_HEADER_NAME = "X-CSRF-Token";
    private static final String CSRF_COOKIE_NAME = "csrf_token";
    private static final int CSRF_TOKEN_BYTES = 32;
    private static final SecureRandom CSRF_RANDOM = new SecureRandom();

    private final SuperAgentOpenApiProperties properties;
    private final SuperAgentOpenApiSseParser sseParser;
    private final ObjectMapper objectMapper;
    private final HttpClient injectedHttpClient;

    /**
     * 注入 SuperAgent 配置、SSE 解析器和 JSON 工具。
     */
    @Autowired
    public SuperAgentOpenApiClientImpl(
            SuperAgentOpenApiProperties properties,
            SuperAgentOpenApiSseParser sseParser,
            ObjectMapper objectMapper) {
        this(properties, sseParser, objectMapper, null);
    }

    /**
     * 注入可替换的 HTTP 客户端，供单元测试验证低层 HTTP 边界行为。
     */
    SuperAgentOpenApiClientImpl(
            SuperAgentOpenApiProperties properties,
            SuperAgentOpenApiSseParser sseParser,
            ObjectMapper objectMapper,
            HttpClient injectedHttpClient) {
        this.properties = properties;
        this.sseParser = sseParser;
        this.objectMapper = objectMapper;
        this.injectedHttpClient = injectedHttpClient;
    }

    /**
     * 调用 SuperAgent Open API 邮件 Debug 流程；配置缺失时直接失败，避免静默跳过真实调用。
     */
    @Override
    public SuperAgentOpenApiResult invokeMailDebug(
            SuperAgentMailDebugRequest request,
            Consumer<SuperAgentOpenApiTraceEvent> traceConsumer) {
        return invokeMessage(new SuperAgentOpenApiMessageRequest(
                request.message(),
                request.idempotencyKey(),
                properties.getExternalSubjectId(),
                request.idempotencyKey(),
                request.metadata()), traceConsumer);
    }

    /**
     * 调用 SuperAgent Open API 通用消息流程，严格校验 SSE 终止事件并支持断流恢复。
     */
    @Override
    public SuperAgentOpenApiResult invokeMessage(
            SuperAgentOpenApiMessageRequest request,
            Consumer<SuperAgentOpenApiTraceEvent> traceConsumer) {
        validateProperties();
        try {
            HttpClient httpClient = httpClient();
            URI baseUri = baseUri();
            String sessionId = createSession(httpClient, baseUri, request);
            return sendMessage(httpClient, baseUri, sessionId, request, traceConsumer);
        } catch (SuperAgentOpenApiException exception) {
            throw exception;
        } catch (Exception exception) {
            throw new SuperAgentOpenApiException("SuperAgent Open API 调用失败。", exception);
        }
    }

    /**
     * 创建 SuperAgent session。
     */
    private String createSession(HttpClient httpClient, URI baseUri, SuperAgentOpenApiMessageRequest request)
            throws Exception {
        Map<String, Object> body = new LinkedHashMap<>();
        body.put("external_subject_id", textOrDefault(request.externalSubjectId(), properties.getExternalSubjectId()));
        body.put("idempotency_key", request.idempotencyKey() + "-session");
        body.put("metadata", request.metadata());
        HttpRequest httpRequest = baseRequest(baseUri.resolve("/api/open/agent-sessions"), request.requestId())
                .header("Content-Type", "application/json")
                .header("Accept", "application/json")
                .POST(HttpRequest.BodyPublishers.ofString(objectMapper.writeValueAsString(body), StandardCharsets.UTF_8))
                .build();
        HttpResponse<String> httpResponse = httpClient.send(
                httpRequest,
                HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
        require2xx(httpResponse.statusCode(), httpResponse.body());
        String response = httpResponse.body();
        JsonNode json = objectMapper.readTree(response == null ? "{}" : response);
        String sessionId = text(json, "session_id");
        if (sessionId == null) {
            sessionId = text(json, "id");
        }
        if (sessionId == null) {
            throw new SuperAgentOpenApiException("SuperAgent 创建 session 响应缺少 session_id。");
        }
        return sessionId;
    }

    /**
     * 发送 Debug 邮件消息并流式解析 SSE 文本响应。
     */
    private SuperAgentOpenApiResult sendMessage(
            HttpClient httpClient,
            URI baseUri,
            String sessionId,
            SuperAgentOpenApiMessageRequest request,
            Consumer<SuperAgentOpenApiTraceEvent> traceConsumer) throws Exception {
        Map<String, Object> body = new LinkedHashMap<>();
        body.put("message", request.message());
        body.put("idempotency_key", request.idempotencyKey() + "-message");
        body.put("metadata", request.metadata());
        URI streamUri = baseUri.resolve("/api/open/agent-sessions/" + sessionId + "/messages/stream?include_trace=true");
        HttpRequest httpRequest = baseRequest(streamUri, request.requestId())
                .header("Content-Type", "application/json")
                .header("Accept", "text/event-stream")
                .POST(HttpRequest.BodyPublishers.ofString(objectMapper.writeValueAsString(body), StandardCharsets.UTF_8))
                .build();
        HttpResponse<InputStream> httpResponse = httpClient.send(
                httpRequest,
                HttpResponse.BodyHandlers.ofInputStream());
        require2xxOrClose(httpResponse.statusCode(), httpResponse.body());
        SuperAgentOpenApiSseParser.ParsedState state = sseParser.newState(sessionId);
        applyRunLocation(baseUri, httpResponse, state);
        try {
            consumeResponse(httpResponse.body(), state, traceConsumer);
        } catch (SuperAgentOpenApiException exception) {
            if (!state.endSeen() && state.runUri() != null) {
                recover(httpClient, request.requestId(), state, traceConsumer);
            } else {
                throw exception;
            }
        }
        return sseParser.requireSuccessfulResult(state);
    }

    /**
     * 校验 SuperAgent Open API 必要配置。
     */
    private void validateProperties() {
        if (!properties.isEnabled()) {
            throw new SuperAgentOpenApiException("SuperAgent Open API 未启用。");
        }
        if (blank(properties.getBaseUrl()) || blank(properties.getApiKey()) || blank(properties.getExternalSubjectId())) {
            throw new SuperAgentOpenApiException("SuperAgent Open API 配置不完整。");
        }
    }

    /**
     * 构造 JDK HttpClient；SSE 请求不设置整体 read timeout，避免长任务被固定超时截断。
     */
    private HttpClient httpClient() {
        if (injectedHttpClient != null) {
            return injectedHttpClient;
        }
        return HttpClient.newBuilder()
                .connectTimeout(properties.getConnectTimeout())
                .version(HttpClient.Version.HTTP_1_1)
                .build();
    }

    /**
     * 解析基础 URI，统一处理末尾斜杠。
     */
    private URI baseUri() {
        String normalizedBaseUrl = properties.getBaseUrl().endsWith("/")
                ? properties.getBaseUrl()
                : properties.getBaseUrl() + "/";
        return URI.create(normalizedBaseUrl);
    }

    /**
     * 构造 SuperAgent Open API 通用请求头，不记录或返回 API Key。
     */
    private HttpRequest.Builder baseRequest(URI uri, String requestId) {
        String csrfToken = newCsrfToken();
        HttpRequest.Builder builder = HttpRequest.newBuilder(uri)
                .header("Authorization", "Bearer " + properties.getApiKey())
                .header("Cache-Control", "no-cache")
                .header(CSRF_HEADER_NAME, csrfToken)
                .header("Cookie", CSRF_COOKIE_NAME + "=" + csrfToken);
        if (!blank(requestId)) {
            builder.header("X-Request-ID", requestId);
        }
        return builder;
    }

    /**
     * 生成临时 CSRF double-submit token。该值只用于当前 HTTP 请求，不作为长期 Secret 保存或输出。
     */
    private String newCsrfToken() {
        byte[] bytes = new byte[CSRF_TOKEN_BYTES];
        CSRF_RANDOM.nextBytes(bytes);
        return Base64.getUrlEncoder().withoutPadding().encodeToString(bytes);
    }

    /**
     * 校验 HTTP 状态码，错误摘要不包含 Secret。
     */
    private void require2xx(int statusCode, String responseBody) {
        if (statusCode < 200 || statusCode >= 300) {
            String safeBody = responseBody == null ? "" : responseBody;
            if (safeBody.length() > 512) {
                safeBody = safeBody.substring(0, 512);
            }
            throw new SuperAgentOpenApiException("SuperAgent Open API HTTP 调用失败，status=" + statusCode + "，body=" + safeBody);
        }
    }

    /**
     * 校验流式响应状态码；失败时必须关闭响应流，避免连接资源泄漏。
     */
    private void require2xxOrClose(int statusCode, InputStream responseBody) {
        if (statusCode >= 200 && statusCode < 300) {
            return;
        }
        closeQuietly(responseBody);
        require2xx(statusCode, null);
    }

    /**
     * 静默关闭错误响应流；关闭异常不覆盖原始 HTTP 状态错误。
     */
    private void closeQuietly(InputStream responseBody) {
        if (responseBody == null) {
            return;
        }
        try {
            responseBody.close();
        } catch (IOException exception) {
            // 错误响应流关闭失败不覆盖原始 HTTP 状态错误。
        }
    }

    /**
     * 从 Content-Location 保存 run URI 和 runId，供 EOF 后恢复使用。
     */
    private void applyRunLocation(
            URI baseUri,
            HttpResponse<?> response,
            SuperAgentOpenApiSseParser.ParsedState state) {
        String contentLocation = response.headers().firstValue("Content-Location").orElse(null);
        if (blank(contentLocation)) {
            return;
        }
        URI runUri = baseUri.resolve(contentLocation);
        state.runUri(runUri.toString());
        String path = runUri.getPath();
        int markerIndex = path.lastIndexOf("/runs/");
        if (markerIndex >= 0) {
            state.runId(path.substring(markerIndex + "/runs/".length()));
        }
    }

    /**
     * 消费一段 SSE 响应流，读取完成前不会把部分回答视为成功。
     */
    private void consumeResponse(
            InputStream body,
            SuperAgentOpenApiSseParser.ParsedState state,
            Consumer<SuperAgentOpenApiTraceEvent> traceConsumer) {
        try (InputStreamReader reader = new InputStreamReader(body, StandardCharsets.UTF_8)) {
            sseParser.consume(state, reader, traceConsumer);
        } catch (IOException exception) {
            throw new SuperAgentOpenApiException("SuperAgent SSE 读取失败。", exception);
        }
    }

    /**
     * EOF 后通过已有 run 的 events 接口恢复，不重新 POST 初始消息。
     */
    private void recover(
            HttpClient httpClient,
            String requestId,
            SuperAgentOpenApiSseParser.ParsedState state,
            Consumer<SuperAgentOpenApiTraceEvent> traceConsumer) throws Exception {
        int maxAttempts = Math.max(0, properties.getSseRecoveryMaxAttempts());
        SuperAgentOpenApiException lastFailure = null;
        for (int attempt = 1; attempt <= maxAttempts; attempt++) {
            String runStatus = queryRunStatus(httpClient, URI.create(state.runUri()), requestId);
            if ("error".equals(runStatus) || "timeout".equals(runStatus) || "interrupted".equals(runStatus)) {
                throw new SuperAgentOpenApiException("SuperAgent run 已进入失败终态，runId=" + state.runId() + "，status=" + runStatus);
            }
            try {
                subscribeRunEvents(httpClient, URI.create(state.runUri() + "/events"), requestId, state, traceConsumer);
                if (state.endSeen()) {
                    return;
                }
            } catch (SuperAgentOpenApiException exception) {
                lastFailure = exception;
            }
        }
        throw new SuperAgentOpenApiException("SuperAgent SSE 断流恢复耗尽，runId=" + state.runId(), lastFailure);
    }

    /**
     * 查询 SuperAgent run 当前状态。
     */
    private String queryRunStatus(HttpClient httpClient, URI runUri, String requestId) throws Exception {
        HttpRequest httpRequest = baseRequest(runUri, requestId)
                .header("Accept", "application/json")
                .GET()
                .build();
        HttpResponse<String> response = httpClient.send(
                httpRequest,
                HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
        require2xx(response.statusCode(), response.body());
        JsonNode json = objectMapper.readTree(response.body() == null ? "{}" : response.body());
        return text(json, "status", "running");
    }

    /**
     * 订阅已有 run 的公开 events，并携带 Last-Event-ID 去重恢复。
     */
    private void subscribeRunEvents(
            HttpClient httpClient,
            URI eventsUri,
            String requestId,
            SuperAgentOpenApiSseParser.ParsedState state,
            Consumer<SuperAgentOpenApiTraceEvent> traceConsumer) throws Exception {
        HttpRequest.Builder builder = baseRequest(eventsUri, requestId)
                .header("Accept", "text/event-stream")
                .GET();
        if (!blank(state.lastEventId())) {
            builder.header("Last-Event-ID", state.lastEventId());
        }
        HttpResponse<InputStream> response = httpClient.send(
                builder.build(),
                HttpResponse.BodyHandlers.ofInputStream());
        require2xxOrClose(response.statusCode(), response.body());
        consumeResponse(response.body(), state, traceConsumer);
    }

    /**
     * 读取 JSON 文本字段。
     */
    private String text(JsonNode json, String fieldName) {
        JsonNode value = json.path(fieldName);
        return value.isMissingNode() || value.isNull() || value.asText().isBlank() ? null : value.asText();
    }

    /**
     * 读取 JSON 文本字段，缺失时返回兜底值。
     */
    private String text(JsonNode json, String fieldName, String fallback) {
        String value = text(json, fieldName);
        return value == null ? fallback : value;
    }

    /**
     * 判断字符串是否为空白。
     */
    private boolean blank(String value) {
        return value == null || value.isBlank();
    }

    /**
     * 返回非空文本，否则使用兜底值。
     */
    private String textOrDefault(String value, String fallback) {
        return blank(value) ? fallback : value;
    }
}
