package com.huimv.employment.service.conversation;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.huimv.employment.dao.entity.FeConversation;
import com.huimv.employment.integration.kb.ConsoleChatCollectResult;
import com.huimv.employment.integration.kb.ConsoleChatSseAggregator;
import com.huimv.employment.integration.kb.KbOpenAiProxyService;
import com.huimv.employment.integration.kb.SseStreamSupport;
import com.huimv.employment.service.conversation.dto.ConversationChatRequest;
import com.huimv.employment.service.conversation.dto.ParsedAssistantReply;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.io.OutputStream;

/**
 * 会话内 AI 对话：流式转发；满足条件时在 SSE 最后一条消息注入 next_action 后落库。
 * <p>当回复含 MCP__generate_initial_draft / MCP__update_draft 响应且 draft 已就绪时，追加 show_employment_draft（无 card_payload）。</p>
 */
@Service
public class ConversationChatService {

    private static final Logger log = LoggerFactory.getLogger(ConversationChatService.class);

    private final ConversationService conversationService;
    private final ConversationMessageService conversationMessageService;
    private final ConversationIntentService conversationIntentService;
    private final ConversationDraftCardEnricher conversationDraftCardEnricher;
    private final KbOpenAiProxyService kbOpenAiProxyService;
    private final ObjectMapper objectMapper;

    public ConversationChatService(ConversationService conversationService,
                                   ConversationMessageService conversationMessageService,
                                   ConversationIntentService conversationIntentService,
                                   ConversationDraftCardEnricher conversationDraftCardEnricher,
                                   KbOpenAiProxyService kbOpenAiProxyService,
                                   ObjectMapper objectMapper) {
        this.conversationService = conversationService;
        this.conversationMessageService = conversationMessageService;
        this.conversationIntentService = conversationIntentService;
        this.conversationDraftCardEnricher = conversationDraftCardEnricher;
        this.kbOpenAiProxyService = kbOpenAiProxyService;
        this.objectMapper = objectMapper;
    }

    public void chat(Long userId, Long conversationId, ConversationChatRequest request, String agentId,
                     OutputStream output) throws IOException {
        FeConversation conversation = conversationService.requireOwnedActive(userId, conversationId);
        String userText = request.getMessage().trim();
        byte[] upstreamBody = buildUpstreamBody(conversation, userId, userText);

        boolean firstUserMessage = conversationMessageService.isFirstUserMessage(conversation.getId());
        conversationMessageService.saveUserMessage(conversation.getId(), userText, !firstUserMessage);
        if (firstUserMessage) {
            conversationIntentService.classifyAndUpdateTitleAsync(
                    conversation.getConversationNo(), userText, agentId);
        }

        ConsoleChatCollectResult collectResult = kbOpenAiProxyService.streamConsoleChatCollecting(
                upstreamBody, agentId, output, objectMapper);

        log.info("SSE 收集完成 conversationId={} mcpDraftId={} evidenceLen={} held={}",
                conversation.getId(),
                collectResult.getMcpDraftId(),
                collectResult.getMcpDraftEvidence() != null ? collectResult.getMcpDraftEvidence().length() : 0,
                StringUtils.hasText(collectResult.getHeldTerminalLine()));

        ParsedAssistantReply parsed = conversationDraftCardEnricher.attachCardOnComplete(
                conversation.getId(),
                collectResult.getAssistantText(),
                collectResult.getHeldTerminalLine(),
                collectResult.getMcpDraftEvidence(),
                collectResult.getMcpDraftId());

        // 把卡片字段写入「最后一条」SSE 消息本体后再发 [DONE]
        writeLastSseMessageWithCard(output, collectResult.getHeldTerminalLine(), parsed);
        SseStreamSupport.writeDoneEvent(output);

        if (!StringUtils.hasText(parsed.getDisplayText()) && !parsed.hasCard()) {
            log.warn("assistant 回复为空，未写入 fe_conversation_message conversationId={}",
                    conversation.getId());
        } else if (parsed.hasCard()) {
            log.info("SSE 最后消息已注入卡片 conversationId={} relatedDraftId={}",
                    conversation.getId(), parsed.getRelatedDraftId());
        }
        conversationMessageService.saveAssistantMessage(conversation.getId(), parsed);
    }

    /**
     * 在最后一条 SSE 消息 JSON 上追加 content_type / next_action / card_payload。
     */
    private void writeLastSseMessageWithCard(OutputStream output,
                                             String heldTerminalLine,
                                             ParsedAssistantReply parsed) throws IOException {
        if (output == null) {
            return;
        }
        if (!parsed.hasCard()) {
            // 无卡片也要把 hold 的最后消息发出，避免客户端永远收不到完结消息
            if (StringUtils.hasText(heldTerminalLine)) {
                SseStreamSupport.writeRawSseLine(output, heldTerminalLine);
            }
            return;
        }

        ObjectNode msg = buildEnrichedLastMessage(heldTerminalLine, parsed);
        String json = objectMapper.writeValueAsString(msg);
        SseStreamSupport.writeDataEvent(output, json);
        log.info("SSE 最后消息已写出 next_action={} content_type={} relatedDraftId={}",
                msg.path("next_action").asText(),
                msg.path("content_type").asText(),
                parsed.getRelatedDraftId());
    }

    private ObjectNode buildEnrichedLastMessage(String heldTerminalLine, ParsedAssistantReply parsed)
            throws IOException {
        ObjectNode msg;
        if (StringUtils.hasText(heldTerminalLine)) {
            String payload = ConsoleChatSseAggregator.normalizeDataPayload(heldTerminalLine.trim());
            JsonNode node = objectMapper.readTree(payload);
            if (node != null && node.isObject()) {
                msg = (ObjectNode) node;
            } else {
                msg = objectMapper.createObjectNode();
                msg.put("object", "message");
                msg.put("type", "message");
                msg.put("role", "assistant");
                msg.put("status", "completed");
            }
        } else {
            msg = objectMapper.createObjectNode();
            msg.put("object", "message");
            msg.put("type", "message");
            msg.put("role", "assistant");
            msg.put("status", "completed");
            ArrayNode content = msg.putArray("content");
            ObjectNode textPart = content.addObject();
            textPart.put("type", "text");
            textPart.put("text", parsed.getDisplayText() != null ? parsed.getDisplayText() : "");
        }

        // 强制覆盖/追加卡片字段（不用智能体返回）
        msg.put("content_type", parsed.getContentType());
        msg.put("next_action", parsed.getNextAction());
        if (parsed.getRelatedDraftId() != null) {
            msg.put("related_draft_id", parsed.getRelatedDraftId());
        }
        if (StringUtils.hasText(parsed.getCardPayload())) {
            msg.set("card_payload", objectMapper.readTree(parsed.getCardPayload()));
        }
        if (!msg.has("status") || msg.get("status").asText("").isEmpty()) {
            msg.put("status", "completed");
        }
        return msg;
    }

    private byte[] buildUpstreamBody(FeConversation conversation, Long userId, String userText) throws IOException {
        ObjectNode root = objectMapper.createObjectNode();
        ArrayNode input = objectMapper.createArrayNode();
        ObjectNode userTurn = objectMapper.createObjectNode();
        userTurn.put("role", "user");
        ArrayNode content = objectMapper.createArrayNode();
        ObjectNode textPart = objectMapper.createObjectNode();
        textPart.put("type", "text");
        textPart.put("text", userText);
        content.add(textPart);
        userTurn.set("content", content);
        input.add(userTurn);
        root.set("input", input);
        root.put("order_id", conversation.getConversationNo());
        root.put("session_id", conversation.getConversationNo());
        root.put("user_id", String.valueOf(userId));
        root.put("channel", "console");
        return objectMapper.writeValueAsBytes(root);
    }
}
