package com.agent.management.service.impl;

import com.agent.management.common.exception.BusinessException;
import com.agent.management.config.KbProperties;
import com.agent.management.model.entity.KbChunk;
import com.agent.management.model.entity.KbDocument;
import com.agent.management.repository.KbChunkRepository;
import com.agent.management.repository.KbDocumentRepository;
import com.agent.management.service.DocumentPipeline;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.tika.Tika;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 文档异步流水线实现：调用本地 Embedding Bridge 完成三级分块与向量化。
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class DocumentPipelineImpl implements DocumentPipeline {

    private final KbDocumentRepository documentRepository;
    private final KbChunkRepository chunkRepository;
    private final EmbeddingBridgeClient bridgeClient;
    private final KbProperties kbProps;

    private final Tika tika = new Tika();

    @Override
    @Async
    @Transactional
    public void process(Long documentId) {
        KbDocument doc = documentRepository.findById(documentId).orElse(null);
        if (doc == null) {
            log.warn("pipeline.process: 文档不存在 id={}", documentId);
            return;
        }
        try {
            log.info("[流水线启动] doc={} ({})", doc.getId(), doc.getName());

            // 1. PARSING
            doc.setStatus("PARSING");
            documentRepository.save(doc);
            String text = parseFile(doc);
            log.info("[解析完成] doc={} 字符数={}", doc.getId(), text.length());

            // 2. CHUNKING + EMBEDDING + VECTORIZING（通过 Bridge 完成）
            doc.setStatus("EMBEDDING");
            documentRepository.save(doc);

            Map<String, Object> result = bridgeClient.indexDocument(
                    doc.getId(),
                    text,
                    doc.getName(),
                    doc.getMimeType() == null ? "" : doc.getMimeType(),
                    doc.getSourcePath() == null ? "" : doc.getSourcePath(),
                    doc.getCategoryId()
            );

            @SuppressWarnings("unchecked")
            List<Map<String, Object>> chunkData = (List<Map<String, Object>>) result.get("chunks");
            if (chunkData == null) {
                chunkData = List.of();
            }

            List<KbChunk> savedChunks = saveChunks(doc, chunkData);
            int vectorCount = (int) savedChunks.stream().filter(c -> c.getVectorId() != null).count();

            doc.setChunkCount(savedChunks.size());
            doc.setVectorCount(vectorCount);
            documentRepository.save(doc);
            log.info("[分块/向量化完成] doc={} chunks={} vectors={}",
                    doc.getId(), savedChunks.size(), vectorCount);

            // 3. READY
            doc.setStatus("READY");
            doc.setErrorMessage(null);
            documentRepository.save(doc);
            log.info("[流水线完成] doc={} status=READY", doc.getId());

        } catch (Exception e) {
            log.error("[流水线失败] doc={} err={}", doc.getId(), e.getMessage(), e);
            doc.setStatus("FAILED");
            String msg = e.getMessage();
            doc.setErrorMessage(msg == null ? "未知错误" : (msg.length() > 1000 ? msg.substring(0, 1000) : msg));
            documentRepository.save(doc);
        }
    }

    private String parseFile(KbDocument doc) {
        Path target = Paths.get(kbProps.getUploadDir()).resolve(doc.getSourcePath());
        File file = target.toFile();
        if (!file.exists()) {
            throw new BusinessException("源文件不存在：" + target);
        }
        try {
            return tika.parseToString(file);
        } catch (Exception e) {
            throw new BusinessException("文件解析失败：" + e.getMessage());
        }
    }

    private List<KbChunk> saveChunks(KbDocument doc, List<Map<String, Object>> chunkData) {
        List<KbChunk> entities = new ArrayList<>(chunkData.size());
        int idx = 0;
        for (Map<String, Object> data : chunkData) {
            KbChunk c = new KbChunk();
            c.setDocumentId(doc.getId());
            c.setChunkIndex(idx++);
            c.setContent(asString(data.get("text")));
            c.setCharCount(c.getContent() == null ? 0 : c.getContent().length());
            c.setChunkLevel(asInt(data.get("chunk_level")));
            c.setChunkId(asString(data.get("chunk_id")));
            c.setParentChunkId(asString(data.get("parent_chunk_id")));
            c.setRootChunkId(asString(data.get("root_chunk_id")));
            c.setVectorId(asString(data.get("vector_id")));
            entities.add(c);
        }
        return chunkRepository.saveAll(entities);
    }

    private String asString(Object value) {
        return value == null ? null : String.valueOf(value);
    }

    private int asInt(Object value) {
        if (value == null) return 0;
        if (value instanceof Number n) return n.intValue();
        try {
            return Integer.parseInt(String.valueOf(value));
        } catch (NumberFormatException e) {
            return 0;
        }
    }
}
