package cn.nianxx.thhotel.workflows.reservation.control;

import cn.nianxx.thhotel.integrations.ai.superagent.common.request.SuperAgentTaskResultSecurityRequest;
import cn.nianxx.thhotel.integrations.ai.superagent.service.SuperAgentTaskResultSecurityService;
import cn.nianxx.thhotel.integrations.ai.superagent.service.impl.SuperAgentTaskResultException;
import cn.nianxx.thhotel.integrations.ai.superagent.service.impl.SuperAgentTaskResultProperties;
import cn.nianxx.thhotel.workflows.reservation.common.request.ReservationAiCaseContextQueryRequest;
import cn.nianxx.thhotel.workflows.reservation.common.request.ReservationAiObjectDetailQueryRequest;
import cn.nianxx.thhotel.workflows.reservation.common.request.ReservationMessageConversationQueryRequest;
import cn.nianxx.thhotel.workflows.reservation.common.result.ReservationAiCaseContextResult;
import cn.nianxx.thhotel.workflows.reservation.common.result.ReservationAiObjectDetailResult;
import cn.nianxx.thhotel.workflows.reservation.common.result.ReservationAiQueryResponse;
import cn.nianxx.thhotel.workflows.reservation.common.result.ReservationMessageConversationMessagesResult;
import cn.nianxx.thhotel.workflows.reservation.common.result.ReservationMessageConversationTasksResult;
import cn.nianxx.thhotel.workflows.reservation.service.ReservationAiQueryService;
import cn.nianxx.thhotel.workflows.reservation.service.impl.ReservationAiQueryException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.nio.charset.StandardCharsets;
import java.util.List;
import org.springframework.http.HttpStatus;
import org.springframework.http.InvalidMediaTypeException;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * SuperAgent 只读查询 Controller。提供订单上下文和对象详情，不创建任务、不写 OPERA。
 */
@RestController
@RequestMapping("/api/ai-query/v1")
public class ReservationAiQueryController {

    private static final String CASE_CONTEXT_PATH = "/api/ai-query/v1/case-context";
    private static final String OBJECT_DETAIL_PATH = "/api/ai-query/v1/object-detail";
    private static final String MESSAGE_CONVERSATION_TASKS_PATH = "/api/ai-query/v1/message-conversation/tasks";
    private static final String MESSAGE_CONVERSATION_MESSAGES_PATH = "/api/ai-query/v1/message-conversation/messages";

    private final ReservationAiQueryService aiQueryService;
    private final SuperAgentTaskResultSecurityService securityService;
    private final SuperAgentTaskResultProperties securityProperties;
    private final ObjectMapper objectMapper;

    /**
     * 注入只读查询服务、安全服务和 JSON 解析器，Controller 负责先鉴权再解析请求。
     */
    public ReservationAiQueryController(
            ReservationAiQueryService aiQueryService,
            SuperAgentTaskResultSecurityService securityService,
            SuperAgentTaskResultProperties securityProperties,
            ObjectMapper objectMapper) {
        this.aiQueryService = aiQueryService;
        this.securityService = securityService;
        this.securityProperties = securityProperties;
        this.objectMapper = objectMapper;
    }

    /**
     * 查询订单路由上下文，返回匹配订单、未完成任务、终止记录和目标对象校验摘要。
     */
    @PostMapping(
            value = "/case-context",
            produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<ReservationAiQueryResponse<ReservationAiCaseContextResult>> queryCaseContext(
            @RequestBody(required = false) String rawBody,
            @RequestHeader(value = "Content-Type", required = false) String contentType,
            @RequestHeader(name = "X-TH-Hotel-SuperAgent-Client-Id", required = false) String clientId,
            @RequestHeader(name = "X-TH-Hotel-SuperAgent-Timestamp", required = false) String timestamp,
            @RequestHeader(name = "X-TH-Hotel-SuperAgent-Nonce", required = false) String nonce,
            @RequestHeader(name = "X-TH-Hotel-SuperAgent-Signature", required = false) String signature,
            @RequestHeader(value = "X-TH-Hotel-Request-Id", required = false) String requestId,
            @RequestHeader(value = "X-Request-Id", required = false) String legacyRequestId,
            @RequestHeader(value = "X-AI-Trace-Id", required = false) String traceId,
            @RequestHeader(value = "X-TH-Hotel-AI-Trace-Id", required = false) String thHotelTraceId) {
        String requestBody = rawBody == null ? "" : rawBody;
        verifyHmac(CASE_CONTEXT_PATH, clientId, timestamp, nonce, signature, requestBody);
        requireJsonContentType(contentType);
        ReservationAiCaseContextQueryRequest request = readBody(requestBody, ReservationAiCaseContextQueryRequest.class);
        ReservationAiCaseContextResult result = aiQueryService.queryCaseContext(request);
        return ResponseEntity.ok(ReservationAiQueryResponse.success(
                firstText(requestId, legacyRequestId),
                firstText(thHotelTraceId, traceId),
                result,
                List.of()));
    }

    /**
     * 查询对象详情。第一版只支持 ORDER:{orderId}，OPERA 投影缺口以 warning 表达。
     */
    @PostMapping(
            value = "/object-detail",
            produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<ReservationAiQueryResponse<ReservationAiObjectDetailResult>> queryObjectDetail(
            @RequestBody(required = false) String rawBody,
            @RequestHeader(value = "Content-Type", required = false) String contentType,
            @RequestHeader(name = "X-TH-Hotel-SuperAgent-Client-Id", required = false) String clientId,
            @RequestHeader(name = "X-TH-Hotel-SuperAgent-Timestamp", required = false) String timestamp,
            @RequestHeader(name = "X-TH-Hotel-SuperAgent-Nonce", required = false) String nonce,
            @RequestHeader(name = "X-TH-Hotel-SuperAgent-Signature", required = false) String signature,
            @RequestHeader(value = "X-TH-Hotel-Request-Id", required = false) String requestId,
            @RequestHeader(value = "X-Request-Id", required = false) String legacyRequestId,
            @RequestHeader(value = "X-AI-Trace-Id", required = false) String traceId,
            @RequestHeader(value = "X-TH-Hotel-AI-Trace-Id", required = false) String thHotelTraceId) {
        String requestBody = rawBody == null ? "" : rawBody;
        verifyHmac(OBJECT_DETAIL_PATH, clientId, timestamp, nonce, signature, requestBody);
        requireJsonContentType(contentType);
        ReservationAiObjectDetailQueryRequest request = readBody(requestBody, ReservationAiObjectDetailQueryRequest.class);
        ReservationAiObjectDetailResult result = aiQueryService.queryObjectDetail(request);
        return ResponseEntity.ok(ReservationAiQueryResponse.success(
                firstText(requestId, legacyRequestId),
                firstText(thHotelTraceId, traceId),
                result,
                List.of()));
    }

    /**
     * 查询邮件会话下全部任务，给 SuperAgent 在处理当前邮件前核对历史任务状态。
     */
    @PostMapping(
            value = "/message-conversation/tasks",
            produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<ReservationAiQueryResponse<ReservationMessageConversationTasksResult>>
            queryMessageConversationTasks(
                    @RequestBody(required = false) String rawBody,
                    @RequestHeader(value = "Content-Type", required = false) String contentType,
                    @RequestHeader(name = "X-TH-Hotel-SuperAgent-Client-Id", required = false) String clientId,
                    @RequestHeader(name = "X-TH-Hotel-SuperAgent-Timestamp", required = false) String timestamp,
                    @RequestHeader(name = "X-TH-Hotel-SuperAgent-Nonce", required = false) String nonce,
                    @RequestHeader(name = "X-TH-Hotel-SuperAgent-Signature", required = false) String signature,
                    @RequestHeader(value = "X-TH-Hotel-Request-Id", required = false) String requestId,
                    @RequestHeader(value = "X-Request-Id", required = false) String legacyRequestId,
                    @RequestHeader(value = "X-AI-Trace-Id", required = false) String traceId,
                    @RequestHeader(value = "X-TH-Hotel-AI-Trace-Id", required = false) String thHotelTraceId) {
        String requestBody = rawBody == null ? "" : rawBody;
        verifyHmac(MESSAGE_CONVERSATION_TASKS_PATH, clientId, timestamp, nonce, signature, requestBody);
        requireJsonContentType(contentType);
        ReservationMessageConversationQueryRequest request = readBody(
                requestBody,
                ReservationMessageConversationQueryRequest.class);
        ReservationMessageConversationTasksResult result = aiQueryService.queryMessageConversationTasks(request);
        return ResponseEntity.ok(ReservationAiQueryResponse.success(
                firstText(requestId, legacyRequestId),
                firstText(thHotelTraceId, traceId),
                result,
                List.of()));
    }

    /**
     * 查询邮件会话下全部受控正文，只返回清洗后的 HTML，不返回附件 URL 或原始 HTML。
     */
    @PostMapping(
            value = "/message-conversation/messages",
            produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<ReservationAiQueryResponse<ReservationMessageConversationMessagesResult>>
            queryMessageConversationMessages(
                    @RequestBody(required = false) String rawBody,
                    @RequestHeader(value = "Content-Type", required = false) String contentType,
                    @RequestHeader(name = "X-TH-Hotel-SuperAgent-Client-Id", required = false) String clientId,
                    @RequestHeader(name = "X-TH-Hotel-SuperAgent-Timestamp", required = false) String timestamp,
                    @RequestHeader(name = "X-TH-Hotel-SuperAgent-Nonce", required = false) String nonce,
                    @RequestHeader(name = "X-TH-Hotel-SuperAgent-Signature", required = false) String signature,
                    @RequestHeader(value = "X-TH-Hotel-Request-Id", required = false) String requestId,
                    @RequestHeader(value = "X-Request-Id", required = false) String legacyRequestId,
                    @RequestHeader(value = "X-AI-Trace-Id", required = false) String traceId,
                    @RequestHeader(value = "X-TH-Hotel-AI-Trace-Id", required = false) String thHotelTraceId) {
        String requestBody = rawBody == null ? "" : rawBody;
        verifyHmac(MESSAGE_CONVERSATION_MESSAGES_PATH, clientId, timestamp, nonce, signature, requestBody);
        requireJsonContentType(contentType);
        ReservationMessageConversationQueryRequest request = readBody(
                requestBody,
                ReservationMessageConversationQueryRequest.class);
        ReservationMessageConversationMessagesResult result = aiQueryService.queryMessageConversationMessages(request);
        return ResponseEntity.ok(ReservationAiQueryResponse.success(
                firstText(requestId, legacyRequestId),
                firstText(thHotelTraceId, traceId),
                result,
                List.of()));
    }

    /**
     * 使用任务结果接收接口同一套 HMAC 规则校验查询请求，校验通过后才允许解析业务 JSON。
     */
    private void verifyHmac(
            String requestPath,
            String clientId,
            String timestamp,
            String nonce,
            String signature,
            String requestBody) {
        rejectBodyWhenTooLarge(requestBody);
        securityService.verify(new SuperAgentTaskResultSecurityRequest(
                "POST",
                requestPath,
                clientId,
                timestamp,
                nonce,
                signature,
                requestBody
        ));
    }

    /**
     * 限制查询接口请求体大小，避免鉴权前后处理超大外部输入。
     */
    private void rejectBodyWhenTooLarge(String rawBody) {
        long maxBodyBytes = securityProperties.getMaxBodyBytes();
        int actualBytes = rawBody.getBytes(StandardCharsets.UTF_8).length;
        if (maxBodyBytes >= 0 && actualBytes > maxBodyBytes) {
            throw new SuperAgentTaskResultException(
                    HttpStatus.PAYLOAD_TOO_LARGE,
                    "REQUEST_BODY_TOO_LARGE",
                    "请求体超过允许大小。");
        }
    }

    /**
     * 手动校验 JSON Content-Type，确保协议错误也使用查询接口统一错误包。
     */
    private void requireJsonContentType(String contentType) {
        String text = firstText(contentType, null);
        if (text == null) {
            throw unsupportedContentType();
        }
        try {
            MediaType mediaType = MediaType.parseMediaType(text);
            if (!MediaType.APPLICATION_JSON.isCompatibleWith(mediaType)) {
                throw unsupportedContentType();
            }
        } catch (InvalidMediaTypeException exception) {
            throw unsupportedContentType();
        }
    }

    /**
     * 构造 Content-Type 错误，避免外部协议错误落到 Spring 默认错误结构。
     */
    private ReservationAiQueryException unsupportedContentType() {
        return new ReservationAiQueryException(
                HttpStatus.UNSUPPORTED_MEDIA_TYPE,
                "REQUEST_CONTENT_TYPE_UNSUPPORTED",
                "Content-Type 必须是 application/json。");
    }

    /**
     * 解析已通过鉴权的 JSON 请求体，失败时返回查询接口统一错误响应。
     */
    private <T> T readBody(String rawBody, Class<T> requestType) {
        try {
            return objectMapper.readValue(rawBody, requestType);
        } catch (JsonProcessingException exception) {
            throw new ReservationAiQueryException(
                    HttpStatus.BAD_REQUEST,
                    "REQUEST_BODY_INVALID",
                    "请求体 JSON 不合法。");
        }
    }

    /**
     * 取第一个非空白文本，兼容新旧追踪 Header。
     */
    private String firstText(String first, String second) {
        if (first != null && !first.isBlank()) {
            return first;
        }
        if (second != null && !second.isBlank()) {
            return second;
        }
        return null;
    }
}
