package com.qmth.sop.business.service.impl;

import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qmth.sop.business.bean.dto.DataPermissionDto;
import com.qmth.sop.business.bean.result.analyze.sopWarn.*;
import com.qmth.sop.business.entity.SysUser;
import com.qmth.sop.business.entity.TBService;
import com.qmth.sop.business.entity.TBSopInfo;
import com.qmth.sop.business.mapper.SopAnalyseMapper;
import com.qmth.sop.business.service.SopAnalyseService;
import com.qmth.sop.business.service.SysUserService;
import com.qmth.sop.common.contant.SystemConstant;
import com.qmth.sop.common.enums.SopAnalyseGroupEnum;
import com.qmth.sop.common.enums.SopAnalyseSortEnum;
import com.qmth.sop.common.util.ServletUtil;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class SopAnalyseServiceImpl extends ServiceImpl<SopAnalyseMapper, TBSopInfo> implements SopAnalyseService {
    @Resource
    private SysUserService sysUserService;

    @Override
    public List<TBService> list(Long startTime,Long endTime) {
        SysUser requestUser = (SysUser) ServletUtil.getRequestUser();
        DataPermissionDto dpr = sysUserService.buildUserDataPermission(requestUser.getId());
        return this.baseMapper.list(startTime,endTime,dpr);
    }


    /**
     * * ①可按照大区/人力供应商进行切换如图；
     * * ②按照派单数TOP3的大区、人力供应商显示；
     * * ③完成进度：该大区/供应商派单的完成进度，按照派单已有完成SOP，且没有在执行的SOP认为是已完成的派单；
     * * ④平均处理时限：该大区/供应商预警处理的平均时限。按照预警产生到关闭的时长的均值，≥1小时，按小时显示；＜小时，按分钟显示；
     */
    @Deprecated
    @Override
    public List<Map<String, Object>> overview1(Long serviceId, SopAnalyseGroupEnum group) {
        List<Map<String, Object>> list = this.baseMapper.overview(serviceId);
        List<Map<String, Object>> result = new ArrayList<>();
        if (group.equals(SopAnalyseGroupEnum.REGION)) {
            list.stream().filter(map->map.get("region_name")!=null).collect(Collectors.groupingBy(map -> map.get("region_name"))).forEach((k, v) -> {
                Map<String, Object> map = new HashMap<>();
                map.put("region_name", k);
                processOverviewData(v, map);
                result.add(map);
            });
        } else if (group.equals(SopAnalyseGroupEnum.SUPPLIER)) {
            list.stream().filter(map -> map.get("supplier")!=null).collect(Collectors.groupingBy(map -> map.get("supplier"))).forEach((k, v) -> {
                Map<String, Object> map = new HashMap<>();
                map.put("supplier", k);
                processOverviewData(v, map);
                result.add(map);
            });
        }
        return result.stream().sorted(Comparator.comparingInt(o -> Integer.parseInt(o.get("crmNum").toString()))).limit(3).collect(Collectors.toList());
    }

    /**
     * * ①可按照大区/人力供应商进行切换如图；
     * * ②按照派单数TOP3的大区、人力供应商显示；
     * * ③完成进度：该大区/供应商派单的完成进度，按照派单已有完成SOP，且没有在执行的SOP认为是已完成的派单；
     * * ④平均处理时限：该大区/供应商预警处理的平均时限。按照预警产生到关闭的时长的均值，≥1小时，按小时显示；＜小时，按分钟显示；
     */
    @Override
    public List<SopWarnOverview> overview(Long serviceId, SopAnalyseGroupEnum group) {
        List<SopMonitor> sopMonitorList = this.baseMapper.findSopMonitorListByServiceId(serviceId);
        List<SopWarnOverview> result = new ArrayList<>();
        switch (group) {
            case REGION:
                Set<Long> regionIdSet = sopMonitorList.stream().filter(e -> e.getRegionId() != null && e.getRegionId() > 0).map(SopMonitor::getRegionId).collect(Collectors.toSet());
                for (Long regionId : regionIdSet) {
                    List<SopMonitor> dataCell = sopMonitorList.stream().filter(e -> regionId.equals(e.getRegionId())).collect(Collectors.toList());
                    String regionName = dataCell.get(0).getRegionName();
                    SopWarnOverview regionOverview = buildSopOverviewStatistic(dataCell);
                    regionOverview.setRegionId(regionId);
                    regionOverview.setRegionName(regionName);
                    result.add(regionOverview);
                }
                break;
            case SUPPLIER:
                // 由大区经理兼任不显示
                Set<Long> supplierIdSet = sopMonitorList.stream().filter(e -> e.getSupplierId() != null && e.getSupplierId() > 0).map(SopMonitor::getSupplierId).collect(Collectors.toSet());
                for (Long supplierId : supplierIdSet) {
                    List<SopMonitor> dataCell = sopMonitorList.stream().filter(e -> supplierId.equals(e.getSupplierId())).collect(Collectors.toList());
                    SopWarnOverview supplierOverview = buildSopOverviewStatistic(dataCell);
                    supplierOverview.setSupplierName(dataCell.get(0).getSupplierName());
                    supplierOverview.setSupplierId(supplierId);
                    result.add(supplierOverview);
                }
                break;
        }
        return result;
    }

    /**
     * 创建sop监控统计
     * @param dataCell 数据集
     * @return sop监控
     */
    private SopWarnOverview buildSopOverviewStatistic(List<SopMonitor> dataCell) {
        SopWarnOverview sopWarnOverview = new SopWarnOverview();
        if (CollectionUtils.isNotEmpty(dataCell)) {
            int crmNum = (int) dataCell.stream().map(SopMonitor::getCrmNo).distinct().count();
            int finishCrmNum = (int) (crmNum - dataCell.stream().filter(e -> !e.getFinishStatus()).map(SopMonitor::getCrmNo).distinct().count());
            int effectAvgTimeCount = 0;
            long effectTotalMinutes = 0L;
            int violationNum = 0;
            int finishViolationNum = 0;
            int delayNum = 0;
            int finishDelayNum = 0;

            for (SopMonitor sopMonitor : dataCell) {
                SopMonitorEnum sopMonitorType = sopMonitor.getSopMonitorType();
                switch (sopMonitorType) {
                    case DELAY_WARN:
                        delayNum++;
                        break;
                    case VIOLATION:
                        violationNum++;
                        break;
                }


                Boolean finishStatus = sopMonitor.getFinishStatus();
                if (finishStatus) {
                    Long startTime = sopMonitor.getStartTime();
                    Long closeTime = sopMonitor.getCloseTime();
                    if (startTime != null && startTime > 0 && closeTime != null && closeTime > 0) {
                        effectAvgTimeCount++;
                        long cellMinutes = DateUtil.between(new Date(startTime), new Date(closeTime), DateUnit.MINUTE);
                        effectTotalMinutes = effectTotalMinutes + cellMinutes;
                    }

                    switch (sopMonitorType) {
                        case DELAY_WARN:
                            finishDelayNum++;
                            break;
                        case VIOLATION:
                            finishViolationNum++;
                            break;
                    }
                }
            }

            sopWarnOverview.setCrmNum(crmNum);
            sopWarnOverview.setFinishCrmNum(finishCrmNum);
            BigDecimal avgMinutes = new BigDecimal(0);
            if (effectAvgTimeCount > 0) {
                avgMinutes = new BigDecimal(effectTotalMinutes).divide(new BigDecimal(effectAvgTimeCount), 2, RoundingMode.HALF_UP);
            }
            sopWarnOverview.setAvgMinutes(avgMinutes);
            sopWarnOverview.setViolationNum(violationNum);
            sopWarnOverview.setFinishViolationNum(finishViolationNum);
            sopWarnOverview.setDelayNum(delayNum);
            sopWarnOverview.setFinishDelayNum(finishDelayNum);
        }
        return sopWarnOverview;
    }


    private static void processOverviewData(List<Map<String, Object>> v, Map<String, Object> map) {
        //派单数
        map.put("crmNum", v.stream().collect(Collectors.groupingBy(map1 -> map1.get("crm_no"))).size());
        //已完成派单数
        map.put("finishCrmNum", v.stream().collect(Collectors.groupingBy(map1 -> map1.get("crm_no"))).values().stream().filter(list1 -> list1.stream().allMatch(map1 -> "FINISH".equals(map1.get("sopStatus"))) || list1.stream().allMatch(map1 -> "FINISH".equals(map1.get("status")))).count());
        //违规数
        map.put("violationNum", v.stream().filter(map1 -> map1.get("vid") != null).collect(Collectors.groupingBy(map1 -> map1.get("vid"))).size());
        //已关闭违规数
        map.put("finishViolationNum", v.stream().filter(map1 -> map1.get("vid") != null && "CLOSE".equals(map1.get("vstatus"))).collect(Collectors.groupingBy(map1 -> map1.get("vid"))).size());
        //延期数
        map.put("delayNum", v.stream().filter(map1 -> map1.get("did") != null).collect(Collectors.groupingBy(map1 -> map1.get("did"))).size());
        //已关闭延期数
        map.put("finishDelayNum", v.stream().filter(map1 -> map1.get("did") != null && "CLOSE".equals(map1.get("dstatus"))).collect(Collectors.groupingBy(map1 -> map1.get("vid"))).size());
        Double processViolationTime = v.stream().filter(map1 -> map1.get("vdiff") != null).mapToDouble(map1 -> Double.parseDouble(map1.get("vdiff").toString())).sum();
        Double processDelayTime = v.stream().filter(map1 -> map1.get("ddiff") != null).mapToDouble(map1 -> Double.parseDouble(map1.get("ddiff").toString())).sum();
        //平均处理时限分钟
        int total = (Integer.parseInt(map.get("finishViolationNum").toString()) + Integer.parseInt(map.get("finishDelayNum").toString()));
        map.put("avgMinutes", total == 0 ? 0 : (processViolationTime + processDelayTime) / total);


    }

    /*
     * 4.项目/供应商/大区预警待处理TOP10：
     * ①待处理(待处理预警)：按派单统计待处理的预警数（违规+延期）；
     * ②处理最慢（已处理完毕的预警）：
     * A.平均时限：按照预警产生到关闭的时长的均值，≥1小时，按小时显示；＜小时，按分钟显示；
     * 若预警为关闭后重启，则需要重新从预警产生到最后一次关闭的时长进行计算；
     * B.排序：按平均时限的倒序排列；
     * ③处理最快（已处理完毕的预警）：
     * A.平均时限：同上；
     * B.排序：按平均时限的顺序排列。
     * ④（预警数）均值：由于供应商分配的项目数量不同，所以单从总数无法判定供应商对预警处理的力度。均值=预警总数/派单数；
     * ⑤【数据下钻】不提供。
     */
    @Override
    public List<Map<String, Object>> processing(Long serviceId, SopAnalyseGroupEnum group, SopAnalyseSortEnum sort) {
        List<Map<String, Object>> result = new ArrayList<>();
        List<Map<String, Object>> list = this.baseMapper.overview(serviceId);
        if (group.equals(SopAnalyseGroupEnum.CRM)) {
            list.stream().filter(map->map.get("name")!=null).collect(Collectors.groupingBy(map -> map.get("name"))).forEach((k, v) -> {
                Map<String, Object> map = new HashMap<>();
                map.put("crmName", k);
                //大区
                map.put("region_name", v.get(0).get("region_name"));
                processing(v, map);
                result.add(map);
            });
        } else if (group.equals(SopAnalyseGroupEnum.REGION)) {
            list.stream().filter(map->map.get("region_name")!=null).collect(Collectors.groupingBy(map -> map.get("region_name"))).forEach((k, v) -> {
                Map<String, Object> map = new HashMap<>();
                map.put("region_name", k);
                processing(v, map);
                result.add(map);
            });
        } else if (group.equals(SopAnalyseGroupEnum.SUPPLIER)) {
            list.stream().filter(map->map.get("supplier")!=null).collect(Collectors.groupingBy(map -> map.get("supplier"))).forEach((k, v) -> {
                Map<String, Object> map = new HashMap<>();
                map.put("supplier", k);
                //大区
                map.put("region_name", v.get(0).get("region_name"));
                processing(v, map);
                result.add(map);
            });
        }

        if (sort.equals(SopAnalyseSortEnum.PENDING)) {
            return result.stream().sorted(Comparator.comparingInt(o -> -Integer.parseInt(o.get("pendingProcessing").toString()))).limit(10).collect(Collectors.toList());
        } else if (sort.equals(SopAnalyseSortEnum.SLOWEST)) {
            return result.stream().sorted(Comparator.comparingDouble(o -> -Double.parseDouble(o.get("avgMinutes").toString()))).limit(10).collect(Collectors.toList());
        } else if (sort.equals(SopAnalyseSortEnum.FASTEST)) {
            return result.stream().sorted(Comparator.comparingDouble(o -> Double.parseDouble(o.get("avgMinutes").toString()))).limit(10).collect(Collectors.toList());
        }
        return null;

    }

    private static void processing(List<Map<String, Object>> v, Map<String, Object> map) {
        //区域协调人
        map.put("real_name", v.get(0).get("real_name"));
        //违规数
        Integer violationNum = v.stream().filter(map1 -> map1.get("did") != null).collect(Collectors.groupingBy(map1 -> map1.get("did"))).size();
        //已关闭违规数
        Long finishViolationNum = v.stream().filter(map1 -> map1.get("did") != null && "CLOSE".equals(map1.get("dstatus"))).count();
        //延期数
        Integer delayNum = v.stream().filter(map1 -> map1.get("vid") != null).collect(Collectors.groupingBy(map1 -> map1.get("vid"))).size();
        //已关闭延期数
        Long finishDelayNum = v.stream().filter(map1 -> map1.get("vid") != null && "CLOSE".equals(map1.get("vstatus"))).count();
        Double processViolationTime = v.stream().filter(map1 -> map1.get("ddiff") != null).mapToDouble(map1 -> Double.parseDouble(map1.get("ddiff").toString())).sum();
        Double processDelayTime = v.stream().filter(map1 -> map1.get("vdiff") != null).mapToDouble(map1 -> Double.parseDouble(map1.get("vdiff").toString())).sum();
        //平均处理时限分钟
        long total = finishViolationNum + finishDelayNum;
        map.put("avgMinutes", total == 0 ? 0 : (processViolationTime + processDelayTime) / total);
        //待处理总数数
        map.put("pendingProcessing", violationNum + delayNum - total);
        //预警均值
        map.put("avgWarn", (violationNum + delayNum) / v.stream().collect(Collectors.groupingBy(map1 -> map1.get("crm_no"))).size());
    }

    /*
     * 5.供应商预警均值走势：
     * 供应商近一周预警数的均值走势。
     */
    @Override
    public Map<Object, List<Map<String, Object>>> trend1(Long serviceId) {
        List<Map<String, Object>> list = this.baseMapper.trend(serviceId);
//        Map<Object, List<Map<String, Object>>> collect1 = list.stream().collect(Collectors.groupingBy(map -> map.get("click_date")));
//        Map<Object, Map<Object, List<Map<String, Object>>>> collect = new HashMap<>();
//        collect1.forEach((k, v) -> collect.put(k,v.stream().filter(map -> map.get("supplier") != null).collect(Collectors.groupingBy(map1 -> map1.get("supplier")))));
        return list.stream().filter(map -> map.get("supplier") != null).collect(Collectors.groupingBy(map1 -> map1.get("supplier")));

    }

    /*
     * 5.供应商预警均值走势：
     * 供应商近一周预警数的均值走势。
     * ④（预警数）均值：由于供应商分配的项目数量不同，所以单从总数无法判定供应商对预警处理的力度。均值=预警总数/派单数；
     */
    @Override
    public List<SopSupplierAvgViewTimes> trend(Long serviceId) {
        List<CrmSupplierRelation> crmSupplierRelationList = this.baseMapper.findCrmSupplierRelationByServiceId(serviceId);
        Map<Long, Set<String>> supplierCrmNumMap = new HashMap<>();
        for (CrmSupplierRelation crmSupplierRelation : crmSupplierRelationList) {
            String crmNo = crmSupplierRelation.getCrmNo();
            Long supplierId = crmSupplierRelation.getSupplierId();
            if (supplierCrmNumMap.containsKey(supplierId)) {
                supplierCrmNumMap.get(supplierId).add(crmNo);
            } else {
                Set<String> crmNoSet = new HashSet<>();
                crmNoSet.add(crmNo);
                supplierCrmNumMap.put(supplierId, crmNoSet);
            }
        }


        List<SopMonitor> sopMonitorList = this.baseMapper.findSopMonitorListByServiceId(serviceId);
        Date date = new Date();
        List<Long> times = new ArrayList<>();
        for (int i = 0; i < 7; i++) {
            int v = i + 1;
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(date);
            calendar.add(Calendar.DAY_OF_MONTH, -v);
            calendar.set(Calendar.HOUR_OF_DAY, 24);
            calendar.set(Calendar.MINUTE, 0);
            calendar.set(Calendar.SECOND, 0);
            calendar.set(Calendar.MILLISECOND, 0);
            Long endTime = calendar.getTimeInMillis() - 1;
            times.add(endTime);
        }
        // 7天涉及到的所有供应商信息 id - name 补充零用
        Map<Long, String> supplierInfoMap = new HashMap<>();
        List<SopSupplierAvgViewTimes> result = new ArrayList<>();
        for (Long time : times) {
            // 当天之前的预警（大区经理兼任的不算）
            List<SopMonitor> monitorTimeCell = sopMonitorList
                    .stream()
                    .filter(e -> !e.getFinishStatus() && e.getStartTime() < time && e.getSupplierId() != null && e.getSupplierId() > 0)
                    .collect(Collectors.toList());

            List<Long> supplierIdList = monitorTimeCell.stream().map(SopMonitor::getSupplierId).distinct().collect(Collectors.toList());
            List<SopSupplierAvgView> sopSupplierAvgViewList = new ArrayList<>();
            for (Long supplierId : supplierIdList) {
                List<SopMonitor> monitorSupplierCell = monitorTimeCell
                        .stream()
                        .filter(e -> e.getSupplierId().equals(supplierId))
                        .collect(Collectors.toList());

                if (CollectionUtils.isNotEmpty(monitorSupplierCell)) {
                    String supplierName = monitorSupplierCell.get(0).getSupplierName();
                    int warnCount = monitorSupplierCell.size();
                    if (supplierCrmNumMap.containsKey(supplierId)) {
                        int crmCount = supplierCrmNumMap.get(supplierId).size();
                        BigDecimal rate = new BigDecimal(warnCount).divide(new BigDecimal(crmCount), 2, RoundingMode.HALF_UP);

                        SopSupplierAvgView sopSupplierAvgView = new SopSupplierAvgView();
                        sopSupplierAvgView.setSupplierId(supplierId);
                        sopSupplierAvgView.setRate(rate);
                        sopSupplierAvgView.setSupplierName(supplierName);
                        sopSupplierAvgViewList.add(sopSupplierAvgView);
                        if (!supplierInfoMap.containsKey(supplierId)) {
                            supplierInfoMap.put(supplierId, supplierName);
                        }
                    }

                }
            }

            SopSupplierAvgViewTimes sopSupplierAvgViewTimes = new SopSupplierAvgViewTimes();
            sopSupplierAvgViewTimes.setTime(time);
            sopSupplierAvgViewTimes.setTimeStr(DateFormatUtils.format(time, SystemConstant.DEFAULT_DATE_DAY_PATTERN));
            if (CollectionUtils.isNotEmpty(sopSupplierAvgViewList)) {
                sopSupplierAvgViewTimes.setSopSupplierAvgViewInfo(sopSupplierAvgViewList);
            }
            result.add(sopSupplierAvgViewTimes);

            // 补零
            for (SopSupplierAvgViewTimes supplierAvgViewTimes : result) {
                List<SopSupplierAvgView> list = supplierAvgViewTimes.getSopSupplierAvgViewInfo();
                if (CollectionUtils.isEmpty(list)){
                    list = new ArrayList<>();
                }
                List<Long> alreadyUsedSupplierIdList = list.stream().map(SopSupplierAvgView::getSupplierId).collect(Collectors.toList());
                List<Long> needFillIdList = supplierInfoMap.keySet().stream().filter(k -> !alreadyUsedSupplierIdList.contains(k)).collect(Collectors.toList());
                for (Long supplierId : needFillIdList) {
                    SopSupplierAvgView view = new SopSupplierAvgView();
                    view.setRate(new BigDecimal(0));
                    view.setSupplierId(supplierId);
                    view.setSupplierName(supplierInfoMap.get(supplierId));
                    list.add(view);
                }
            }
        }
        return result;
    }

    /*
      6.项目/大区/供应商考勤异常TOP：
      ①（异常）均值=异常总数/派单数；
      ②【数据下钻】不提供；
     */
    @Override
    public List<Map<String, Object>> attendance(Long serviceId, SopAnalyseGroupEnum group) {
        List<Map<String, Object>> result = new ArrayList<>();
        List<Map<String, Object>> attendance = this.baseMapper.attendance(serviceId);
        if (group.equals(SopAnalyseGroupEnum.CRM)) {
            attendance.stream().filter(map -> map.get("name")!=null).collect(Collectors.groupingBy(map -> map.get("name"))).forEach((k, v) -> v.stream().filter(map -> map.get("dname")!=null).collect(Collectors.groupingBy(map -> map.get("dname"))).forEach((x, y)->{
                Map<String, Object> map = new HashMap<>();
                map.put("crmName", k);
                map.put("dname", x);
                map.put("real_name", v.get(0).get("real_name"));
                map.put("total", y.stream().filter(map1 -> map1.get("id") != null).count());
                result.add(map);
            }));


        } else if (group.equals(SopAnalyseGroupEnum.REGION)) {
            attendance.stream().filter(map -> map.get("region_name")!=null).collect(Collectors.groupingBy(map -> map.get("region_name"))).forEach((k, v) -> {
                Map<String, Object> map = new HashMap<>();
                map.put("region_name", k);
                map.put("real_name", v.get(0).get("real_name"));
                long total = v.stream().filter(map1 -> map1.get("id") != null).count();
                map.put("total", total);
                map.put("avg", Double.parseDouble(total+"")/v.stream().collect(Collectors.groupingBy(map1 -> map1.get("crm_no"))).size());
                result.add(map);
            });

        } else if (group.equals(SopAnalyseGroupEnum.SUPPLIER)) {
            attendance.stream().filter(map -> map.get("supplier")!=null).collect(Collectors.groupingBy(map -> map.get("supplier"))).forEach((k, v) -> {
                Map<String, Object> map = new HashMap<>();
                map.put("supplier", k);
                long total = v.stream().filter(map1 -> map1.get("id") != null).count();
                map.put("total",total);
                map.put("avg", Double.parseDouble(total+"")/v.stream().collect(Collectors.groupingBy(map1 -> map1.get("crm_no"))).size());
                result.add(map);
            });

        }
        return result.stream().sorted(Comparator.comparingInt(o -> -Integer.parseInt(o.get("total").toString()))).limit(10).collect(Collectors.toList());
    }

}
