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

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qmth.sop.business.bean.dto.DataPermissionDto;
import com.qmth.sop.business.bean.result.CrmArchivesAllocationResult;
import com.qmth.sop.business.bean.result.TBCrmResult;
import com.qmth.sop.business.bean.result.UserArchivesAllocationResult;
import com.qmth.sop.business.bean.result.UserArchivesAllocationSubTotalResult;
import com.qmth.sop.business.bean.result.analyze.serviceUnit.RegionPersonnel;
import com.qmth.sop.business.bean.result.analyze.serviceUnit.ServiceUnitOverview;
import com.qmth.sop.business.bean.result.analyze.serviceUnit.ServiceUnitPersonnel;
import com.qmth.sop.business.bean.result.analyze.serviceUnit.SupplierPersonnel;
import com.qmth.sop.business.entity.*;
import com.qmth.sop.business.mapper.ServiceAnalyseMapper;
import com.qmth.sop.business.service.*;
import com.qmth.sop.common.contant.SystemConstant;
import com.qmth.sop.common.enums.CrmStatusEnum;
import com.qmth.sop.common.enums.DeviceStatusEnum;
import com.qmth.sop.common.enums.ExceptionResultEnum;
import com.qmth.sop.common.enums.InOutTypeEnum;
import com.qmth.sop.common.util.ServletUtil;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

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

@Service
public class ServiceAnalyseServiceImpl extends ServiceImpl<ServiceAnalyseMapper, TBService> implements ServiceAnalyseService {

    @Resource
    private TBSopInfoService tbSopInfoService;

    @Resource
    private TBUserArchivesSupplierService tbUserArchivesSupplierService;

    @Resource
    private SysUserService sysUserService;

    @Resource
    private TBUserArchivesAllocationService tbUserArchivesAllocationService;

    @Resource
    private TBCrmService tbCrmService;

    @Resource
    private TBDeviceInOutService tbDeviceInOutService;

    @Resource
    private SysDeviceService sysDeviceService;

    @Resource
    private TBServiceRegionService tbServiceRegionService;

    @Resource
    private SysSupplierService sysSupplierService;

    @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);
    }

    /**
     * 4.服务单元概览
     * ①项目执行进度=服务单元下已完成的派单（研究生派单下SOP已完结；教务处派单下已有完成的SOP，无在执行的SOP。可能存在教务处多个SOP之间空窗期导致数据的误差，这个指标允许少量误差存在）；
     * ②设备出库总量：当前服务单元下已出库且尚未入库的设备总量；
     * ③设备占用率：（设备出库总量÷正常设备总量）×100%；
     * ④在服务人员总数：在执行的SOP现场服务人员数量，包括助理工程师、实施工程师、区域协调人；
     * ⑤现场人员占用率：（在服务人员总数÷认证有效的在服务人员总数）×100%；
     */
    @Deprecated
    @Override
    public Map<String, Object> overview1(Long serviceUnitId) {
        Map<String, Object> map = new HashMap<>();
        List<Map<String, Object>> list = this.baseMapper.projectProgress(serviceUnitId);
        int finishCount = 0, size = 0;
        if (!CollectionUtils.isEmpty(list)) {
            size = list.size();
            finishCount = (int) list.stream().filter(s -> Objects.nonNull(s.get("status")) && s.get("status").equals("FINISH")).count();
        }
        BigDecimal sizeBig = BigDecimal.valueOf(size), percent = BigDecimal.valueOf(100),
                finishCompleteRate = BigDecimal.valueOf(finishCount);
        finishCompleteRate = size > 0 ? finishCompleteRate.divide(sizeBig, 2, BigDecimal.ROUND_HALF_UP).multiply(percent).setScale(2, BigDecimal.ROUND_HALF_UP) : sizeBig;
        map.put("projectProgress", finishCompleteRate);
        List<Map<String, Object>> deviceMap = this.baseMapper.equipmentOutboundTotal(serviceUnitId);
        map.put("equipmentOutboundTotal", !CollectionUtils.isEmpty(deviceMap) ? Integer.parseInt(deviceMap.get(0).get("outs").toString()) : 0);
        int totalDevices = !CollectionUtils.isEmpty(deviceMap) ? Integer.parseInt(deviceMap.get(0).get("deviceSum").toString()) : 0;
        BigDecimal totalDevicesBig = BigDecimal.valueOf(totalDevices),
                OutboundTotal = BigDecimal.valueOf(Integer.parseInt(map.get("equipmentOutboundTotal").toString())),
                deviceRate = totalDevices > 0 ? OutboundTotal.divide(totalDevicesBig, 2, BigDecimal.ROUND_HALF_UP).multiply(percent).setScale(2, BigDecimal.ROUND_HALF_UP) : OutboundTotal;
        map.put("equipmentOccupancyRate", deviceRate);
        List<Map<String, Object>> sopPassageMap = tbSopInfoService.findSopPassage(serviceUnitId);
        Integer distributed = 0;
        if (!CollectionUtils.isEmpty(sopPassageMap)) {
            for (Map m : sopPassageMap) {
                //需要去重
                String regionUserStr = Objects.nonNull(m) && Objects.nonNull(m.get("regionUserIds")) ? m.get("regionUserIds").toString() : null;
                if (Objects.nonNull(regionUserStr)) {
                    String[] strs = regionUserStr.split(",");
                    Set<String> set = new HashSet<>(Arrays.asList(strs));
                    distributed = distributed + set.size();
                }
                String engineerUserStr = Objects.nonNull(m) && Objects.nonNull(m.get("engineerUserIds")) ? m.get("engineerUserIds").toString() : null;
                if (Objects.nonNull(engineerUserStr)) {
                    String[] strs = engineerUserStr.split(",");
                    Set<String> set = new HashSet<>(Arrays.asList(strs));
                    distributed = distributed + set.size();
                }
                String assistantEngineerUserId = Objects.nonNull(m) && Objects.nonNull(m.get("assistantEngineerUserIds")) ? m.get("assistantEngineerUserIds").toString() : null;
                if (Objects.nonNull(assistantEngineerUserId)) {
                    String[] strs = assistantEngineerUserId.split(",");
                    Set<String> set = new HashSet<>(Arrays.asList(strs));
                    distributed = distributed + set.size();
                }
            }
        }
        map.put("servicePersonnelTotal", distributed);
        //认证有效的人员总数
        int totalUserArchives = tbUserArchivesSupplierService.countAuthentication();
        BigDecimal totalUserBig = BigDecimal.valueOf(totalUserArchives),
                userServicesTotal = BigDecimal.valueOf(distributed),
                userRate = totalUserArchives > 0 ? userServicesTotal.divide(totalUserBig, 2, BigDecimal.ROUND_HALF_UP).multiply(percent).setScale(2, BigDecimal.ROUND_HALF_UP) : userServicesTotal;
        map.put("sitePersonnelOccupancyRate", userRate);
        return map;
    }

    /**
     * 4.服务单元概览
     * ①项目执行进度=服务单元下已完成的派单（研究生派单下SOP已完结；教务处派单下已有完成的SOP，无在执行的SOP。可能存在教务处多个SOP之间空窗期导致数据的误差，这个指标允许少量误差存在）；
     * ②设备出库总量：当前服务单元下已出库且尚未入库的设备总量；
     * ③设备占用率：（设备出库总量÷正常设备总量）×100%；
     * ④在服务人员总数：在执行的SOP现场服务人员数量，包括助理工程师、实施工程师、区域协调人；
     * ⑤现场人员占用率：（在服务人员总数÷认证有效的在服务人员总数）×100%；
     */
    @Override
    public ServiceUnitOverview overview(Long serviceUnitId) {
        ServiceUnitOverview result = new ServiceUnitOverview();
        BigDecimal percent = new BigDecimal(100);
        // -- 项目执行进度 --
        List<TBCrm> tbCrmDatasource = tbCrmService.list(new QueryWrapper<TBCrm>().lambda()
                .eq(TBCrm::getServiceId, serviceUnitId)
                .eq(TBCrm::getEnable,true));
        int crmFinishCount = (int) tbCrmDatasource.stream().filter(e -> CrmStatusEnum.FINISH.equals(e.getStatus())).count();
        int crmTotalCount = tbCrmDatasource.size();
        BigDecimal projectProgress = new BigDecimal(0);
        if (crmTotalCount > 0){
            projectProgress = new BigDecimal(crmFinishCount).divide(new BigDecimal(crmTotalCount), 2, RoundingMode.HALF_UP).multiply(percent);
        }

        // -- 设备出库总量 -- (可以知道是哪个设备没入库)
        List<TBDeviceInOut> tbDeviceInOutDatasource = tbDeviceInOutService.list(new QueryWrapper<TBDeviceInOut>().lambda().eq(TBDeviceInOut::getServiceId, serviceUnitId));

        // 设备入库map -> （设备编号，入库次数）
        Map<String, Integer> deviceInMap = tbDeviceInOutDatasource
                .stream()
                .filter(e -> InOutTypeEnum.IN.equals(e.getType()))
                .collect(Collectors.toMap(TBDeviceInOut::getDeviceSerialNo, e -> 1, Integer::sum));

        // 设备出库map -> （设备编号，出库次数）
        int equipmentOutboundTotal = 0;
        Map<String, Integer> deviceOutMap = tbDeviceInOutDatasource
                .stream()
                .filter(e -> InOutTypeEnum.OUT.equals(e.getType()))
                .collect(Collectors.toMap(TBDeviceInOut::getDeviceSerialNo, e -> 1, Integer::sum));

        for (String deviceNo : deviceOutMap.keySet()) {
            // 出库次数
            Integer outCount = deviceOutMap.get(deviceNo);
            Integer inCount = 0;
            if (deviceInMap.containsKey(deviceNo)) {
                inCount = deviceInMap.get(deviceNo);
            }
            if (inCount > outCount) {
                throw ExceptionResultEnum.ERROR.exception(String.format("在服务单元下,设备[%s]入库次数比出库次数多异常", deviceNo));
            }
            outCount = outCount - inCount;
            if (outCount > 1) {
                throw ExceptionResultEnum.ERROR.exception(String.format("在服务单元下,设备[%s]出库次数异常", deviceNo));
            }
            equipmentOutboundTotal = equipmentOutboundTotal + outCount;
        }

        // -- 设备占用率 --
        int normalDeviceCount = sysDeviceService.count(new QueryWrapper<SysDevice>().lambda().eq(SysDevice::getStatus, DeviceStatusEnum.NORMAL));
        BigDecimal equipmentOccupancyRate = new BigDecimal(0);
        if (normalDeviceCount > 0){
            equipmentOccupancyRate = new BigDecimal(equipmentOutboundTotal).divide(new BigDecimal(normalDeviceCount), 2, RoundingMode.HALF_UP).multiply(percent);
        }

        // -- 在服务人员总数 --
        List<Map<String, Object>> sopPassageMap = tbSopInfoService.findSopPassage(serviceUnitId);
        Integer servicePersonnelTotal = 0;
        if (!CollectionUtils.isEmpty(sopPassageMap)) {
            for (Map m : sopPassageMap) {
                //需要去重
                String regionUserStr = Objects.nonNull(m) && Objects.nonNull(m.get("regionUserIds")) ? m.get("regionUserIds").toString() : null;
                if (Objects.nonNull(regionUserStr)) {
                    String[] strs = regionUserStr.split(",");
                    Set<String> set = new HashSet<>(Arrays.asList(strs));
                    servicePersonnelTotal = servicePersonnelTotal + set.size();
                }
                String engineerUserStr = Objects.nonNull(m) && Objects.nonNull(m.get("engineerUserIds")) ? m.get("engineerUserIds").toString() : null;
                if (Objects.nonNull(engineerUserStr)) {
                    String[] strs = engineerUserStr.split(",");
                    Set<String> set = new HashSet<>(Arrays.asList(strs));
                    servicePersonnelTotal = servicePersonnelTotal + set.size();
                }
                String assistantEngineerUserId = Objects.nonNull(m) && Objects.nonNull(m.get("assistantEngineerUserIds")) ? m.get("assistantEngineerUserIds").toString() : null;
                if (Objects.nonNull(assistantEngineerUserId)) {
                    String[] strs = assistantEngineerUserId.split(",");
                    Set<String> set = new HashSet<>(Arrays.asList(strs));
                    servicePersonnelTotal = servicePersonnelTotal + set.size();
                }
            }
        }


        //认证有效的人员总数
        int totalUserArchives = tbUserArchivesSupplierService.countAuthentication();
        BigDecimal sitePersonnelOccupancyRate = new BigDecimal(0);
        if (totalUserArchives > 0) {
            sitePersonnelOccupancyRate = new BigDecimal(servicePersonnelTotal).divide(new BigDecimal(totalUserArchives), 2, RoundingMode.HALF_UP).multiply(percent);
        }
        result.setProjectProgress(projectProgress);
        result.setEquipmentOutboundTotal(equipmentOutboundTotal);
        result.setEquipmentOccupancyRate(equipmentOccupancyRate);
        result.setServicePersonnelTotal(servicePersonnelTotal);
        result.setSitePersonnelOccupancyRate(sitePersonnelOccupancyRate);
        return result;
    }

    /**
     * ①当前服务单元已划分了区域协调人的项目总数、供应商分布占比；
     */
    @Override
    public Map<String, Object> supplier(Long serviceUnitId) {
        Map<String, Object> map = new HashMap<>();
        map.put("projectCount", this.baseMapper.projectCount(serviceUnitId));
        List<Map<String, Object>> maps = this.baseMapper.supplierDistribution(serviceUnitId);
//        maps = maps.stream().peek(m -> m.put("percent",(Integer) map.get("projectCount")==0?0: ((Long) m.get("count")) * 100 / ((Integer) map.get("projectCount")) + "%")).collect(Collectors.toList());
        map.put("supplierDistribution", maps);
        return map;
    }

    @Override
    public IPage<TBCrmResult> supplierDetail(Page<Object> iPage, Long serviceUnitId, Long supplierId) {
        return this.baseMapper.crmDetail(iPage, serviceUnitId, supplierId);
    }

    @Override
    public List<Map<String, Object>> supplierRole(Long serviceUnitId, Long supplierId) {
        return this.baseMapper.supplierRole(serviceUnitId, supplierId);
    }

    @Override
    public List<Map<String, Object>> supplierRegion(Long serviceUnitId, Long supplierId) {
        return this.baseMapper.supplierRegion(serviceUnitId, supplierId);
    }


    @Override
    public List<Map<String, Object>> device(Long serviceUnitId) {
        return this.baseMapper.device(serviceUnitId);
    }

    @Deprecated
    @Override
    public List<Map<String, Object>> personnel1(Long serviceUnitId) {
        return this.baseMapper.personnel(serviceUnitId);
    }

    @Override
    public ServiceUnitPersonnel personnel(Long serviceUnitId) {
        List<TBCrm> publishCrmList = tbCrmService.list(new QueryWrapper<TBCrm>()
                .lambda()
                .eq(TBCrm::getServiceId, serviceUnitId)
                .eq(TBCrm::getEnable, true));
//                .eq(TBCrm::getStatus, CrmStatusEnum.PUBLISH));

        Set<Long> regionIdSet = publishCrmList.stream()
                .filter(e -> e.getRegionId() != null && e.getRegionId() > 0)
                .map(TBCrm::getRegionId).collect(Collectors.toSet());

        List<RegionPersonnel> regionPersonnelList = new ArrayList<>();

        // 全量供应商id集合
        Set<Long> supplierIdSet = new HashSet<>();
        for (Long regionId : regionIdSet) {
            TBServiceRegion tbServiceRegion = tbServiceRegionService.getById(regionId);
            if (Objects.isNull(tbServiceRegion)) {
                throw ExceptionResultEnum.ERROR.exception("未找到大区信息");
            }
            String regionName = tbServiceRegion.getRegionName();

            List<TBCrm> crmList = publishCrmList.stream()
                    .filter(e -> regionId.equals(e.getRegionId()))
                    .collect(Collectors.toList());

            // 大区下供应商和人员关系Map
            Map<Long, Set<Long>> supplierMap = new HashMap<>();
            for (TBCrm tbCrm : crmList) {
                List<CrmArchivesAllocationResult> detail = tbUserArchivesAllocationService.findAllocationByCrmNo(tbCrm.getCrmNo());
                detail.forEach(e -> {
                    Long supplierId = e.getSupplierId();
                    Long userId = e.getUserId();
                    if (supplierMap.containsKey(supplierId)) {
                        supplierMap.get(supplierId).add(userId);
                    } else {
                        Set<Long> userIdSet = new HashSet<>();
                        userIdSet.add(userId);
                        supplierMap.put(supplierId, userIdSet);
                        supplierIdSet.add(supplierId);
                    }
                });
            }

            List<SupplierPersonnel> supplierPersonnelList = new ArrayList<>();
            int count = 0;
            for (Long supplierId : supplierMap.keySet()) {
                SysSupplier sysSupplier = sysSupplierService.getById(supplierId);
                String supplierName = sysSupplier.getName();

                SupplierPersonnel supplierPersonnel = new SupplierPersonnel();
                supplierPersonnel.setSupplierId(supplierId);
                supplierPersonnel.setSupplierName(supplierName);
                int cellCount = supplierMap.get(supplierId).size();
                supplierPersonnel.setCount(cellCount);
                supplierPersonnelList.add(supplierPersonnel);
                count = count + cellCount;
            }
            RegionPersonnel regionPersonnel = new RegionPersonnel();

            regionPersonnel.setRegionId(regionId);
            regionPersonnel.setRegionName(regionName);
            regionPersonnel.setSupplierPersonnelInfo(supplierPersonnelList);
            regionPersonnel.setCount(count);
            regionPersonnelList.add(regionPersonnel);
        }
        ServiceUnitPersonnel serviceUnitPersonnel = new ServiceUnitPersonnel();
        serviceUnitPersonnel.setSupplierIdSet(supplierIdSet);
        serviceUnitPersonnel.setRegionPersonnelInfo(regionPersonnelList);
        return serviceUnitPersonnel;
    }


    @Override
    public UserArchivesAllocationSubTotalResult findCrmAllocationSubTotal(Long serviceUnitId, Long regionId, Long supplierId) {

//        List<UserArchivesAllocationResult> list = this.baseMapper.findCrmAllocation(serviceUnitId, regionId, supplierId);
        IPage<UserArchivesAllocationResult> userArchivesAllocationResultIPage = tbUserArchivesAllocationService.findCrmAllocationPage(serviceUnitId, null, null, null, null, null, regionId, SystemConstant.PAGE_NUMBER_MIN, SystemConstant.PAGE_SIZE_MAX_SELECT);
        Integer publishedCrmCount = 0, totalCrmCount = 0, coordinatorQuota = 0, coordinatorDistributed = 0, effectQuota = 0,
                effectDistributed = 0, assistantQuota = 0, assistantDistributed = 0;
        if (Objects.nonNull(userArchivesAllocationResultIPage)) {
            List<UserArchivesAllocationResult> list = userArchivesAllocationResultIPage.getRecords();
            publishedCrmCount = Math.toIntExact(list.stream().filter(e -> CrmStatusEnum.PUBLISH.equals(e.getCrmStatus())).count());
            totalCrmCount = list.size();
            coordinatorQuota = list.stream().mapToInt(UserArchivesAllocationResult::getCoordinatorQuota).sum();
            coordinatorDistributed = list.stream().mapToInt(UserArchivesAllocationResult::getCoordinatorDistributed).sum();
            effectQuota = list.stream().mapToInt(UserArchivesAllocationResult::getEffectQuota).sum();
            effectDistributed = list.stream().mapToInt(UserArchivesAllocationResult::getEffectDistributed).sum();
            assistantQuota = list.stream().mapToInt(UserArchivesAllocationResult::getAssistantQuota).sum();
            assistantDistributed = list.stream().mapToInt(UserArchivesAllocationResult::getAssistantDistributed).sum();
        }
        UserArchivesAllocationSubTotalResult result = new UserArchivesAllocationSubTotalResult();
        result.setPublishedCrmCount(publishedCrmCount);
        result.setTotalCrmCount(totalCrmCount);
        result.setCoordinatorDistributed(coordinatorDistributed);
        result.setCoordinatorQuota(coordinatorQuota);
        result.setEffectDistributed(effectDistributed);
        result.setEffectQuota(effectQuota);
        result.setAssistantDistributed(assistantDistributed);
        result.setAssistantQuota(assistantQuota);
        return result;
    }

    @Override
    public List<UserArchivesAllocationResult> findCrmAllocation(Long serviceUnitId, Long regionId, Long supplierId) {
        return this.baseMapper.findCrmAllocation(serviceUnitId, regionId, supplierId);
    }

    /**
     * 查找当前大区待划定派单数量
     *
     * @param regionId
     * @return
     */
    @Override
    public int findWaitZone(Long regionId) {
        return this.baseMapper.findWaitZone(regionId);
    }
}
