package com.huimv.employment.service.conversation;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.huimv.employment.dao.entity.FeEmploymentDraft;
import com.huimv.employment.dao.mapper.FeEmploymentDraftMapper;
import com.huimv.employment.service.conversation.dto.ParsedAssistantReply;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * SSE 最后消息时，由服务端按 MCP 草稿工具响应注入前端动作（不依赖智能体拼卡片）。
 * <p>
 * 当本轮 SSE 含 {@code MCP__generate_initial_draft} / {@code MCP__update_draft} 响应
 * 且存在非空 {@code draft_id}，且该草稿 {@code missing_fields} 为空时，
 * 追加 {@code next_action=show_employment_draft}，不注入 {@code card_payload}
 * （前端按 {@code related_draft_id} 自行拉详情）。
 * </p>
 * <p>
 * 注意：MCP 工具名与 draft_id 通常出现在 plugin_call_output，不在 assistant 展示文案中，
 * 须结合 SSE 聚合得到的 mcpEvidence / mcpDraftId。
 * </p>
 */
@Component
public class ConversationDraftCardEnricher {

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

    private static final String DB_STATUS_PENDING = "pending";

    private static final Pattern FENCED_CARD_BLOCK = Pattern.compile(
            "```(?:fe-card|json)?\\s*[\\s\\S]*?```",
            Pattern.CASE_INSENSITIVE);

    /** 识别 MCP 草稿工具调用痕迹（generate_initial_draft / update_draft） */
    private static final Pattern MCP_DRAFT_HINT = Pattern.compile(
            "MCP__generate_initial_draft|MCP__update_draft",
            Pattern.CASE_INSENSITIVE);

    /** 从文本中提取 draft_id（取最后一个非空匹配） */
    private static final Pattern DRAFT_ID_PATTERN = Pattern.compile(
            "\"draft_id\"\\s*:\\s*\"?(\\d+)\"?",
            Pattern.CASE_INSENSITIVE);

    private final FeEmploymentDraftMapper feEmploymentDraftMapper;
    private final ObjectMapper objectMapper;

    public ConversationDraftCardEnricher(FeEmploymentDraftMapper feEmploymentDraftMapper,
                                         ObjectMapper objectMapper) {
        this.feEmploymentDraftMapper = feEmploymentDraftMapper;
        this.objectMapper = objectMapper;
    }

    /**
     * 清理展示文案；若本轮 MCP 草稿响应就绪则追加 show_employment_draft（无 card_payload）。
     *
     * @param conversationId 会话 ID
     * @param assistantText  展示用助手文本
     * @param heldTerminalLine 暂存的最后一条终端 SSE 行
     * @param mcpDraftEvidence 流中 MCP 草稿工具相关 JSON 片段
     * @param mcpDraftId       聚合器已解析的 draft_id（可为 null）
     */
    public ParsedAssistantReply attachCardOnComplete(Long conversationId,
                                                     String assistantText,
                                                     String heldTerminalLine,
                                                     String mcpDraftEvidence,
                                                     Long mcpDraftId) {
        String displayText = sanitizeDisplayText(assistantText);
        ParsedAssistantReply parsed = ParsedAssistantReply.textOnly(displayText);
        if (conversationId == null) {
            log.warn("SSE 注入卡片跳过：conversationId 为空");
            return parsed;
        }

        String scanText = joinScanText(assistantText, heldTerminalLine, mcpDraftEvidence);
        if (!hasMcpDraftHint(scanText) && mcpDraftId == null) {
            log.info("SSE 未追加草稿动作：本轮无 MCP__generate_initial_draft/MCP__update_draft 痕迹 conversationId={}",
                    conversationId);
            return parsed;
        }

        Long draftId = mcpDraftId;
        if (draftId == null) {
            draftId = extractDraftIdFromMcpResponse(scanText);
        }
        if (draftId == null) {
            // MCP 已调用但响应结构异常时，回退查本会话就绪草稿
            draftId = findReadyPendingDraftId(conversationId);
            if (draftId != null) {
                log.info("SSE 从会话就绪草稿回退 draft_id={} conversationId={}", draftId, conversationId);
            }
        }
        if (draftId == null) {
            log.info("SSE 未追加草稿动作：未能提取 draft_id conversationId={} evidenceLen={}",
                    conversationId, scanText.length());
            return parsed;
        }

        FeEmploymentDraft draft = feEmploymentDraftMapper.selectById(draftId);
        if (draft == null) {
            log.info("SSE 未追加草稿动作：draft_id={} 不存在 conversationId={}", draftId, conversationId);
            return parsed;
        }
        if (!conversationId.equals(draft.getConversationId())) {
            log.info("SSE 未追加草稿动作：draft_id={} 不属于会话 {}", draftId, conversationId);
            return parsed;
        }
        if (!DB_STATUS_PENDING.equals(draft.getStatus())) {
            log.info("SSE 未追加草稿动作：draft_id={} status={} 非 pending", draftId, draft.getStatus());
            return parsed;
        }
        List<String> missing = parseMissingFields(draft.getMissingFields());
        if (!missing.isEmpty()) {
            log.info("SSE 未追加草稿动作：draft_id={} missing_fields 非空 {}", draftId, missing);
            return parsed;
        }

        log.info("SSE 追加 show_employment_draft conversationId={} draftId={}（无 card_payload）",
                conversationId, draftId);
        return parsed.withCard(
                MessageCardConstants.CONTENT_TYPE_CARD_DRAFT,
                MessageCardConstants.NEXT_ACTION_SHOW_EMPLOYMENT_DRAFT,
                null,
                draftId);
    }

    /** 兼容旧调用：仅展示文案（通常无法命中 MCP 痕迹）。 */
    public ParsedAssistantReply attachCardOnComplete(Long conversationId, String assistantText) {
        return attachCardOnComplete(conversationId, assistantText, null, null, null);
    }

    private Long findReadyPendingDraftId(Long conversationId) {
        FeEmploymentDraft draft = feEmploymentDraftMapper.selectOne(new LambdaQueryWrapper<FeEmploymentDraft>()
                .eq(FeEmploymentDraft::getConversationId, conversationId)
                .eq(FeEmploymentDraft::getStatus, DB_STATUS_PENDING)
                .orderByDesc(FeEmploymentDraft::getUpdateTime)
                .last("LIMIT 1"));
        if (draft == null) {
            return null;
        }
        List<String> missing = parseMissingFields(draft.getMissingFields());
        return missing.isEmpty() ? draft.getId() : null;
    }

    private static String joinScanText(String... parts) {
        StringBuilder sb = new StringBuilder();
        if (parts == null) {
            return "";
        }
        for (String part : parts) {
            if (!StringUtils.hasText(part)) {
                continue;
            }
            if (sb.length() > 0) {
                sb.append('\n');
            }
            sb.append(part);
        }
        return sb.toString();
    }

    private static boolean hasMcpDraftHint(String text) {
        return StringUtils.hasText(text) && MCP_DRAFT_HINT.matcher(text).find();
    }

    /**
     * 从文本中识别 MCP 草稿工具响应并提取 draft_id。
     */
    Long extractDraftIdFromMcpResponse(String rawText) {
        if (!StringUtils.hasText(rawText)) {
            return null;
        }
        if (!MCP_DRAFT_HINT.matcher(rawText).find()) {
            return null;
        }
        Long fromJson = extractDraftIdFromJsonBlobs(rawText);
        if (fromJson != null) {
            return fromJson;
        }
        Matcher matcher = DRAFT_ID_PATTERN.matcher(rawText);
        Long last = null;
        while (matcher.find()) {
            try {
                last = Long.parseLong(matcher.group(1));
            } catch (NumberFormatException ignored) {
                // skip
            }
        }
        return last;
    }

    private Long extractDraftIdFromJsonBlobs(String text) {
        Long last = null;
        int i = 0;
        while (i < text.length()) {
            int start = text.indexOf('{', i);
            if (start < 0) {
                break;
            }
            int end = findMatchingBrace(text, start);
            if (end < 0) {
                break;
            }
            String candidate = text.substring(start, end + 1);
            try {
                JsonNode root = objectMapper.readTree(candidate);
                Long id = findDraftIdDeep(root);
                if (id != null) {
                    last = id;
                }
            } catch (Exception ignored) {
                // 非完整 JSON，跳过
            }
            i = end + 1;
        }
        return last;
    }

    private static Long findDraftIdDeep(JsonNode node) {
        if (node == null || node.isNull()) {
            return null;
        }
        if (node.isObject()) {
            Long direct = asLongId(node.get("draft_id"));
            if (direct == null) {
                direct = asLongId(node.get("draftId"));
            }
            if (direct != null) {
                return direct;
            }
            Long last = null;
            Iterator<Map.Entry<String, JsonNode>> fields = node.fields();
            while (fields.hasNext()) {
                Map.Entry<String, JsonNode> entry = fields.next();
                Long nested = findDraftIdDeep(entry.getValue());
                if (nested != null) {
                    last = nested;
                }
            }
            return last;
        }
        if (node.isArray()) {
            Long last = null;
            for (JsonNode child : node) {
                Long nested = findDraftIdDeep(child);
                if (nested != null) {
                    last = nested;
                }
            }
            return last;
        }
        return null;
    }

    private static Long asLongId(JsonNode node) {
        if (node == null || node.isNull()) {
            return null;
        }
        if (node.isNumber()) {
            long value = node.asLong();
            return value > 0 ? value : null;
        }
        if (node.isTextual()) {
            String text = node.asText().trim();
            if (!StringUtils.hasText(text)) {
                return null;
            }
            try {
                long value = Long.parseLong(text);
                return value > 0 ? value : null;
            } catch (NumberFormatException ex) {
                return null;
            }
        }
        return null;
    }

    private static int findMatchingBrace(String text, int start) {
        int depth = 0;
        boolean inString = false;
        boolean escape = false;
        for (int i = start; i < text.length(); i++) {
            char c = text.charAt(i);
            if (inString) {
                if (escape) {
                    escape = false;
                } else if (c == '\\') {
                    escape = true;
                } else if (c == '"') {
                    inString = false;
                }
                continue;
            }
            if (c == '"') {
                inString = true;
            } else if (c == '{') {
                depth++;
            } else if (c == '}') {
                depth--;
                if (depth == 0) {
                    return i;
                }
            }
        }
        return -1;
    }

    private String sanitizeDisplayText(String raw) {
        if (!StringUtils.hasText(raw)) {
            return "";
        }
        String cleaned = FENCED_CARD_BLOCK.matcher(raw).replaceAll("").trim();
        return StringUtils.hasText(cleaned) ? cleaned : raw.trim();
    }

    private List<String> parseMissingFields(String json) {
        if (!StringUtils.hasText(json)) {
            return Collections.emptyList();
        }
        try {
            List<String> parsed = objectMapper.readValue(json.trim(), new TypeReference<List<String>>() {
            });
            return parsed != null ? parsed : Collections.emptyList();
        } catch (Exception ex) {
            log.warn("解析 missing_fields 失败，视为非空，跳过注入: {}", ex.getMessage());
            return Collections.singletonList("_parse_error");
        }
    }
}
