package com.lingyue.graph.listener;

import com.lingyue.common.event.DocumentParsedEvent;
import com.lingyue.graph.service.GraphNerService;
import com.lingyue.graph.service.NerToBlockService;
import com.lingyue.graph.service.NerToBlockService.TextElementDTO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.event.EventListener;
import org.springframework.http.*;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

import java.util.*;

/**
 * 文档解析完成事件监听器
 * 监听文档解析完成事件，自动触发 NER 提取并保存到图数据库
 * 
 * 2026-01-21 更新：增加将 NER 结果转换为 TextElement 的能力
 *
 * @author lingyue
 * @since 2026-01-19
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class DocumentParsedEventListener {

    private final GraphNerService graphNerService;
    private final NerToBlockService nerToBlockService;
    private final RestTemplate restTemplate;

    @Value("${ner.auto-extract.enabled:true}")
    private boolean nerAutoExtractEnabled;

    @Value("${ner.python-service.url:http://localhost:8001}")
    private String nerServiceUrl;
    
    @Value("${ner.python-service.use-async:true}")
    private boolean useAsyncApi;
    
    @Value("${ner.python-service.poll-interval:3000}")
    private long pollInterval;  // 轮询间隔（毫秒）
    
    @Value("${ner.python-service.max-wait-time:600000}")
    private long maxWaitTime;  // 最大等待时间（毫秒）

    /**
     * 处理文档解析完成事件
     * 异步执行 NER 提取，不阻塞主流程
     */
    @Async
    @EventListener
    public void handleDocumentParsedEvent(DocumentParsedEvent event) {
        if (!nerAutoExtractEnabled) {
            log.debug("NER 自动提取已禁用，跳过: documentId={}", event.getDocumentId());
            return;
        }

        String documentId = event.getDocumentId();
        String userId = event.getUserId();

        log.info("收到文档解析完成事件，开始 NER 提取: documentId={}, userId={}", documentId, userId);

        long startTime = System.currentTimeMillis();

        try {
            // 1. 获取文档文本
            if (!graphNerService.hasDocumentText(documentId)) {
                log.warn("文档文本不存在，跳过 NER: documentId={}", documentId);
                return;
            }
            
            String text = graphNerService.getDocumentText(documentId);
            if (text == null || text.isEmpty()) {
                log.warn("文档文本为空，跳过 NER: documentId={}", documentId);
                return;
            }

            // 2. 调用 Python NER 服务（根据配置选择异步轮询或同步 API）
            Map<String, Object> nerResponse;
            if (useAsyncApi) {
                nerResponse = callPythonNerServiceAsync(documentId, text, userId);
            } else {
                nerResponse = callPythonNerService(documentId, text, userId);
            }
            
            if (nerResponse == null || !Boolean.TRUE.equals(nerResponse.get("success"))) {
                log.warn("NER 服务调用失败: documentId={}, error={}", 
                        documentId, nerResponse != null ? nerResponse.get("errorMessage") : "null response");
                return;
            }

            // 3. 保存实体到图数据库
            @SuppressWarnings("unchecked")
            List<Map<String, Object>> entities = (List<Map<String, Object>>) nerResponse.get("entities");
            Map<String, String> tempIdToNodeId = graphNerService.saveEntitiesToGraph(documentId, userId, entities);

            // 4. 保存关系到图数据库
            @SuppressWarnings("unchecked")
            List<Map<String, Object>> relations = (List<Map<String, Object>>) nerResponse.get("relations");
            int relationCount = graphNerService.saveRelationsToGraph(relations, tempIdToNodeId);
            
            // 5. 将 NER 结果转换为 TextElement 格式（用于结构化文档）
            List<TextElementDTO> textElements = nerToBlockService.convertToTextElements(text, entities);
            log.info("NER 结果已转换为 TextElement: documentId={}, elementCount={}", 
                    documentId, textElements.size());
            
            // TODO: 将 textElements 保存到 DocumentBlock 表
            // 这需要调用 document-service 的 API 或通过事件通知

            long processingTime = System.currentTimeMillis() - startTime;
            
            log.info("NER 自动提取完成: documentId={}, entityCount={}, relationCount={}, textElements={}, time={}ms",
                    documentId,
                    entities != null ? entities.size() : 0,
                    relationCount,
                    textElements.size(),
                    processingTime);

        } catch (Exception e) {
            log.error("NER 自动提取异常: documentId={}", documentId, e);
            // 异常不向上抛出，不影响其他处理
        }
    }

    /**
     * 调用 Python NER 服务（同步 REST API）
     */
    private Map<String, Object> callPythonNerService(String documentId, String text, String userId) {
        try {
            String url = nerServiceUrl + "/ner/extract";
            
            Map<String, Object> request = new HashMap<>();
            request.put("documentId", documentId);
            request.put("text", text);
            request.put("userId", userId);
            request.put("extractRelations", true);
            
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_JSON);
            
            HttpEntity<Map<String, Object>> entity = new HttpEntity<>(request, headers);
            
            ResponseEntity<Map> response = restTemplate.exchange(url, HttpMethod.POST, entity, Map.class);
            
            if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) {
                @SuppressWarnings("unchecked")
                Map<String, Object> body = response.getBody();
                return body;
            }
            
            return null;
            
        } catch (Exception e) {
            log.error("调用 Python NER 服务失败: {}", e.getMessage());
            return null;
        }
    }
    
    /**
     * 调用 Python NER 服务（异步 + 轮询模式）
     * 
     * 流程：
     * 1. 提交异步任务，立即获得 task_id
     * 2. 定期轮询任务状态
     * 3. 任务完成后获取结果
     */
    private Map<String, Object> callPythonNerServiceAsync(String documentId, String text, String userId) {
        try {
            // 1. 提交异步任务
            String submitUrl = nerServiceUrl + "/ner/extract/async";
            
            Map<String, Object> request = new HashMap<>();
            request.put("documentId", documentId);
            request.put("text", text);
            request.put("userId", userId);
            request.put("extractRelations", true);
            
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_JSON);
            
            HttpEntity<Map<String, Object>> entity = new HttpEntity<>(request, headers);
            
            @SuppressWarnings("unchecked")
            ResponseEntity<Map<String, Object>> submitResponse = restTemplate.exchange(
                    submitUrl, HttpMethod.POST, entity, 
                    (Class<Map<String, Object>>) (Class<?>) Map.class);
            
            if (!submitResponse.getStatusCode().is2xxSuccessful() || submitResponse.getBody() == null) {
                log.error("提交异步 NER 任务失败: documentId={}", documentId);
                return null;
            }
            
            String taskId = (String) submitResponse.getBody().get("task_id");
            log.info("异步 NER 任务已提交: documentId={}, taskId={}", documentId, taskId);
            
            // 2. 轮询任务状态
            String statusUrl = nerServiceUrl + "/ner/task/" + taskId;
            long startTime = System.currentTimeMillis();
            int lastProgress = -1;
            
            while (System.currentTimeMillis() - startTime < maxWaitTime) {
                try {
                    Thread.sleep(pollInterval);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    log.warn("轮询被中断: taskId={}", taskId);
                    break;
                }
                
                try {
                    @SuppressWarnings("unchecked")
                    ResponseEntity<Map<String, Object>> statusResponse = restTemplate.exchange(
                            statusUrl, HttpMethod.GET, null,
                            (Class<Map<String, Object>>) (Class<?>) Map.class);
                    
                    if (!statusResponse.getStatusCode().is2xxSuccessful() || statusResponse.getBody() == null) {
                        log.warn("查询任务状态失败: taskId={}", taskId);
                        continue;
                    }
                    
                    Map<String, Object> taskStatus = statusResponse.getBody();
                    String status = (String) taskStatus.get("status");
                    int progress = taskStatus.get("progress") != null ? 
                            ((Number) taskStatus.get("progress")).intValue() : 0;
                    String message = (String) taskStatus.get("message");
                    
                    // 只在进度变化时打印日志
                    if (progress != lastProgress) {
                        log.info("NER 进度: documentId={}, taskId={}, status={}, progress={}%, message={}", 
                                documentId, taskId, status, progress, message);
                        lastProgress = progress;
                    }
                    
                    // 检查任务是否完成
                    if ("completed".equals(status)) {
                        @SuppressWarnings("unchecked")
                        Map<String, Object> result = (Map<String, Object>) taskStatus.get("result");
                        log.info("异步 NER 任务完成: documentId={}, taskId={}", documentId, taskId);
                        
                        // 删除任务（释放服务端内存）
                        try {
                            restTemplate.delete(statusUrl);
                        } catch (Exception e) {
                            log.debug("删除任务失败（可忽略）: taskId={}", taskId);
                        }
                        
                        return result;
                    } else if ("failed".equals(status)) {
                        String error = (String) taskStatus.get("error");
                        log.error("异步 NER 任务失败: documentId={}, taskId={}, error={}", documentId, taskId, error);
                        return null;
                    }
                    
                } catch (Exception e) {
                    log.warn("轮询任务状态异常: taskId={}, error={}", taskId, e.getMessage());
                }
            }
            
            log.error("异步 NER 任务超时: documentId={}, taskId={}, maxWaitTime={}ms", 
                    documentId, taskId, maxWaitTime);
            return null;
            
        } catch (Exception e) {
            log.error("调用异步 NER 服务失败: documentId={}, error={}", documentId, e.getMessage(), e);
            // 回退到同步 API
            log.info("回退到同步 NER API: documentId={}", documentId);
            return callPythonNerService(documentId, text, userId);
        }
    }
}
