package cn.com.poc.knowledge.aggregate.impl;


import cn.com.poc.common.constant.CommonConstant;
import cn.com.poc.common.service.BosConfigService;
import cn.com.poc.common.utils.*;
import cn.com.poc.knowledge.aggregate.KnowledgeService;
import cn.com.poc.knowledge.constant.KnowledgeConstant;
import cn.com.poc.knowledge.entity.BizKnowledgeDocumentEntity;
import cn.com.poc.knowledge.entity.BizKnowledgeInfoEntity;
import cn.com.poc.knowledge.entity.BizMemberKnowledgeRelationEntity;
import cn.com.poc.knowledge.query.KnowledgeQueryItem;
import cn.com.poc.knowledge.service.BizKnowledgeDocumentService;
import cn.com.poc.knowledge.service.BizKnowledgeInfoService;
import cn.com.poc.knowledge.service.BizMemberKnowledgeRelationService;
import cn.com.poc.message.entity.TrainKnowledgeMessage;
import cn.com.poc.message.service.KnowledgeProducerService;
import cn.com.poc.support.security.oauth.entity.UserBaseEntity;
import cn.com.poc.thirdparty.resource.demand.ai.aggregate.DemandKnowledgeService;
import cn.com.poc.thirdparty.resource.demand.ai.entity.knowledge.GetKnowledgeChunkInfoResult;
import cn.com.poc.thirdparty.resource.demand.ai.entity.knowledge.SegmentationConfigRequest;
import cn.com.poc.thirdparty.resource.demand.ai.entity.qaknowledge.QAKnowledgeConfig;
import cn.com.yict.framemax.core.i18n.I18nMessageException;
import cn.com.yict.framemax.data.model.PagingInfo;
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import com.fasterxml.jackson.core.type.TypeReference;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class KnowledgeServiceImpl implements KnowledgeService {


    @Resource
    private BizKnowledgeDocumentService bizKnowledgeDocumentService;

    @Resource
    private BizMemberKnowledgeRelationService bizMemberKnowledgeRelationService;

    @Resource
    private KnowledgeProducerService knowledgeProducerService;

    @Resource
    private DemandKnowledgeService demandKnowledgeService;

    @Resource
    private BizKnowledgeInfoService bizKnowledgeInfoService;

    @Resource
    private BosConfigService bosConfigService;

    @Override
    public List<BizKnowledgeDocumentEntity> uploadDocument(MultipartFile[] documentFiles, String knowledgeType) throws Exception {
        //获取用户类型
        UserBaseEntity userBaseEntity = BlContext.getCurrentUserNotException();
        String memberId = userBaseEntity.getUserId().toString();
        String memberType = "system";

        String trainStatus = KnowledgeConstant.TrainStatus.UNOPENED;//默认状态-未执行
        Date uploadTime = new Date();//上传时间
        //记录成功
        List<BizKnowledgeDocumentEntity> result = new ArrayList<>();
        for (MultipartFile documentFile : documentFiles) {
            //获取文件名
            String documentName = documentFile.getOriginalFilename();

            // 统计文件字符数
            String type = documentFile.getOriginalFilename().substring(documentFile.getOriginalFilename().lastIndexOf(".") + 1, documentFile.getOriginalFilename().length());
            File file = File.createTempFile(UUIDTool.getUUID(), "." + type);
            documentFile.transferTo(file);
            // 文件大小不能超过10M
            long fileSizeInBytes = file.length();
            double fileSizeInMB = (double) fileSizeInBytes / (1024 * 1024);
            if (fileSizeInMB > 10) {
                throw new I18nMessageException("exception/upload.more.than.10m");
            }

            String fileContent = DocumentLoad.documentToText(file).replaceAll(StringUtils.LF, StringUtils.EMPTY).replaceAll(StringUtils.CR, StringUtils.EMPTY);//文件内容
            if (StringUtils.isBlank(fileContent)) {
                throw new I18nMessageException("exception/error.file.content.is.null");
            }
            //获取文件字符数
            long charCount = fileContent.length();
            //文件字符数不能超过100w
            if (charCount > 100 * 10000) {
                throw new I18nMessageException("exception/file.content.more.than.100w");
            }

            //文件上传
            String documentUrl = bosConfigService.upload(Files.newInputStream(file.toPath()), type, documentFile.getContentType());

            //保存记录
            BizKnowledgeDocumentEntity bizKnowledgeDocumentEntity = new BizKnowledgeDocumentEntity();
            bizKnowledgeDocumentEntity.setOwner(KnowledgeConstant.Owner.USER);
            bizKnowledgeDocumentEntity.setKnowledgeType(knowledgeType);
            bizKnowledgeDocumentEntity.setDocumentName(documentName);
            bizKnowledgeDocumentEntity.setDocumentUrl(documentUrl);
            bizKnowledgeDocumentEntity.setDocumentSize(fileSizeInBytes);
            bizKnowledgeDocumentEntity.setCharCount(charCount);
            bizKnowledgeDocumentEntity.setUploadTime(uploadTime);
            bizKnowledgeDocumentEntity.setIsEnable(CommonConstant.YOrN.N);
            bizKnowledgeDocumentEntity.setKnowledgeId(null);
            bizKnowledgeDocumentEntity.setTrainStatus(trainStatus);
            bizKnowledgeDocumentEntity.setIsDeleted(CommonConstant.IsDeleted.N);
            BizKnowledgeDocumentEntity knowledgeDocumentEntity = bizKnowledgeDocumentService.save(bizKnowledgeDocumentEntity);

            //保存关联
            BizMemberKnowledgeRelationEntity bizMemberKnowledgeRelationEntity = new BizMemberKnowledgeRelationEntity();
            bizMemberKnowledgeRelationEntity.setMemberId(memberId);
            bizMemberKnowledgeRelationEntity.setMemberType(memberType);
            bizMemberKnowledgeRelationEntity.setKdId(knowledgeDocumentEntity.getKdId());
            bizMemberKnowledgeRelationEntity.setIsDeleted(CommonConstant.YOrN.N);
            bizMemberKnowledgeRelationService.save(bizMemberKnowledgeRelationEntity);

            //构造返回
            result.add(knowledgeDocumentEntity);
        }
        return result;
    }

    @Override
    public Boolean delDocument(Integer knowledgeInfoId, Integer kdId) {
        Assert.notNull(kdId);
        bizKnowledgeDocumentService.deleted(kdId);
        bizMemberKnowledgeRelationService.deletedByKdId(kdId);
        bizKnowledgeInfoService.removeDocument(knowledgeInfoId, kdId);

        BizKnowledgeInfoEntity bizKnowledgeInfoEntity = bizKnowledgeInfoService.get(knowledgeInfoId);
        String kdIds = bizKnowledgeInfoEntity.getKdIds();
        List<Integer> kdIdList = JsonUtils.deSerialize(kdIds, new TypeReference<List<Integer>>() {
        }.getType());
        List<KnowledgeQueryItem> knowledgeQueryItems = bizKnowledgeDocumentService.searchKnowledge(null, null, bizKnowledgeInfoEntity.getMemberId(), null, kdIdList, null);
        boolean isCompleted = true;
        String lock = knowledgeInfoId.toString().intern();
        synchronized (lock) {
            for (KnowledgeQueryItem knowledgeQueryItem : knowledgeQueryItems) {
                if (!KnowledgeConstant.TrainStatus.COMPLETE.equals(knowledgeQueryItem.getTrainStatus())) {
                    isCompleted = false;
                    break;
                }
            }
            if (isCompleted) {
                bizKnowledgeInfoEntity.setTrainStatus(KnowledgeConstant.TrainStatus.COMPLETE);
                bizKnowledgeInfoService.update(bizKnowledgeInfoEntity);
            }
        }
        return true;
    }

    @Override
    public Boolean renameDocument(Integer kdId, String rename) throws Exception {
        Assert.notNull(kdId);
        Assert.notBlank(rename);
        BizKnowledgeDocumentEntity bizKnowledgeDocumentEntity = bizKnowledgeDocumentService.get(kdId);
        if (bizKnowledgeDocumentEntity == null) {
            throw new I18nMessageException("exception/document.does.not.exist");
        }
        bizKnowledgeDocumentEntity.setDocumentName(rename);
        bizKnowledgeDocumentService.update(kdId, bizKnowledgeDocumentEntity);
        return true;
    }

    @Override
    public String enableKnowledge(Integer kdId) throws Exception {
        BizKnowledgeDocumentEntity bizKnowledgeDocumentEntity = bizKnowledgeDocumentService.get(kdId);
        if (bizKnowledgeDocumentEntity == null) {
            throw new I18nMessageException("exception/document.does.not.exist");
        }

        String isEnable = bizKnowledgeDocumentEntity.getIsEnable();
        if (CommonConstant.YOrN.N.equals(isEnable)) {
            bizKnowledgeDocumentEntity.setIsEnable(CommonConstant.YOrN.Y);
        } else {
            bizKnowledgeDocumentEntity.setIsEnable(CommonConstant.YOrN.N);
        }
        BizKnowledgeDocumentEntity documentEntity = bizKnowledgeDocumentService.update(kdId, bizKnowledgeDocumentEntity);
        return documentEntity.getIsEnable();
    }

    @Override
    public Boolean trainKnowledge(Integer knowledgeInfoId, List<Integer> kdIds, SegmentationConfigRequest segmentationConfig) throws Exception {
        Assert.notEmpty(kdIds);
        Assert.isTrue(kdIds.size() <= 5, "Training cannot exceed five documents");
        BizKnowledgeInfoEntity knowledgeInfoEntity = bizKnowledgeInfoService.get(knowledgeInfoId);
        if (knowledgeInfoEntity == null) {
            throw new I18nMessageException("exception/knowledge.base.information.does.not.exist");
        }
        String knowledgeType = knowledgeInfoEntity.getKnowledgeType();

        for (Integer kdId : kdIds) {
            List<QAKnowledgeConfig> qaKnowledgeConfigs = checkQAKnowledgeStruct(kdId, knowledgeType);
            BizKnowledgeDocumentEntity bizKnowledgeDocumentEntity = bizKnowledgeDocumentService.get(kdId);
            String currentStatus = bizKnowledgeDocumentEntity.getTrainStatus();
            if (KnowledgeConstant.TrainStatus.COMPLETE.equals(currentStatus) ||
                    KnowledgeConstant.TrainStatus.LINE.equals(currentStatus) ||
                    KnowledgeConstant.TrainStatus.TRAINING.equals(currentStatus)) {
                throw new I18nMessageException("exception/repetitive.training.in.the.knowledge.base");
            }

            bizKnowledgeDocumentEntity.setTrainStatus(KnowledgeConstant.TrainStatus.LINE);
            bizKnowledgeDocumentEntity.setSegmentationConfig(segmentationConfig);
            bizKnowledgeDocumentService.update(kdId, bizKnowledgeDocumentEntity);

            TrainKnowledgeMessage message = new TrainKnowledgeMessage();
            message.setKid(kdId);
            message.setKnowledgeType(knowledgeType);
            message.setFileUrl(bizKnowledgeDocumentEntity.getDocumentUrl());
            message.setSegmentationConfig(segmentationConfig);
            message.setKnowledgeInfoId(knowledgeInfoId);
            message.setQaKnowledgeConfigList(qaKnowledgeConfigs);
            knowledgeProducerService.trainKnowledge(message);
        }

        if (StringUtils.isNotBlank(knowledgeInfoEntity.getKdIds())) {
            List<Integer> kdIdList = JsonUtils.deSerialize(knowledgeInfoEntity.getKdIds(), new TypeReference<List<Integer>>() {
            }.getType());
            kdIds.addAll(kdIdList);
        }
        knowledgeInfoEntity.setId(knowledgeInfoId);
        knowledgeInfoEntity.setTrainStatus(KnowledgeConstant.TrainStatus.TRAINING);
        knowledgeInfoEntity.setKdIds(JsonUtils.serialize(kdIds.stream().
                distinct().
                collect(Collectors.toList())));
        bizKnowledgeInfoService.update(knowledgeInfoEntity);
        return true;
    }

    /**
     * 获取问答知识库结构
     *
     * @param kdId
     * @param knowledgeType
     * @return
     * @throws IOException
     */
    private List<QAKnowledgeConfig> checkQAKnowledgeStruct(Integer kdId, String knowledgeType) throws IOException {
        List<QAKnowledgeConfig> qaKnowledgeConfigs = new ArrayList<>();
        if (knowledgeType.equals(KnowledgeConstant.KnowledgeType.QA)) {
            BizKnowledgeDocumentEntity bizKnowledgeDocumentEntity = bizKnowledgeDocumentService.get(kdId);
            String documentUrl = bizKnowledgeDocumentEntity.getDocumentUrl();
            File file = DocumentLoad.downloadURLDocument(documentUrl);
            ExcelReader excelReader = ExcelUtil.getReader(file);
            Workbook workbook = excelReader.getWorkbook();
            Sheet sheetAt = workbook.getSheetAt(0);
            Row row = sheetAt.getRow(1);
            Iterator<Cell> cellIterator = row.cellIterator();
            int maxCellNum = 10;//最大列数限制
            while (cellIterator.hasNext()) {
                Cell next = cellIterator.next();
                if (next.getColumnIndex() >= maxCellNum) {
                    break;
                }
                QAKnowledgeConfig qaKnowledgeConfig = new QAKnowledgeConfig();
                qaKnowledgeConfig.setColumn(next.getColumnIndex());
                qaKnowledgeConfig.setKeyName(next.toString());
                qaKnowledgeConfig.setIndex(next.getColumnIndex() == 0);
                qaKnowledgeConfigs.add(qaKnowledgeConfig);
            }
            workbook.close();
            excelReader.close();
        }
        return qaKnowledgeConfigs;
    }

    @Override
    public List<BizKnowledgeDocumentEntity> searchDocuments(String search, String trainStatus, List<Integer> kdIds, String memberId, PagingInfo pagingInfo) {
        List<BizKnowledgeDocumentEntity> result = new ArrayList<>();
        List<KnowledgeQueryItem> knowledgeQueryItems = bizKnowledgeDocumentService.searchKnowledge(search, trainStatus, memberId, null, kdIds, pagingInfo);
        if (CollectionUtils.isNotEmpty(knowledgeQueryItems)) {
            result = knowledgeQueryItems.stream().map(item -> {
                BizKnowledgeDocumentEntity entity = new BizKnowledgeDocumentEntity();
                entity.setKdId(item.getKdId());
                entity.setOwner(item.getOwner());
                entity.setKnowledgeType(item.getKnowledgeType());
                entity.setDocumentName(item.getDocumentName());
                entity.setDocumentUrl(item.getDocumentUrl());
                entity.setCharCount(item.getCharCount());
                entity.setUploadTime(item.getUploadTime());
                entity.setIsEnable(item.getIsEnable());
                entity.setKnowledgeId(item.getKnowledgeId());
                entity.setTrainStatus(item.getTrainStatus());
                entity.setDocumentSize(item.getDocumentSize());
                if (StringUtils.isNotBlank(item.getSegmentationConfig())) {
                    SegmentationConfigRequest segmentationConfigRequest = JsonUtils.deSerialize(item.getSegmentationConfig(), SegmentationConfigRequest.class);
                    entity.setSegmentationConfig(segmentationConfigRequest);
                }
                return entity;
            }).collect(Collectors.toList());
        }
        return result;
    }

    @Override
    public List<BizKnowledgeDocumentEntity> getListByKdIds(List<Integer> kdIds) {
        Assert.notEmpty(kdIds);
        List<BizKnowledgeDocumentEntity> res = new ArrayList<>();
        for (Integer kdId : kdIds) {
            BizKnowledgeDocumentEntity bizKnowledgeDocumentEntity = bizKnowledgeDocumentService.get(kdId);
            if (bizKnowledgeDocumentEntity == null) {
                continue;
            }
            res.add(bizKnowledgeDocumentEntity);
        }
        return res;
    }

    @Override
    public GetKnowledgeChunkInfoResult getChunkInfo(List<Integer> kdIds, String query, PagingInfo pagingInfo) {
        List<String> knowledgeIds = new ArrayList<>();
        for (Integer kdId : kdIds) {
            BizKnowledgeDocumentEntity bizKnowledgeDocumentEntity = bizKnowledgeDocumentService.get(kdId);
            String knowledgeId = bizKnowledgeDocumentEntity.getKnowledgeId();
            knowledgeIds.add(knowledgeId);
        }
        return demandKnowledgeService.getKnowledgeChunkInfos(knowledgeIds, query, pagingInfo);
    }

    @Override
    public void openKnowledgeChunk(Integer kdId, String chunkRelationId, String isOpen) {
        BizKnowledgeDocumentEntity bizKnowledgeDocumentEntity = bizKnowledgeDocumentService.get(kdId);
        if (bizKnowledgeDocumentEntity == null) {
            throw new I18nMessageException("exception/knowledge.base.does.not.exist");
        }
        String knowledgeId = bizKnowledgeDocumentEntity.getKnowledgeId();
        demandKnowledgeService.openKnowledgeChunk(knowledgeId, chunkRelationId, isOpen);
    }

    @Override
    public void deleteKnowledgeChunk(Integer kdId, String chunkRelationId) {
        BizKnowledgeDocumentEntity bizKnowledgeDocumentEntity = bizKnowledgeDocumentService.get(kdId);
        if (bizKnowledgeDocumentEntity == null) {
            throw new I18nMessageException("exception/knowledge.base.does.not.exist");
        }
        String knowledgeId = bizKnowledgeDocumentEntity.getKnowledgeId();
        demandKnowledgeService.deleteKnowledgeChunk(knowledgeId, chunkRelationId);
    }

    @Override
    public void updateKnowledgeChunkDoc(Integer kdId, String chunkRelationId, String content) {
        BizKnowledgeDocumentEntity bizKnowledgeDocumentEntity = bizKnowledgeDocumentService.get(kdId);
        if (bizKnowledgeDocumentEntity == null) {
            throw new I18nMessageException("exception/knowledge.base.does.not.exist");
        }
        String knowledgeId = bizKnowledgeDocumentEntity.getKnowledgeId();

        demandKnowledgeService.updateKnowledgeChunkDoc(knowledgeId, chunkRelationId, content);
    }

    @Override
    public void addKnowledgeChunk(Integer kdId, String content, Integer chunkSort) {
        BizKnowledgeDocumentEntity bizKnowledgeDocumentEntity = bizKnowledgeDocumentService.get(kdId);
        if (bizKnowledgeDocumentEntity == null) {
            throw new I18nMessageException("exception/knowledge.base.does.not.exist");
        }
        String knowledgeId = bizKnowledgeDocumentEntity.getKnowledgeId();
        demandKnowledgeService.addKnowledgeChunk(knowledgeId, content, chunkSort);
    }

    @Override
    public List<Integer> getKdIdsByKnowledgeInfoIds(Integer[] knowledgeInfoIds) {
        //获取知识库配置
        List<Integer> kdIdList = new ArrayList<>();
        if (ArrayUtils.isNotEmpty(knowledgeInfoIds)) {
            for (Integer knowledgeId : knowledgeInfoIds) {
                BizKnowledgeInfoEntity bizKnowledgeInfoEntity = bizKnowledgeInfoService.get(knowledgeId);
                if (bizKnowledgeInfoEntity == null || StringUtils.isBlank(bizKnowledgeInfoEntity.getKdIds()) || CommonConstant.YOrN.N.equals(bizKnowledgeInfoEntity.getIsOpen())) {
                    continue;
                }
                List<Integer> kdIds = JsonUtils.deSerialize(bizKnowledgeInfoEntity.getKdIds(), new TypeReference<List<Integer>>() {
                }.getType());
                kdIdList.addAll(kdIds);
            }
        }
        return kdIdList;
    }
}
