package com.tofly.qualitycontrol.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tofly.api.UserClent;
import com.tofly.common.core.util.DateUtil;
import com.tofly.common.oauth.auth.ToflyUser;
import com.tofly.pojo.message.Messagepush;
import com.tofly.qualitycontrol.common.QualityControlConstant;
import com.tofly.qualitycontrol.dto.PlanDTO;
import com.tofly.qualitycontrol.dto.PlanReportDTO;
import com.tofly.qualitycontrol.entity.Plan;
import com.tofly.qualitycontrol.entity.PlanAuditor;
import com.tofly.qualitycontrol.entity.PlanProcess;
import com.tofly.qualitycontrol.exception.QualityControlBusinessException;
import com.tofly.qualitycontrol.mapper.PlanMapper;
import com.tofly.qualitycontrol.qcenum.QualityControlMessageTopicEnum;
import com.tofly.qualitycontrol.qcenum.QualityControlMessageTypeEnum;
import com.tofly.qualitycontrol.service.*;
import com.tofly.qualitycontrol.util.QualityControlUtil;
import com.tofly.qualitycontrol.vo.PlanAuditDetailVO;
import com.tofly.qualitycontrol.vo.PlanAuditHistoryVO;
import com.tofly.qualitycontrol.vo.PlanAuditVO;
import com.tofly.qualitycontrol.vo.PlanManageVO;
import lombok.RequiredArgsConstructor;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

import static com.google.common.collect.Lists.newArrayList;
import static com.tofly.common.oauth.util.SecurityUtils.getUser;
import static com.tofly.common.oauth.util.SecurityUtils.getUserId;
import static com.tofly.qualitycontrol.util.QualityControlUtil.*;
import static java.lang.Long.parseLong;
import static java.util.Arrays.stream;
import static java.util.Objects.isNull;
import static java.util.Objects.nonNull;
import static java.util.stream.Collectors.toList;

/**
 * @author B
 */
@Service
@RequiredArgsConstructor
public class PlanServiceImpl extends ServiceImpl<PlanMapper, Plan> implements IPlanService, QualityControlConstant {

    private final IPlanProcessService planProcessService;
    private final IPlanAuditorService planAuditorService;
    private final IQualityControlFileService fileService;
    @Value("${xrty.ftp.qualityControl}")
    private String certificationOrderFilePath;
    private final IQualityControlMessageRecipientService qualityControlMessageRecipientService;
    private final UserClent userClent;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean savePlan(Plan plan, List<MultipartFile> fileList) {
        validatePlan(plan);

        if (CollectionUtils.isNotEmpty(fileList)) {
            StringBuilder fileIdList = fileService.saveFileList(fileList, certificationOrderFilePath, PLAN_TABLE_NAME);
            if (fileIdList == null) {
                return false;
            }
            plan.setFileIds(fileIdList.toString());
        }

        ToflyUser toflyUser = getUser();

        plan.setReporterId(toflyUser.getUserid())
                .setReporterName(toflyUser.getUsername())
                .setReportTime(DateUtil.getCurrentTime())
                .setPlanStatus(PLAN_NOT_REPORTED);
        return save(plan);
    }

    @Override
    public Boolean updatePlan(Plan plan, List<MultipartFile> fileList) {
        validatePlan(plan);

        if (!PLAN_NOT_REPORTED.equals(plan.getPlanStatus()) && !PLAN_REJECTED.equals(plan.getPlanStatus())) {
            throw new QualityControlBusinessException("只能修改未上报的计划");
        }

        Boolean flag = true;


        //存储文件
        String fileIdsdb = getById(plan.getId()).getFileIds();
        String fileIds = plan.getFileIds();
        StringBuilder files = saveFiles(fileList, fileIdsdb, fileIds);
        if (files == null && CollUtil.isNotEmpty(fileList)) {
            return false;
        }
        if (StringUtils.isEmpty(files)) {
            this.update(new UpdateWrapper<Plan>().lambda().set(Plan::getFileIds, "").eq(Plan::getId, plan.getId()));
        }
        plan.setFileIds(files.toString());

        return updateById(plan) && flag;
    }

    /**
     * 存储文件
     *
     * @param fileList
     * @param fileIdsdb
     * @param fileIds
     * @return
     */
    private StringBuilder saveFiles(List<MultipartFile> fileList, String fileIdsdb, String fileIds) {
        StringBuilder fileIdList = null;
        if (CollectionUtils.isNotEmpty(fileList)) {
            fileIdList = fileService.saveFileList(fileList, certificationOrderFilePath, RECTIFY_ORDER_TABLE_NAME);
            if (fileIdList == null || fileIdList.length() == 0) {
                return null;
            }
        }
        fileIds = delRepeat(fileIds);
        String fs = fileIds;
        if (StringUtils.isNotEmpty(fileIdsdb) && !fileIdsdb.equals(fileIds)) {
            List<Long> removeFileId = new ArrayList<>();
            if (StringUtils.isEmpty(fileIds)) {
                removeFileId = Arrays.stream(fileIdsdb.split(CSV)).map(s -> Long.parseLong(s.trim()))
                        .collect(Collectors.toList());
            } else {
                removeFileId = Arrays.stream(fileIdsdb.split(CSV)).filter(st -> !fs.contains(st)).map(st -> Long.parseLong(st.trim())).collect(Collectors.toList());
            }
            // 删除对应文件数据
            if (!removeFileId.isEmpty()) {
                // 删除远程文件
                fileService.removeFileList(removeFileId);
            }
        }
        StringBuilder files = new StringBuilder();

        if (StringUtils.isEmpty(fileIds) || StringUtils.isEmpty(fileIdList)) {
            if (StringUtils.isNotEmpty(fileIds)) {
                files.append(fileIds);
            } else if (StringUtils.isNotEmpty(fileIdList)) {
                files.append(fileIdList);
            }
        } else {
            files.append(fileIds).append(CSV).append(fileIdList);
        }
        return files;
    }


    private String delRepeat(String str) {
        StringBuilder sb = new StringBuilder();
        if (StringUtils.isNotEmpty(str)) {
            List<Long> removeFileId = Arrays.stream(str.split(CSV)).filter(s -> StringUtils.isNotEmpty(s)).map(s -> Long.parseLong(s.trim())).collect(Collectors.toList());
            for (Long l : removeFileId) {
                sb.append(l).append(",");
            }
            sb.delete(sb.length() - 1, sb.length());
            return sb.toString();
        }
        return null;
    }

    @Override
    public IPage<PlanManageVO> getManageList(String planTime, String planStatus, Page<Plan> page) {
        Boolean flag1 = isNotNull(planTime);
        Boolean flag2 = isNotNull(planStatus);
        List<Long> planIdList = planAuditorService.list(Wrappers.<PlanAuditor>lambdaQuery()
                .eq(PlanAuditor::getAuditorId, getUserId())
                .eq(PlanAuditor::getStatus, PLAN_STATUS_TO_AUDIT))
                .stream().map(PlanAuditor::getPlanId).collect(toList());

        LambdaQueryWrapper<Plan> queryWrapper = Wrappers.<Plan>lambdaQuery()
                .eq(flag1, Plan::getPlanTime, planTime)
                .eq(flag2, Plan::getPlanStatus, planStatus)
                .and(r -> r.eq(Plan::getReporterDeptId, getUser().getDepartmentId()).or().in(CollUtil.isNotEmpty(planIdList), Plan::getId, planIdList))
                .orderByDesc(Plan::getReportTime);

        List<PlanManageVO> voList = newArrayList();

        page(page, queryWrapper).getRecords().forEach(l -> {
            PlanManageVO vo = getVO(l, PlanManageVO.class);

            String fileIds = l.getFileIds();

            Boolean flag = isNotNull(fileIds);

            if (flag) {
                List<Long> list = stream(fileIds.split(",")).map(s -> parseLong(s.trim())).collect(toList());

                vo.setFileInfo(fileService.getFileInfo(list));
            }
            voList.add(vo);
        });

        return getIPage(page, voList, count(queryWrapper));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean reportPlan(PlanReportDTO planReportDTO) {
        Long id = planReportDTO.getId();
        List<PlanAuditor> planAuditorList = planReportDTO.getPlanAuditorList();

        List<PlanAuditor> oldpa = planAuditorService.list(Wrappers.<PlanAuditor>lambdaQuery()
                .eq(PlanAuditor::getPlanId, id).orderByAsc(PlanAuditor::getAuditLevel));

        List<Long> auditorIds = oldpa.stream().map(PlanAuditor::getId).collect(toList());

        Boolean flag1 = true;

        //修改审核状态
        Plan plan = getById(id);
        plan.setPlanStatus(PLAN_AUDITING).setPlanReportTime(DateUtil.getCurrentTime());
        if (!updateById(plan)) {
            return false;
        }
        //重新上报时,清空之前的审核人
        if (CollectionUtils.isNotEmpty(auditorIds)) {
            planAuditorService.update(new UpdateWrapper<PlanAuditor>().lambda().set(PlanAuditor::getStatus, "").in(PlanAuditor::getId, auditorIds));
            planAuditorService.update(new UpdateWrapper<PlanAuditor>().lambda().set(PlanAuditor::getStatus, "1").eq(PlanAuditor::getId, oldpa.get(0).getId()));
            qualityControlMessageRecipientService.removeDB(id, QualityControlMessageTopicEnum.PLAN_AUDIT_PROCESS);
        }
        planAuditorList.forEach(planAuditor -> {
            Integer auditLevel = planAuditor.getAuditLevel();

            Boolean flag = isNull(auditLevel);
            if (flag) {
                throw new QualityControlBusinessException("审核等级不能为空");
            }

            if (auditLevel == 1){
                planAuditor.setStatus(PLAN_STATUS_TO_AUDIT);
                Messagepush messagePush = getMessagePush(id, PLAN_TO_AUDIT_MESSAGE, QualityControlMessageTopicEnum.PLAN_AUDIT, QualityControlMessageTypeEnum.MESSAGE_TYPE_REPORT);
                messagePush.setRouteIp("/qualityTesting/QualityPlan/planReview");
                qualityControlMessageRecipientService.overrideSetMessageRecipient(messagePush, planAuditor.getAuditorId());
            }else {
                planAuditor.setStatus("");
            }
            planAuditor.setPlanId(id);

        });
        if (CollectionUtils.isEmpty(auditorIds)) {
            planAuditorService.insertBatch(planAuditorList);
        }
        return true;
    }

    @Override
    public IPage<PlanAuditVO> getAuditList(Long reporterDeptIdId, String planTime, String planStatus, Page<Plan> page) {
        Boolean flag1 = nonNull(reporterDeptIdId);
        Boolean flag2 = isNotNull(planTime);
        List<PlanAuditVO> voList = newArrayList();

        Long userId = getUserId();

        List<Long> planIdList = planAuditorService.list(Wrappers.<PlanAuditor>lambdaQuery()
                .eq(PlanAuditor::getAuditorId, userId)
                .eq(PlanAuditor::getStatus, PLAN_STATUS_TO_AUDIT))
                .stream().map(PlanAuditor::getPlanId).collect(toList());
        if (CollUtil.isEmpty(planIdList)) {
            return getIPage(page, voList, 0);
        }
        Long depid = getUser().getDepartmentId();
        LambdaQueryWrapper<Plan> queryWrapper = Wrappers.<Plan>lambdaQuery()
                .eq(flag1, Plan::getReporterDeptId, reporterDeptIdId)
                .eq(flag2, Plan::getPlanTime, planTime)
                .eq(isNotNull(planStatus), Plan::getPlanStatus, planStatus)
                .in(CollUtil.isNotEmpty(planIdList), Plan::getId, planIdList)
                //.and(r->r.eq(Plan::getReporterDeptId,depid).or().in(CollUtil.isNotEmpty(planIdList),Plan::getId,planIdList))
                .orderByDesc(Plan::getReportTime);


        Page<Plan> planPage = page(page, queryWrapper);
        if (CollUtil.isNotEmpty(planPage.getRecords())) {
            for (Plan l : planPage.getRecords()) {
                PlanAuditVO vo = new PlanAuditVO();
                BeanUtils.copyProperties(l, vo);
                String fileIds = l.getFileIds();
                Boolean flag = isNotNull(fileIds);
                if (flag) {
                    List<Long> list = stream(fileIds.split(",")).map(s -> parseLong(s.trim())).collect(toList());
                    vo.setFileInfo(fileService.getFileInfo(list));
                }
                voList.add(vo);
            }
        }
        return getIPage(page, voList, planPage.getTotal());
    }

    @Override
    public PlanDTO getDetailAndHistoryList(Long id, Long type) {

        PlanAuditDetailVO planAuditDetailVO = getVO(getById(id), PlanAuditDetailVO.class);
        List<PlanAuditHistoryVO> historyList = null;
        if (1 == type) {
            historyList = planProcessService.getHistoryList(id);
        } else {
            historyList = planAuditorService.getExamine(id);
        }
        return PlanDTO.builder().voList(historyList).planAuditDetailVO(planAuditDetailVO).build();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean auditPlan(Long id, String auditResult, String auditDesc) {
        //获取待审核人
        PlanAuditor planAuditor = planAuditorService.getOne(Wrappers.<PlanAuditor>lambdaQuery()
                .eq(PlanAuditor::getPlanId, id)
                .eq(PlanAuditor::getStatus, PLAN_STATUS_TO_AUDIT));
        if (planAuditor == null || planAuditor.getAuditorId() == null || !planAuditor.getAuditorId().equals(getUserId())) {
            return false;
        }
        Integer currentLevel = planAuditor.getAuditLevel();
        Integer maxLevel = planAuditorService.getMaxLevel(id);

        Boolean flag1 = true;
        Boolean flag2 = true;
        Boolean flag3 = true;
        Boolean flag4 = true;

        //1级驳回
        if (1 == currentLevel && PLAN_PROCESS_REJECTED.equals(auditResult)) {
            //更新总状态
            Plan plan = getById(id);
            plan.setPlanStatus(PLAN_REJECTED);
            flag1 = updateById(plan);

            //人员状态改默认值
            planAuditor.setStatus(PLAN_STATUS_DEFAULT);
            flag3 = planAuditorService.updateById(planAuditor);

            //生成流程记录
            PlanProcess planProcess = getVO(planAuditor, PlanProcess.class);
            planProcess.setAuditResult(PLAN_PROCESS_REJECTED).setAuditDesc(auditDesc).setAuditTime(DateUtil.getCurrentTime());
            flag2 = planProcessService.save(planProcess);
            //1级驳回tableid为计划id
            Messagepush messagePush = getMessagePush(id, PLAN_REJECTED_MESSAGE, QualityControlMessageTopicEnum.PLAN_AUDIT_PROCESS, QualityControlMessageTypeEnum.MESSAGE_TYPE_REJECTED);
            // /qualityTesting/QualityPlan/planManagement
            //1级别驳回应直接跳转计划管理
            messagePush.setRouteIp("/qualityTesting/QualityPlan/planManagement");
            qualityControlMessageRecipientService.overrideSetMessageRecipient(messagePush, plan.getReporterId());
            qualityControlMessageRecipientService.removeDB(id, QualityControlMessageTopicEnum.PLAN_AUDIT);
        }
        //最大等级通过
        else if (maxLevel.equals(currentLevel) && PLAN_PROCESS_PASSED.equals(auditResult)) {
            Plan plan = getById(id);
            plan.setPlanStatus(PLAN_AUDIT_FINISHED);
            flag1 = updateById(plan);

            planAuditor.setStatus(PLAN_STATUS_DEFAULT);
            flag3 = planAuditorService.updateById(planAuditor);

            PlanProcess planProcess = getVO(planAuditor, PlanProcess.class);
            planProcess.setAuditResult(PLAN_PROCESS_PASSED).setAuditDesc(auditDesc).setAuditTime(DateUtil.getCurrentTime());
            flag2 = planProcessService.save(planProcess);

            Messagepush messagePush = getMessagePush(planProcess.getId(), PLAN_FINISHED_MESSAGE, QualityControlMessageTopicEnum.PLAN_AUDIT_PROCESS, QualityControlMessageTypeEnum.MESSAGE_TYPE_ORDINARY_MESSAGE);
            // /qualityTesting/QualityPlan/planManagement
            messagePush.setRouteIp("/qualityTesting/QualityPlan/planReview");
            qualityControlMessageRecipientService.setMessageRecipient(messagePush, plan.getReporterId());

            if (currentLevel == 1) {
                qualityControlMessageRecipientService.removeDB(id, QualityControlMessageTopicEnum.PLAN_AUDIT);
            } else {
                //后续生成待办为主题为质检计划流程，首先查询
                List<PlanProcess> processList = planProcessService.list(Wrappers.<PlanProcess>lambdaQuery()
                        .eq(PlanProcess::getPlanId, id)
                        .eq(PlanProcess::getAuditLevel, currentLevel - 1)
                        .eq(PlanProcess::getAuditResult,"1")
                        .orderByDesc(PlanProcess::getAuditTime)
                        .select(PlanProcess::getId));
                PlanProcess process = processList.get(0);
                qualityControlMessageRecipientService.removeDB(process.getId(), QualityControlMessageTopicEnum.PLAN_AUDIT_PROCESS);
            }
        }
        //中间等级;1级通过;最大等级驳回
        else {
            if (PLAN_PROCESS_PASSED.equals(auditResult)) {
                Integer nextLevel = currentLevel + 1;
                List<PlanAuditor> nextLevelAuditorList = planAuditorService.list(Wrappers.<PlanAuditor>lambdaQuery()
                        .eq(PlanAuditor::getAuditLevel, nextLevel)
                        .eq(PlanAuditor::getPlanId, id).orderByDesc(PlanAuditor::getId));
                PlanAuditor nextLevelAuditor = nextLevelAuditorList.get(0);

                nextLevelAuditor.setStatus(PLAN_STATUS_TO_AUDIT);
                flag4 = planAuditorService.updateById(nextLevelAuditor);

                planAuditor.setStatus(PLAN_STATUS_DEFAULT);
                flag3 = planAuditorService.updateById(planAuditor);

                PlanProcess planProcess = getVO(planAuditor, PlanProcess.class);
                planProcess.setAuditResult(PLAN_PROCESS_PASSED).setAuditDesc(auditDesc).setAuditTime(DateUtil.getCurrentTime());
                flag2 = planProcessService.save(planProcess);

                Messagepush messagePush = getMessagePush(planProcess.getId(), PLAN_TO_AUDIT_MESSAGE, QualityControlMessageTopicEnum.PLAN_AUDIT_PROCESS, QualityControlMessageTypeEnum.MESSAGE_TYPE_AUDIT);
                ///qualityTesting/QualityPlan/planManagement
                messagePush.setRouteIp("qualityTesting/QualityPlan/planReview");
                qualityControlMessageRecipientService.overrideSetMessageRecipient(messagePush, nextLevelAuditor.getAuditorId());
                //当审核登记为为1时，主题为质检计划，消除质检计划待办
                if (currentLevel == 1) {
                    qualityControlMessageRecipientService.removeDB(id, QualityControlMessageTopicEnum.PLAN_AUDIT);
                } else if (currentLevel < maxLevel) {
                    //后续生成待办为主题为质检计划流程，首先查询,一级审核可能有多驳回、审核多条数据所以不能查单个
                    List<PlanProcess> process = planProcessService.list(Wrappers.<PlanProcess>lambdaQuery()
                            .eq(PlanProcess::getPlanId, id)
                            .eq(PlanProcess::getAuditLevel, currentLevel - 1)
                            .select(PlanProcess::getId));
                    for (PlanProcess pro : process) {
                        qualityControlMessageRecipientService.removeDB(pro.getId(), QualityControlMessageTopicEnum.PLAN_AUDIT_PROCESS);
                    }
                }

            } else {
                Integer previousLevel = currentLevel - 1;
                List<PlanAuditor> previousLevelAuditorList = planAuditorService.list(Wrappers.<PlanAuditor>lambdaQuery()
                        .eq(PlanAuditor::getAuditLevel, previousLevel)
                        .eq(PlanAuditor::getPlanId, id));
                PlanAuditor previousLevelAuditor = previousLevelAuditorList.get(0);
                previousLevelAuditor.setStatus(PLAN_STATUS_TO_AUDIT);
                flag4 = planAuditorService.updateById(previousLevelAuditor);

                planAuditor.setStatus(PLAN_STATUS_DEFAULT);
                flag3 = planAuditorService.updateById(planAuditor);

                PlanProcess planProcess = getVO(planAuditor, PlanProcess.class);
                planProcess.setAuditResult(PLAN_PROCESS_REJECTED).setAuditDesc(auditDesc).setAuditTime(DateUtil.getCurrentTime());
                flag2 = planProcessService.save(planProcess);

                Messagepush messagePush = getMessagePush(planProcess.getId(), PLAN_REJECTED_MESSAGE, QualityControlMessageTopicEnum.PLAN_AUDIT_PROCESS, QualityControlMessageTypeEnum.MESSAGE_TYPE_REJECTED);
                ///qualityTesting/QualityPlan/planManagement
                messagePush.setRouteIp("/qualityTesting/QualityPlan/planReview");
                qualityControlMessageRecipientService.overrideSetMessageRecipient(messagePush, previousLevelAuditor.getAuditorId());
            }
        }
        //驳回时清除当前人员待办
        if ("2".equals(auditResult)) {
            qualityControlMessageRecipientService.removeDB(planAuditor.getId(), QualityControlMessageTopicEnum.PLAN_AUDIT_PROCESS);
            qualityControlMessageRecipientService.removeDB(id, QualityControlMessageTopicEnum.PLAN_AUDIT);
        }
        return flag1 && flag2 && flag3 && flag4;
    }

    private void validatePlan(Plan plan) {
        String planTime = plan.getPlanTime();
        String planType = plan.getPlanType();

        if (QualityControlUtil.isNull(planTime) || QualityControlUtil.isNull(planType)) {
            throw new QualityControlBusinessException("计划时间或计划类型不能为空");
        }

        Plan p = getOne(Wrappers.<Plan>lambdaQuery().eq(Plan::getPlanTime, planTime).eq(Plan::getReporterDeptId, plan.getReporterDeptId()).eq(Plan::getPlanType, planType));

        if (p != null && !p.getId().equals(plan.getId())) {
            throw new QualityControlBusinessException("已存在该部门该时间质检计划");
        }
    }

}
