package com.njcn.process.service.impl;

import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.afterturn.easypoi.excel.entity.result.ExcelImportResult;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.njcn.common.pojo.enums.common.DataStateEnum;
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
import com.njcn.common.pojo.exception.BusinessException;
import com.njcn.device.pms.enums.PmsDeviceResponseEnum;
import com.njcn.oss.constant.OssPath;
import com.njcn.oss.enums.OssResponseEnum;
import com.njcn.oss.utils.FileStorageUtil;
import com.njcn.poi.excel.ExcelUtil;
import com.njcn.poi.util.PoiUtil;
import com.njcn.process.enums.ProcessResponseEnum;
import com.njcn.process.pojo.dto.excel.TerminalExcel;
import com.njcn.process.pojo.param.TerminalParam;
import com.njcn.process.pojo.po.PmsTerminalDetection;
import com.njcn.process.mapper.PmsTerminalDetectionMapper;
import com.njcn.process.pojo.vo.TerminalVO;
import com.njcn.process.service.PmsTerminalDetectionService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.njcn.process.utils.ExcelStyleUtil;
import com.njcn.system.api.DicDataFeignClient;
import com.njcn.system.enums.DicDataTypeEnum;
import com.njcn.system.pojo.po.DictData;
import com.njcn.user.api.DeptFeignClient;
import com.njcn.user.pojo.po.Dept;
import com.njcn.user.pojo.vo.PvTerminalTreeVO;
import lombok.RequiredArgsConstructor;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.core.io.InputStreamResource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.time.LocalDate;
import java.util.*;
import java.util.function.Function;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * <p>
 * 终端检测监督
 * </p>
 *
 * @author wr
 * @since 2023-02-27
 */
@Service
@RequiredArgsConstructor
public class PmsTerminalDetectionServiceImpl extends ServiceImpl<PmsTerminalDetectionMapper, PmsTerminalDetection> implements PmsTerminalDetectionService {

    private final DeptFeignClient deptFeignClient;
    private final FileStorageUtil fileStorageUtil;
    private final DicDataFeignClient dicDataFeignClient;

    @Override
    public Page<TerminalVO> terminalPage(TerminalParam.TerminalPageParam param) {
        List<Dept> data = deptFeignClient.getDirectSonSelf(param.getId()).getData();
        List<String> deptCodes = data.stream().map(Dept::getCode).collect(Collectors.toList());
        Page<PmsTerminalDetection> pageList = this.page(new Page<>(param.getPageNum(), param.getPageSize())
                , new LambdaQueryWrapper<PmsTerminalDetection>()
                        .in(CollectionUtil.isNotEmpty(deptCodes), PmsTerminalDetection::getOrgNo, deptCodes)
                        .eq(PmsTerminalDetection::getStatus, DataStateEnum.ENABLE.getCode())
                        .eq(param.getTestResults() != null, PmsTerminalDetection::getTestResults, param.getTestResults())

                        .in(CollectionUtil.isNotEmpty(param.getManufacture()), PmsTerminalDetection::getManufacture, param.getManufacture())
                        .like(StrUtil.isNotBlank(param.getName()), PmsTerminalDetection::getName, param.getName())
                        .ge(StrUtil.isNotBlank(param.getStartTime()), PmsTerminalDetection::getInspectionTime, param.getStartTime())
                        .le(StrUtil.isNotBlank(param.getEndTime()), PmsTerminalDetection::getInspectionTime, param.getEndTime())

                        .le(param.getType() != 0, PmsTerminalDetection::getNextInspectionTime, LocalDate.now())
                        .orderByDesc(PmsTerminalDetection::getCreateTime)
        );
        List<TerminalVO> rMpWpPowerDetailVOS2 = BeanUtil.copyToList(pageList.getRecords(), TerminalVO.class);

        Page<TerminalVO> page = BeanUtil.copyProperties(pageList, Page.class);
        page.setRecords(rMpWpPowerDetailVOS2);
        return page;
    }

    @Override
    public InputStreamResource exportTemplate() throws IOException {
        ExportParams exportParams = new ExportParams("批量导入模板（请严格按照模板标准填入数据）", "终端入网检测录入信息");
        exportParams.setStyle(ExcelStyleUtil.class);
        Workbook workbook = ExcelExportUtil.exportExcel(exportParams, TerminalExcel.class, new ArrayList<TerminalExcel>());
        List<DictData> businessList = dicDataFeignClient.getDicDataByTypeCode(DicDataTypeEnum.DEV_MANUFACTURER.getCode()).getData();
        ExcelUtil.selectList(workbook, 3, 3, businessList.stream().map(DictData::getName).collect(Collectors.toList()).toArray(new String[]{}));
        //临时缓冲区
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        //创建临时文件
        workbook.write(out);
        byte[] bookByteAry = out.toByteArray();
        InputStream in = new ByteArrayInputStream(bookByteAry);
        InputStreamResource inputStreamResource = new InputStreamResource(in);
        return inputStreamResource;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean insertTerminal(TerminalParam.TerminalInsertParam param)  {
        Dept data = deptFeignClient.getDeptById(param.getOrgNo()).getData();
        if (ObjectUtil.isNull(data)) {
            throw new BusinessException(CommonResponseEnum.NO_DATA, "部门不存在");
        }
        List<PmsTerminalDetection> list = this.list(new LambdaQueryWrapper<PmsTerminalDetection>()
                .eq(PmsTerminalDetection::getId, param.getId())
                .or(wrapper ->
                        wrapper.eq(PmsTerminalDetection::getName, param.getName())
                )
        );
        if (CollectionUtil.isNotEmpty(list)) {
            List<String> collect = list.stream().map(PmsTerminalDetection::getName).collect(Collectors.toList());
            if(collect.contains(param.getName())){
                throw new BusinessException(PmsDeviceResponseEnum.MODEL_NAME_REPEAT);
            }
            throw new BusinessException(ProcessResponseEnum.TERMINAL_ID_REPEAT);
        }
        PmsTerminalDetection detection = BeanUtil.copyProperties(param, PmsTerminalDetection.class);
        detection.setOriginalName(StrUtil.isNotBlank(param.getOriginalReport())?detection.getId() + "-原始数据报告.docx":"");
        detection.setOrgNo(data.getCode());
        detection.setOrgName(data.getName());

        LocalDate inspectionTime = param.getInspectionTime();
        detection.setNextInspectionTime(inspectionTime.plusYears(5));
        detection.setTestResults(StrUtil.isNotBlank(param.getOriginalReport())?1:0);
        detection.setStatus(1);
        return this.save(detection);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchTerminal(MultipartFile file, HttpServletResponse response) {

        ImportParams params = new ImportParams();
        params.setHeadRows(1);
        params.setTitleRows(1);
        params.setNeedVerify(true);
        //第一个sheet为台账信息
        params.setStartSheetIndex(0);
        params.setSheetNum(1);
        try {
            ExcelImportResult<TerminalExcel> terminalList = ExcelImportUtil.importExcelMore(file.getInputStream(), TerminalExcel.class, params);
            //如果存在非法数据，将不合格的数据导出
            if (terminalList.isVerifyFail()) {
                PoiUtil.exportFileByWorkbook(terminalList.getFailWorkbook(), "非法终端入网检测录入信息.xlsx", response);
            } else {
                saveTerminalBase(terminalList.getList());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateTerminal(TerminalParam.TerminalUpdateParam param) {
        List<PmsTerminalDetection> list = this.list(new LambdaQueryWrapper<PmsTerminalDetection>()
                .ne(PmsTerminalDetection::getId, param.getId())
                .and(wrapper ->
                        wrapper.eq(PmsTerminalDetection::getName, param.getName())
                )
        );
        if (CollectionUtil.isNotEmpty(list)) {
            List<String> collect = list.stream().map(PmsTerminalDetection::getName).collect(Collectors.toList());
            if(collect.contains(param.getName())){
                throw new BusinessException(PmsDeviceResponseEnum.MODEL_NAME_REPEAT);
            }
        }
        PmsTerminalDetection detection = BeanUtil.copyProperties(param, PmsTerminalDetection.class);
        LocalDate inspectionTime = param.getInspectionTime();
        detection.setNextInspectionTime(inspectionTime.plusYears(5));
        boolean b = this.updateById(detection);
        if (b) {
            PmsTerminalDetection cc = this.getOne(new LambdaQueryWrapper<PmsTerminalDetection>()
                    .eq(PmsTerminalDetection::getId, param.getId())
            );
            if (StrUtil.isNotBlank(cc.getOriginalReport())) {
                if (!cc.getOriginalReport().equals(param.getOriginalReport())) {
                    fileStorageUtil.deleteFile(cc.getOriginalReport());
                }

            }

        }
        return b;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean delTerminal(List<String> ids) {
        return this.removeByIds(ids);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void importReport(MultipartFile[] files, Integer type, HttpServletResponse response) {
        PmsTerminalDetection detection;
        List<PmsTerminalDetection> data = new ArrayList<>();
        for (int i = 0; i < files.length; i++) {
            detection = new PmsTerminalDetection();
            //上传的文件名称
            String originalFilename = files[i].getOriginalFilename();
            if (StrUtil.isNotBlank(originalFilename)) {
                if(type==0){
                    //判断文件名称是否正确
                    boolean matches = Pattern.matches("^[A-Za-z0-9\\u4e00-\\u9fa5]*-原始数据报告.docx$",originalFilename );
                    if(!matches){
                        continue;
                    }
                }else{
                    String time="[(]((([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3})-(((0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01]))|((0[469]|11)-(0[1-9]|[12][0-9]|30))|(02-(0[1-9]|[1][0-9]|2[0-8]))))|((([0-9]{2})(0[48]|[2468][048]|[13579][26])|((0[48]|[2468][048]|[3579][26])00))-02-29))[)]";
                    boolean matches1 = Pattern.matches("^[A-Za-z0-9\\u4e00-\\u9fa5]*-检测报告"+time+".docx$", originalFilename);
                    if(!matches1){
                        continue;
                    }
                }
                //获取编号
                String terminalIds = originalFilename.substring(0, originalFilename.indexOf("-"));
                //判断数据是否存在
                int count = this.count(new LambdaQueryWrapper<PmsTerminalDetection>()
                        .eq(PmsTerminalDetection::getId, terminalIds));
                if (count == 0) {
                    continue;
                }
                String fileName = originalFilename.substring(originalFilename.indexOf("-") + 1);
                if (type == 0) {
                    //传入的文件格式(终端编号-原始数据报告.docx);
                    if (fileName.substring(0, fileName.indexOf(".")).equals("原始数据报告")) {
                        //文件上传的地址
                        String path = fileStorageUtil.uploadMultipart(files[i], OssPath.TEST_REPORT);
                        detection.setOriginalReport(path);
                        detection.setOriginalName(originalFilename);
                        detection.setTestResults(1);
                        detection.setId(terminalIds);
                        data.add(detection);
                    }
                } else {
                    //传入的文件格式(终端编号-检测报告(2023-03-01).docx);
                    if (fileName.substring(0,  fileName.indexOf("(")).equals("检测报告")) {
                        String nextInspectionTime = fileName.substring( fileName.indexOf("(") + 1, fileName.indexOf(")"));
                        //文件上传的地址
                        String path = fileStorageUtil.uploadMultipart(files[i], OssPath.TEST_REPORT);
                        detection.setInspectionReport(path);
                        detection.setInspectionTime(LocalDate.parse(nextInspectionTime));
                        detection.setNextInspectionTime(LocalDate.parse(nextInspectionTime).plusYears(5));
                        detection.setInspectionName(originalFilename);
                        detection.setId(terminalIds);
                        data.add(detection);
                    }
                }

            }
        }
        if (CollectionUtil.isNotEmpty(data)) {
//            Map<String, PmsTerminalDetection> terminalMap = data.stream().collect(Collectors.toMap(PmsTerminalDetection::getId, Function.identity(), (key1, key2) -> {
//                //检测报告告为空
//                if (key1.getInspectionReport() == null) {
//                    key1.setInspectionReport(key2.getInspectionReport());
//                    key1.setInspectionName(key2.getInspectionName());
//                }
//                //原始数据报告为空
//                if (key1.getOriginalReport() == null) {
//                    key1.setOriginalReport(key2.getOriginalReport());
//                    key1.setOriginalName(key2.getOriginalName());
//                }
//                return key1;
//            }));
//            List<PmsTerminalDetection> updateTerminal = new ArrayList<>(terminalMap.values());

            //todo 批量删除功能是否需要
            List<String> delIds = data.stream().map(PmsTerminalDetection::getId).collect(Collectors.toList());
            List<PmsTerminalDetection> terminals = this.listByIds(delIds);
            List<String> remove = new ArrayList<>();
            for (PmsTerminalDetection terminal : terminals) {
                //用于区分是0:原始数据报告，还是1:检测报告
                if (type == 0) {
                    if (StrUtil.isNotBlank(terminal.getOriginalReport())) {
                        remove.add(terminal.getOriginalReport());
                    }
                } else {
                    if (StrUtil.isNotBlank(terminal.getInspectionReport())) {
                        remove.add(terminal.getInspectionReport());
                    }
                }
            }
            for (String s : remove) {
                fileStorageUtil.deleteFile(s);
            }
            this.updateBatchById(data);
        }

    }

    @Override
    public InputStreamResource reportDownload(TerminalParam.DownloadParam param) {
        PmsTerminalDetection terminal = this.getById(param.getId());
        if (ObjectUtil.isNull(terminal)) {
            if (StringUtils.isEmpty(terminal)) {
                throw new BusinessException(CommonResponseEnum.NO_DATA, "暂无该终端信息");
            }
        }
        if (param.getType().equals(1)) {
            if (StrUtil.isBlank(terminal.getInspectionReport())) {
                throw new BusinessException(OssResponseEnum.DOWNLOAD_FILE_ERROR, "下载检测报告文件URL不存在，请检查数据");
            }
            InputStream fileStream = fileStorageUtil.getFileStream(terminal.getInspectionReport());
            return new InputStreamResource(fileStream);
        }
        if (StrUtil.isBlank(terminal.getOriginalReport())) {
            throw new BusinessException(OssResponseEnum.DOWNLOAD_FILE_ERROR, "下载原始数据报告文件URL不存在，请检查数据");
        }
        InputStream fileStream = fileStorageUtil.getFileStream(terminal.getOriginalReport());

        return new InputStreamResource(fileStream);
    }

    @Override
    public TerminalVO.TerminalStatistics getStatistics(TerminalParam param) {
        TerminalVO.TerminalStatistics statistics = new TerminalVO.TerminalStatistics();
        List<Dept> deptDTOS = deptFeignClient.getDirectSonSelf(param.getId()).getData();
        List<String> ids = deptDTOS.stream().map(Dept::getCode).collect(Collectors.toList());
        List<TerminalVO.OrgStatistics> list = this.baseMapper.selectStatistics(param, ids);
        statistics.setOrgStatistics(getStatisticsList(deptDTOS, list));
        statistics.setDateStatistics(this.baseMapper.selectDateStatistics(param, ids));
        return statistics;
    }

    @Override
    public TerminalVO.TerminalStatistics getCycleStatistics(TerminalParam param) {
        TerminalVO.TerminalStatistics statistics = new TerminalVO.TerminalStatistics();
        List<Dept> deptDTOS = deptFeignClient.getDirectSonSelf(param.getId()).getData();
        List<String> ids = deptDTOS.stream().map(Dept::getCode).collect(Collectors.toList());
        List<TerminalVO.OrgStatistics> list = this.baseMapper.selectCycleStatistics(param, ids);
        statistics.setOrgStatistics(getStatisticsList(deptDTOS, list));
        statistics.setDateStatistics(this.baseMapper.selectResultsCycleStatistics(param, ids));
        return statistics;
    }

    @Override
    public String url(MultipartFile file) {
        return fileStorageUtil.uploadMultipart(file, OssPath.TEST_REPORT);
    }

    /***
     * 统计数据，单位名称赋值
     * @author wr
     * @date 2023-02-28 14:04
     * @param deptDTOS
     * @param list
     * @return List<TerminalStatistics>
     */
    private List<TerminalVO.OrgStatistics> getStatisticsList(List<Dept> deptDTOS, List<TerminalVO.OrgStatistics> list) {
        Map<String, String> deptMap = deptDTOS.stream().collect(Collectors.toMap(Dept::getCode, Dept::getName));
        for (TerminalVO.OrgStatistics t : list) {
            if (deptMap.containsKey(t.getOrgNo())) {
                t.setOrgName(deptMap.get(t.getOrgNo()));
            }
        }
        Map<String, TerminalVO.OrgStatistics> listMap = list.stream().collect(
                Collectors.toMap(TerminalVO.OrgStatistics::getOrgNo, Function.identity()));

        List<Dept> notDeptDTOS = deptDTOS.stream().filter(r -> !listMap.containsKey(r.getCode()))
                .collect(Collectors.toList());
        if (CollectionUtil.isNotEmpty(notDeptDTOS)) {
            TerminalVO.OrgStatistics notTerminal;
            for (Dept notDeptDTO : notDeptDTOS) {
                notTerminal = new TerminalVO.OrgStatistics();
                notTerminal.setOrgNo(notDeptDTO.getCode());
                notTerminal.setOrgName(notDeptDTO.getName());
                notTerminal.setCount(0);
                list.add(notTerminal);
            }
        }
        return list;
    }

    /***
     * 批量导入终端入网检测信息
     * @author wr
     * @date 2023-02-28 14:05
     * @param terminalList
     */
    private void saveTerminalBase(List<TerminalExcel> terminalList) {
        List<DictData> manufacturerList = dicDataFeignClient.getDicDataByTypeCode(DicDataTypeEnum.DEV_MANUFACTURER.getCode()).getData();
        //厂商信息
        Map<String, String> manufacturerMap = manufacturerList.stream().collect(Collectors.toMap(DictData::getName, DictData::getId));
        List<PvTerminalTreeVO> data = deptFeignClient.allDeptList().getData();
        List<PmsTerminalDetection> list = this.list(new LambdaQueryWrapper<PmsTerminalDetection>()
                .eq(PmsTerminalDetection::getStatus, DataStateEnum.ENABLE.getCode()));
        Map<String, PmsTerminalDetection> terminalMap = list.stream().collect(Collectors.toMap(p -> p.getId(), Function.identity()));
        List<TerminalExcel.TerminalExcelMsg> terminalExcelMsg = new ArrayList<>();
        //错误数据初始化
        TerminalExcel.TerminalExcelMsg msg = null;
        List<PmsTerminalDetection> pmsTerminalDetections = new ArrayList<>();
        //正常数据
        PmsTerminalDetection detection;
        for (TerminalExcel terminalExcel : terminalList) {
            //根据终端编号和终端名称判断数据是否又重复终端
            if (terminalMap.containsKey(terminalExcel.getId())) {
                addMsg(terminalExcelMsg, terminalExcel, msg, "当前终端编号重复");
                continue;
            }
            String orgName = terminalExcel.getOrgName().trim();
            //判断
            List<PvTerminalTreeVO> deptList = data.stream().filter(d ->
                    d.getName().indexOf(orgName) > -1
            ).collect(Collectors.toList());
            if (CollectionUtil.isEmpty(deptList)) {
                addMsg(terminalExcelMsg, terminalExcel, msg, "当前部门机构不存在");
                continue;
            }
            if (deptList.size() > 1) {
                addMsg(terminalExcelMsg, terminalExcel, msg, "当前部门机构模糊匹配出现多个部门,请详细编写部门机构");
                continue;
            }
            if (!manufacturerMap.containsKey(terminalExcel.getManufacture())) {
                addMsg(terminalExcelMsg, terminalExcel, msg, "当前生产厂商名称不存在,请检查生产厂商名称");
                continue;
            } else {
                terminalExcel.setManufacture(manufacturerMap.get(terminalExcel.getManufacture()));

            }
            detection = BeanUtil.copyProperties(terminalExcel, PmsTerminalDetection.class);
            detection.setOrgNo(deptList.get(0).getCode());
            detection.setOrgName(deptList.get(0).getName());
            LocalDate inspectionTime = detection.getInspectionTime();
            detection.setNextInspectionTime(inspectionTime.plusYears(5));
            detection.setTestResults(0);
            detection.setStatus(1);
            pmsTerminalDetections.add(detection);
        }

        this.saveBatch(pmsTerminalDetections);

        //错误信息不为空，则以excel的形式输出到前台
        if (CollectionUtil.isNotEmpty(terminalExcelMsg)) {
            ExcelUtil.exportExcel("失败列表.xlsx", TerminalExcel.TerminalExcelMsg.class, terminalExcelMsg);
        }

    }

    /***
     * 错误信息，赋值
     * @author wr
     * @date 2023-02-28 14:05
     * @param terminalExcelMsg
     * @param terminalExcel
     * @param msg
     * @param content
     */
    private void addMsg(List<TerminalExcel.TerminalExcelMsg> terminalExcelMsg, TerminalExcel terminalExcel, TerminalExcel.TerminalExcelMsg msg, String content) {
        msg = BeanUtil.copyProperties(terminalExcel, TerminalExcel.TerminalExcelMsg.class);
        msg.setMsg(content);
        terminalExcelMsg.add(msg);
    }
}
