package org.dromara.knowledge.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.knowledge.data.DocumentParserFactory;
import org.dromara.knowledge.domain.KnowledgeAttach;
import org.dromara.knowledge.domain.KnowledgeFragment;
import org.dromara.knowledge.domain.KnowledgeInfo;
import org.dromara.knowledge.domain.KnowledgeJob;
import org.dromara.knowledge.domain.vo.*;
import org.dromara.knowledge.service.*;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

/**
 * 知识库文档处理Service业务层处理
 *
 * @author pengles
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class KnowledgeDocumentServiceImpl implements KnowledgeDocumentService {

    // 文本清理正则表达式
    private static final Pattern MULTIPLE_SPACES = Pattern.compile("\\s+");
    private static final Pattern MULTIPLE_NEWLINES = Pattern.compile("\\n{3,}");
    private final KnowledgeAttachService knowledgeAttachService;
    private final KnowledgeFragmentService knowledgeFragmentService;
    private final KnowledgeJobService knowledgeJobService;
    private final KnowledgeVectorService knowledgeVectorService;
    private final DocumentParserFactory documentParserFactory;
    @org.springframework.beans.factory.annotation.Value("${ruoyi.ai.knowledge.chunk.size:1000}")
    private Integer defaultChunkSize;
    @org.springframework.beans.factory.annotation.Value("${ruoyi.ai.knowledge.chunk.overlap:100}")
    private Integer defaultOverlapSize;

    @Override
    public String processUploadedDocument(String knowledgeId, MultipartFile file) {
        // 验证文件类型
        if (!isSupportedFileType(file)) {
            throw new ServiceException("不支持的文件类型: " + getFileExtension(file.getOriginalFilename()));
        }

        // 生成文档ID
        String docId = IdUtil.fastSimpleUUID();

        try {
            // 提取文本内容
            String content = extractTextContent(file);

            // 清理文本内容
            String cleanContent = cleanTextContent(content);

            // 创建文档附件记录
            KnowledgeAttach attach = new KnowledgeAttach();
            attach.setKnowledgeId(knowledgeId);
            attach.setDocId(docId);
            attach.setDocName(file.getOriginalFilename());
            attach.setDocType(getFileExtension(file.getOriginalFilename()));
            attach.setContent(cleanContent);
            attach.setPicStatus(10); // 未开始
            attach.setPicAnysStatus(10); // 未开始
            attach.setVectorStatus(10); // 未开始

            // 保存文档记录
            knowledgeAttachService.save(attach);

            splitDocumentIntoFragments(knowledgeId, docId, cleanContent, defaultChunkSize, defaultOverlapSize);

            return docId;
        } catch (Exception e) {
            log.error("处理文档失败: {}", e.getMessage(), e);
            throw new ServiceException("文档处理失败: " + e.getMessage());
        }
    }

    @Override
    public List<String> splitDocumentIntoFragments(String knowledgeId, String docId, String content,
                                                   Integer chunkSize, Integer overlapSize) {
        List<String> fragmentIds = new ArrayList<>();

        if (StrUtil.isBlank(content)) {
            return fragmentIds;
        }

        // 设置默认值
        int actualChunkSize = chunkSize != null ? chunkSize : 1000;
        int actualOverlapSize = overlapSize != null ? overlapSize : 100;

        // 分割文档
        List<String> chunks = splitTextIntoChunks(content, actualChunkSize, actualOverlapSize);

        for (int i = 0; i < chunks.size(); i++) {
            String chunk = chunks.get(i);
            if (StrUtil.isNotBlank(chunk)) {
                String fragmentId = IdUtil.fastSimpleUUID();

                // 创建知识片段记录
                KnowledgeFragment fragment = new KnowledgeFragment();
                fragment.setKnowledgeId(knowledgeId);
                fragment.setDocId(docId);
                fragment.setFid(fragmentId);
                fragment.setIdx(i);
                fragment.setContent(chunk.trim());

                // 保存知识片段
                knowledgeFragmentService.save(fragment);
                fragmentIds.add(fragmentId);
            }
        }

        log.info("文档 {} 分割完成，共生成 {} 个知识片段", docId, fragmentIds.size());
        return fragmentIds;
    }

    @Override
    public String extractTextContent(MultipartFile file) {
        try {
            String fileName = file.getOriginalFilename();
            if (StrUtil.isBlank(fileName)) {
                throw new ServiceException("文件名不能为空");
            }

            // 使用文档解析器工厂解析文件
            byte[] fileBytes = file.getBytes();
            String content = documentParserFactory.parse(fileBytes, fileName);

            log.debug("成功解析文件: {}, 内容长度: {}", fileName, content != null ? content.length() : 0);
            return content;
        } catch (IOException e) {
            log.error("读取文件内容失败: {}", file.getOriginalFilename(), e);
            throw new ServiceException("读取文件内容失败: " + e.getMessage());
        } catch (Exception e) {
            log.error("解析文件失败: {}", file.getOriginalFilename(), e);
            throw new ServiceException("解析文件失败: " + e.getMessage());
        }
    }

    @Override
    public String cleanTextContent(String content) {
        if (StrUtil.isBlank(content)) {
            return "";
        }

        // 移除多余的空白字符
        String cleaned = MULTIPLE_SPACES.matcher(content).replaceAll(" ");

        // 移除多余的换行符
        cleaned = MULTIPLE_NEWLINES.matcher(cleaned).replaceAll("\n\n");

        // 移除首尾空白
        cleaned = cleaned.trim();

        return cleaned;
    }

    @Override
    public boolean isSupportedFileType(MultipartFile file) {
        String fileName = file.getOriginalFilename();
        if (StrUtil.isBlank(fileName)) {
            return false;
        }

        String extension = getFileExtension(fileName);
        return documentParserFactory.isSupported(extension);
    }

    @Override
    public List<String> getSupportedFileTypes() {
        return new ArrayList<>(documentParserFactory.getSupportedExtensions());
    }

    @Override
    public KnowledgeStatusVo getKnowledgeProcessingStatus(Long knowledgeId, KnowledgeInfoVo knowledgeInfo) {
        KnowledgeStatusVo statusVo = new KnowledgeStatusVo();

        // 设置基本信息
        statusVo.setKnowledgeId(knowledgeId.toString());
        statusVo.setKnowledgeName(knowledgeInfo.getKnowledgeName());
        statusVo.setLastUpdateTime(LocalDateTime.parse(DateUtil.now()));

        try {
            // 获取知识库附件统计
            List<KnowledgeAttachVo> attachList = knowledgeAttachService.queryListByKnowledgeId(knowledgeId.toString());
            int totalDocuments = attachList.size();
            int processedDocuments = (int) attachList.stream().filter(attach -> attach.getVectorStatus() != null && attach.getVectorStatus() == 30).count();

            statusVo.setTotalDocuments(totalDocuments);
            statusVo.setProcessedDocuments(processedDocuments);

            // 获取知识片段统计
            List<KnowledgeFragmentVo> fragmentList = knowledgeFragmentService.queryListByKnowledgeId(knowledgeId);
            int totalFragments = fragmentList.size();
            statusVo.setTotalFragments(totalFragments);

            // 获取向量化统计
            List<KnowledgeVectorVo> vectorList = knowledgeVectorService.queryByKnowledgeId(knowledgeId.toString());
            int vectorizedFragments = vectorList.size();
            statusVo.setVectorizedFragments(vectorizedFragments);

            // 获取任务统计
            List<KnowledgeJobVo> runningJobs = knowledgeJobService.queryRunningJobs();
            List<KnowledgeJobVo> pendingJobs = knowledgeJobService.queryPendingJobs();
            List<KnowledgeJobVo> failedJobs = knowledgeJobService.queryFailedJobs();

            // 过滤当前知识库的任务
            long runningCount = runningJobs.stream().filter(job -> knowledgeId.toString().equals(job.getKnowledgeId())).count();
            long pendingCount = pendingJobs.stream().filter(job -> knowledgeId.toString().equals(job.getKnowledgeId())).count();
            long failedCount = failedJobs.stream().filter(job -> knowledgeId.toString().equals(job.getKnowledgeId())).count();

            statusVo.setRunningJobs((int) runningCount);
            statusVo.setPendingJobs((int) pendingCount);
            statusVo.setFailedJobs((int) failedCount);

            // 计算进度百分比
            if (totalFragments > 0) {
                double progress = (double) vectorizedFragments / totalFragments * 100;
                statusVo.setProgressPercentage((double) Math.round(progress));
            } else {
                statusVo.setProgressPercentage((double) 0);
            }

            // 确定处理状态
            if (runningCount > 0) {
                statusVo.setProcessingStatus("PROCESSING");
            } else if (pendingCount > 0) {
                statusVo.setProcessingStatus("PENDING");
            } else if (failedCount > 0) {
                statusVo.setProcessingStatus("FAILED");
            } else if (totalFragments > 0 && vectorizedFragments == totalFragments) {
                statusVo.setProcessingStatus("COMPLETED");
            } else {
                statusVo.setProcessingStatus("IDLE");
            }

        } catch (Exception e) {
            statusVo.setProcessingStatus("ERROR");
            statusVo.setErrorMessage("获取状态信息时发生错误: " + e.getMessage());
        }

        return statusVo;
    }

    @Override
    public String processDocumentContent(String knowledgeId, String docId, String content, KnowledgeInfo knowledgeInfo) {
        try {
            // 生成任务ID
            String taskId = IdUtil.fastSimpleUUID();

            // 创建处理任务记录
            KnowledgeJob job = new KnowledgeJob();
            job.setJobId(taskId);
            job.setKnowledgeId(knowledgeId);
            job.setDocId(docId);
            job.setJobType("DOCUMENT_PROCESSING");
            job.setStatus(1); // 处理中
            job.setProgress(0);
            job.setTotalItems(1);
            job.setProcessedItems(0);
            job.setSuccessItems(0);
            job.setFailedItems(0);
            job.setRetryCount(0);
            job.setPriority(5);
            job.setRemark("文档内容处理任务");

            knowledgeJobService.save(job);

            // 异步处理文档内容分片
            splitDocumentIntoFragments(knowledgeId, docId, content, 1000, 100);

            // 更新任务状态为完成
            job.setStatus(2); // 已完成
            job.setProgress(100);
            job.setProcessedItems(1);
            job.setSuccessItems(1);
            knowledgeJobService.updateById(job);

            return taskId;
        } catch (Exception e) {
            log.error("处理文档内容失败: {}", e.getMessage(), e);
            throw new ServiceException("文档内容处理失败: " + e.getMessage());
        }
    }

    @Override
    public String vectorizeKnowledgeFragment(String knowledgeId, String fragmentId, KnowledgeInfo knowledgeInfo) {
        try {
            // 生成任务ID
            String taskId = IdUtil.fastSimpleUUID();

            // 创建向量化任务记录
            KnowledgeJob job = new KnowledgeJob();
            job.setJobId(taskId);
            job.setKnowledgeId(knowledgeId);
            job.setJobType("FRAGMENT_VECTORIZATION");
            job.setStatus(1); // 处理中
            job.setProgress(0);
            job.setTotalItems(1);
            job.setProcessedItems(0);
            job.setSuccessItems(0);
            job.setFailedItems(0);
            job.setRetryCount(0);
            job.setPriority(5);
            job.setRemark("知识片段向量化任务");

            knowledgeJobService.save(job);

            // 获取知识片段信息
            KnowledgeFragmentVo fragment = knowledgeFragmentService.queryByFid(fragmentId);
            if (fragment == null) {
                throw new ServiceException("知识片段不存在: " + fragmentId);
            }

            // 调用向量化服务
            String vectorId = knowledgeVectorService.generateVector(knowledgeId, fragment.getDocId(), fragmentId, fragment.getContent(), "bge-base-zh");

            // 更新任务状态为完成
            job.setStatus(2); // 已完成
            job.setProgress(100);
            job.setProcessedItems(1);
            job.setSuccessItems(1);
            job.setRemark("知识片段向量化任务完成，向量ID: " + vectorId);
            knowledgeJobService.updateById(job);

            log.info("知识片段 {} 向量化完成，向量ID: {}", fragmentId, vectorId);

            return taskId;
        } catch (Exception e) {
            log.error("向量化知识片段失败: {}", e.getMessage(), e);
            throw new ServiceException("向量化处理失败: " + e.getMessage());
        }
    }

    /**
     * 获取文件扩展名
     */
    private String getFileExtension(String fileName) {
        if (StrUtil.isBlank(fileName)) {
            return "";
        }

        int lastDotIndex = fileName.lastIndexOf('.');
        if (lastDotIndex == -1 || lastDotIndex == fileName.length() - 1) {
            return "";
        }

        return fileName.substring(lastDotIndex + 1);
    }

    /**
     * 将文本分割成指定大小的块
     */
    private List<String> splitTextIntoChunks(String text, int chunkSize, int overlapSize) {
        List<String> chunks = new ArrayList<>();

        if (text.length() <= chunkSize) {
            chunks.add(text);
            return chunks;
        }

        int start = 0;
        while (start < text.length()) {
            int end = Math.min(start + chunkSize, text.length());

            // 尝试在句号、问号、感叹号处分割
            if (end < text.length()) {
                int lastSentenceEnd = findLastSentenceEnd(text, start, end);
                if (lastSentenceEnd > start) {
                    end = lastSentenceEnd + 1;
                }
            }

            String chunk = text.substring(start, end);
            chunks.add(chunk);

            // 计算下一个块的起始位置（考虑重叠）
            start = Math.max(start + 1, end - overlapSize);
        }

        return chunks;
    }

    /**
     * 查找最后一个句子结束位置
     */
    private int findLastSentenceEnd(String text, int start, int end) {
        for (int i = end - 1; i >= start; i--) {
            char c = text.charAt(i);
            if (c == '。' || c == '！' || c == '？' || c == '.' || c == '!' || c == '?') {
                return i;
            }
        }
        return -1;
    }

}
