package com.supervision.police.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.supervision.constant.EvidencePropertyName;
import com.supervision.police.domain.EvidenceFile;
import com.supervision.police.domain.FileEvidenceProperty;
import com.supervision.police.domain.FileOcrProcess;
import com.supervision.police.dto.*;
import com.supervision.police.service.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.framework.AopContext;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

@Slf4j
@Service
@RequiredArgsConstructor
public class OCREvidenceServiceImpl implements OCREvidenceService {

    private final FileOcrProcessService ocrProcessService;

    private final OCRService ocrService;

    private final FileEvidencePropertyService fileEvidencePropertyService;

    private final EvidenceFileService evidenceFileService;

    private final CaseEvidenceService caseEvidenceService;

    private final OcrExtractService ocrExtractService;
    @Override
    public Boolean submitOrcTask(String fileId) {
        Assert.notEmpty(fileId, "文件id不能为空");

        ((OCREvidenceService)AopContext.currentProxy()).asyncDoOrcTask(null, fileId);
        return true;
    }

    /**
     * 识别文件信息 由于识别文件标题需要通过组文件进行上下文分析,所以废弃单个文件识别的方式
     * @param evidenceId
     * @param fileId
     * @return
     */
    @Deprecated
    @Override
    @Transactional(rollbackFor = Exception.class,transactionManager = "dataSourceTransactionManager")
    public List<FileEvidenceProperty> doOrcTask(String evidenceId,String fileId) {

        List<FileEvidenceProperty> evidencePropertyList = new ArrayList<>();
        // 识别文件信息
        List<FileOcrProcess> fileOcrProcesses = ocrProcessService.syncSubmitOCR(CollUtil.newArrayList(fileId));

        if (CollUtil.isEmpty(fileOcrProcesses)){
            log.warn("subOrcTask:文件id:{} 识别失败", fileId);
            return evidencePropertyList;
        }

        for (FileOcrProcess fileOcrProcess : fileOcrProcesses) {

            // 调用提取属性接口
            fileOcrProcess = ocrProcessService.getById(fileOcrProcess.getId());
            if (StrUtil.isEmpty(fileOcrProcess.getOcrText())){
                log.warn("subOrcTask:文件id:{}, 对应的ocrText属性为空,跳过属性识别...", fileId);
                continue;
            }
            log.debug("subOrcTask:远程接口retrieve开始识别文本...:{}", fileOcrProcess.getOcrText());
            RetrieveResDTO retrieve = ocrService.retrieve(new RetrieveReqDTO(fileOcrProcess.getOcrText()));
            log.info("subOrcTask:文件id:{} 识别结果:{}", fileId, JSONUtil.toJsonStr(retrieve));

            if (Integer.valueOf(0).equals(retrieve.getStatus())){
                // 保存属性信息
                String title = retrieve.getTitle();
                if (StrUtil.isNotEmpty(title)){

                    evidencePropertyList.addAll(this.saveEvidenceProperty(evidenceId, fileId,
                            Map.of(EvidencePropertyName.TITLE.getName(), retrieve.getTitle())));

                    if (StrUtil.isNotEmpty(evidenceId)){
                        evidenceFileService.save(new EvidenceFile(evidenceId,fileId));
                    }

                }else {
                    log.warn("subOrcTask:文件id:{} 识别结果为空", fileId);
                }
            }
        }

        return evidencePropertyList;
    }

    @Async
    @Override
    public void asyncDoOrcTask(String evidenceId, String fileId) {
        ocrProcessService.syncSubmitOCR(CollUtil.newArrayList(fileId));
    }

    @Override
    @Transactional(rollbackFor = Exception.class,transactionManager = "dataSourceTransactionManager")
    public List<FileEvidenceProperty> saveEvidenceProperty(String evidenceId,String fileId ,Map<String, String> propertyMap) {

        // 先清空数据
        fileEvidencePropertyService.removeByEvidenceAndFileId(evidenceId,fileId);

        List<FileEvidenceProperty> fileEvidence = toFileEvidence(evidenceId, fileId, propertyMap);
        for (FileEvidenceProperty property : fileEvidence) {
            fileEvidencePropertyService.save(property);
        }

        return fileEvidence;
    }

    @Override
    public List<EvidenceFileOCRDTO> queryFileList(List<String> fileIdList) {

        if (CollUtil.isEmpty(fileIdList)){
            return new ArrayList<>(1);
        }
        List<OCREvidencePropertyDTO> ocrEvidencePropertyDTOS = fileEvidencePropertyService.listPrewByFileIdList(fileIdList);

        List<EvidenceFileOCRDTO> collect = ocrEvidencePropertyDTOS.stream()
                .collect(Collectors.groupingBy(OCREvidencePropertyDTO::getFileId))
                .values().stream()
                .map(EvidenceFileOCRDTO::new).peek(evidenceFileOCRDTO -> {
                    if (StrUtil.isEmpty(evidenceFileOCRDTO.getEvidenceType())){
                        evidenceFileOCRDTO.setEvidenceType("1");// 默认为书证
                    }
                    evidenceFileOCRDTO.setEvidenceNameValue();
                })
                .collect(Collectors.toList());

        return sortByIdOrder(fileIdList, collect);
    }

    @Override
    @Transactional(rollbackFor = Exception.class,transactionManager = "dataSourceTransactionManager")
    public String saveEvidence(EvidenceFileOCRDTO evidenceFileOCRDTO) {
        Assert.notEmpty(evidenceFileOCRDTO.getFileId(), "文件id不能为空");
        Assert.notEmpty(evidenceFileOCRDTO.getCaseId(), "案件id不能为空");
        Assert.notEmpty(evidenceFileOCRDTO.getEvidenceName(), "案件证据名称不能为空");
        Assert.notEmpty(evidenceFileOCRDTO.getEvidenceType(), "案件证据类型不能为空");

        //保存证据信息
        String evidenceId = caseEvidenceService.saveEvidence(evidenceFileOCRDTO.toCaseEvidenceDTO());

        // 保存证据属性
        List<FileEvidenceProperty> evidencePropertyList = saveEvidenceProperty(evidenceId,
                evidenceFileOCRDTO.getFileId(), evidenceFileOCRDTO.getProperty());

        if (CollUtil.isEmpty(evidencePropertyList)){
            return null;
        }
        return CollUtil.getFirst(evidencePropertyList).getEvidenceId();
    }

    @Override
    public Boolean retrieveTitle(List<String> fileIdList) {
        if (CollUtil.isEmpty(fileIdList)){
            return false;
        }
        ((OCREvidenceService)(AopContext.currentProxy())).asyncRetrieveTitle(fileIdList);
        return true;
    }

    @Override
    public Boolean retrieveTitleProcess(List<String> fileIdList) {
        if (CollUtil.isEmpty(fileIdList)){
            return true;
        }
        List<FileEvidenceProperty> properties = fileEvidencePropertyService.lambdaQuery().in(FileEvidenceProperty::getFileId, fileIdList).list();
        for (String fileId : fileIdList) {
            if (!evidencePropertiesExist(fileId, properties)){
                return false;
            }
        }
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class,transactionManager = "dataSourceTransactionManager")
    public List<String> batchSaveEvidence(List<EvidenceFileOCRDTO> evidenceFileOCRDTOS) {

        List<String> evidenceList = new ArrayList<>();
        if (CollUtil.isEmpty(evidenceFileOCRDTOS)){
            return evidenceList;

        }
        for (EvidenceFileOCRDTO evidenceFileOCRDTO : evidenceFileOCRDTOS) {
            String evidence = saveEvidence(evidenceFileOCRDTO);
            evidenceList.add(evidence);
        }
        return evidenceList;
    }

    @Async
    @Override
    @Transactional(rollbackFor = Exception.class,transactionManager = "dataSourceTransactionManager")
    public void asyncRetrieveTitle(List<String> fileIdList) {
        doRetrieveTitle(fileIdList);
    }

    @Override
    @Transactional(rollbackFor = Exception.class,transactionManager = "dataSourceTransactionManager")
    public void doRetrieveTitle(List<String> fileIdList) {

        // todo:等待ocr识别完成
        List<RecordFileDTO> recordFileDTOS = ocrProcessService.queryFileListWithIdSort(fileIdList);
        if (CollUtil.isEmpty(recordFileDTOS)){
            log.warn("doRetrieveTitle:文件id:{} 未查询到文件信息...", StrUtil.join(",", fileIdList));
        }

        List<OcrExtractDto> dtoList = recordFileDTOS.stream().map(recordFileDTO -> {
            OcrExtractDto dto = new OcrExtractDto();
            dto.setId(recordFileDTO.getFileId());
            dto.setText(recordFileDTO.getOcrText());
            return dto;
        }).toList();
        List<OcrExtractDto> result = ocrExtractService.extractTitle(dtoList);

        log.info("doRetrieveTitle,extractTitle返回结果是:{}", JSONUtil.toJsonStr(result));
        if (CollUtil.isEmpty(result)){
            log.warn("retrieveTitle:文件id:{} 提取标题失败", StrUtil.join(",", fileIdList));
        }
        for (OcrExtractDto dto : result) {
            this.saveEvidenceProperty(null, dto.getId(),
                    Map.of(EvidencePropertyName.TITLE.getName(), dto.getTitle()));
        }
    }

    @Override
    @Transactional(transactionManager = "dataSourceTransactionManager",rollbackFor = Exception.class)
    public CaseEvidenceDetailDTO queryEvidenceDetail(String evidenceId) {
        CaseEvidenceDetailDTO caseEvidenceDetailDTO = caseEvidenceService.queryEvidenceDetail(evidenceId);
        if (caseEvidenceDetailDTO == null){
            return null;
        }
        List<FileEvidenceProperty> list = fileEvidencePropertyService.lambdaQuery().eq(FileEvidenceProperty::getEvidenceId, evidenceId).list();
        caseEvidenceDetailDTO.setPropertyValue(list);
        return caseEvidenceDetailDTO;
    }


    private boolean evidencePropertiesExist(String fileId, List<FileEvidenceProperty> properties){
        for (FileEvidenceProperty property : properties) {
            if (StrUtil.equals(fileId, property.getFileId())){
                return true;
            }
        }
        return false;
    }

    private List<EvidenceFileOCRDTO> sortByIdOrder(List<String> fileIdList, List<EvidenceFileOCRDTO> evidenceFileOCRDTOS){

        Map<String, EvidenceFileOCRDTO> fileOCRDTOMap = evidenceFileOCRDTOS.stream()
                .collect(Collectors.toMap(EvidenceFileOCRDTO::getFileId, recordFileDTO -> recordFileDTO, (k1, k2) -> k1));
        return fileIdList.stream().map(fileId -> {
                    EvidenceFileOCRDTO evidenceFileOCRDTO = fileOCRDTOMap.get(fileId);
                    if (Objects.isNull(evidenceFileOCRDTO)) {
                        evidenceFileOCRDTO = new EvidenceFileOCRDTO();
                        evidenceFileOCRDTO.setFileId(fileId);
                        evidenceFileOCRDTO.setOcrStatus(-1);
                    }
                    return evidenceFileOCRDTO;
                }
        ).collect(Collectors.toList());
    }

    private List<FileEvidenceProperty> toFileEvidence(String evidenceId,String fileId ,Map<String, String> propertyMap){

        if (StrUtil.isAllEmpty(evidenceId,fileId) || CollUtil.isEmpty(propertyMap)){
            log.warn("saveEvidenceProperty:参数为空");
            return new ArrayList<>(1);
        }

        return EvidencePropertyName.getByNames(propertyMap)
                .stream().map(evidence ->
                        new FileEvidenceProperty(evidenceId, fileId, evidence.getName(), propertyMap.get(evidence.getName())))
                .toList();

    }
}