package org.dromara.elevator.service.impl;

import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.service.DictService;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StreamUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.helper.DataBaseHelper;
import org.dromara.elevator.constant.WorkStateEnum;
import org.dromara.elevator.constant.WorkType;
import org.dromara.elevator.domain.*;
import org.dromara.elevator.domain.bo.*;
import org.dromara.elevator.domain.vo.EMaintenanceInfoVo;
import org.dromara.elevator.domain.vo.EMaintenanceStatisticsVo;
import org.dromara.elevator.mapper.EMaintenanceInfoMapper;
import org.dromara.elevator.mapper.EWorkDataMapper;
import org.dromara.elevator.service.IEBehaviorStatisticsService;
import org.dromara.elevator.service.IEFaultStatisticsService;
import org.dromara.elevator.service.IEMaintenanceInfoService;
import org.dromara.elevator.service.IEWorkOperationRecordService;
import org.dromara.system.domain.SysDept;
import org.dromara.system.mapper.SysDeptMapper;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;

import static org.dromara.elevator.constant.ElevatorConstant.FAULT_EXCEPTION_TYPE_DICT_KEY;
import static org.dromara.elevator.constant.ElevatorConstant.FAULT_TYPE_DICT_KEY;

/**
 * 维保单位基础信息;Service业务层处理
 *
 * @author zy
 * @date 2024-07-26
 */
@RequiredArgsConstructor
@Service
public class EMaintenanceInfoServiceImpl implements IEMaintenanceInfoService {

    private final EMaintenanceInfoMapper baseMapper;
    private final IEWorkOperationRecordService workOperationRecordService;
    private final IEFaultStatisticsService faultStatisticsService;
    private final IEBehaviorStatisticsService behaviorStatisticsService;
    private final DictService dictService;
    private final EWorkDataMapper workDataMapper;
    private final SysDeptMapper deptMapper;

    /**
     * 查询维保单位基础信息;
     *
     * @param id 主键
     * @return 维保单位基础信息;
     */
    @Override
    public EMaintenanceInfoVo queryById(Long id) {
        return baseMapper.selectVoById(id);
    }

    /**
     * 分页查询维保单位基础信息;列表
     *
     * @param bo        查询条件
     * @param pageQuery 分页参数
     * @return 维保单位基础信息;分页列表
     */
    @Override
    public TableDataInfo<EMaintenanceInfoVo> queryPageList(EMaintenanceInfoBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<EMaintenanceInfo> lqw = buildQueryWrapper(bo);
        Page<EMaintenanceInfoVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
    }

    /**
     * 查询符合条件的维保单位基础信息;列表
     *
     * @param bo 查询条件
     * @return 维保单位基础信息;列表
     */
    @Override
    public List<EMaintenanceInfoVo> queryList(EMaintenanceInfoBo bo) {
        LambdaQueryWrapper<EMaintenanceInfo> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }

    private LambdaQueryWrapper<EMaintenanceInfo> buildQueryWrapper(EMaintenanceInfoBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<EMaintenanceInfo> lqw = Wrappers.lambdaQuery();
        lqw.eq(StringUtils.isNotBlank(bo.getOrganizationId()), EMaintenanceInfo::getOrganizationId, bo.getOrganizationId());
        lqw.eq(StringUtils.isNotBlank(bo.getCode()), EMaintenanceInfo::getCode, bo.getCode());
        lqw.like(StringUtils.isNotBlank(bo.getName()), EMaintenanceInfo::getName, bo.getName());
        lqw.like(StringUtils.isNotBlank(bo.getLicenseName()), EMaintenanceInfo::getLicenseName, bo.getLicenseName());
        lqw.eq(StringUtils.isNotBlank(bo.getAddress()), EMaintenanceInfo::getAddress, bo.getAddress());
        lqw.eq(StringUtils.isNotBlank(bo.getAreaCode()), EMaintenanceInfo::getAreaCode, bo.getAreaCode());
        lqw.eq(bo.getLng() != null, EMaintenanceInfo::getLng, bo.getLng());
        lqw.eq(bo.getLat() != null, EMaintenanceInfo::getLat, bo.getLat());
        lqw.eq(StringUtils.isNotBlank(bo.getPrincipal()), EMaintenanceInfo::getPrincipal, bo.getPrincipal());
        lqw.eq(StringUtils.isNotBlank(bo.getPrincipalPhone()), EMaintenanceInfo::getPrincipalPhone, bo.getPrincipalPhone());
        lqw.eq(bo.getValidDate() != null, EMaintenanceInfo::getValidDate, bo.getValidDate());
        lqw.eq(StringUtils.isNotBlank(bo.getCertificateNumber()), EMaintenanceInfo::getCertificateNumber, bo.getCertificateNumber());
        lqw.eq(StringUtils.isNotBlank(bo.getLevel()), EMaintenanceInfo::getLevel, bo.getLevel());
        lqw.eq(StringUtils.isNotBlank(bo.getInstitutionCode()), EMaintenanceInfo::getInstitutionCode, bo.getInstitutionCode());
        lqw.eq(StringUtils.isNotBlank(bo.getLicence()), EMaintenanceInfo::getLicence, bo.getLicence());
        lqw.eq(StringUtils.isNotBlank(bo.getTelephone()), EMaintenanceInfo::getTelephone, bo.getTelephone());
        lqw.eq(StringUtils.isNotBlank(bo.getLegal()), EMaintenanceInfo::getLegal, bo.getLegal());
        lqw.eq(StringUtils.isNotBlank(bo.getLegalPhone()), EMaintenanceInfo::getLegalPhone, bo.getLegalPhone());
        lqw.eq(StringUtils.isNotBlank(bo.getUrgency()), EMaintenanceInfo::getUrgency, bo.getUrgency());
        lqw.eq(StringUtils.isNotBlank(bo.getUrgencyPhone()), EMaintenanceInfo::getUrgencyPhone, bo.getUrgencyPhone());

        lqw.and(ObjectUtil.isNotNull(bo.getDeptId()), w -> {
            List<SysDept> deptList = deptMapper.selectList(new LambdaQueryWrapper<SysDept>()
                .select(SysDept::getDeptId)
                .apply(DataBaseHelper.findInSet(bo.getDeptId(), "ancestors")));
            List<Long> ids = StreamUtils.toList(deptList, SysDept::getDeptId);
            ids.add(bo.getDeptId());
            w.in(EMaintenanceInfo::getDeptId, ids);
        });

        lqw.orderByDesc(EMaintenanceInfo::getCreateTime);
        return lqw;
    }

    /**
     * 新增维保单位基础信息;
     *
     * @param bo 维保单位基础信息;
     * @return 是否新增成功
     */
    @Override
    public Boolean insertByBo(EMaintenanceInfoBo bo) {
        EMaintenanceInfo add = MapstructUtils.convert(bo, EMaintenanceInfo.class);
        validEntityBeforeSave(add);
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setId(add.getId());
        }
        return flag;
    }

    /**
     * 修改维保单位基础信息;
     *
     * @param bo 维保单位基础信息;
     * @return 是否修改成功
     */
    @Override
    public Boolean updateByBo(EMaintenanceInfoBo bo) {
        EMaintenanceInfo update = MapstructUtils.convert(bo, EMaintenanceInfo.class);
        validEntityBeforeUpdate(update);
        return baseMapper.updateById(update) > 0;
    }

    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(EMaintenanceInfo entity) {
        //TODO 做一些数据校验,如唯一约束
        LambdaQueryWrapper<EMaintenanceInfo> num = Wrappers.lambdaQuery();
        num.eq(StringUtils.isNotBlank(entity.getCode()), EMaintenanceInfo::getCode, entity.getCode());
        List<EMaintenanceInfoVo> infoVoList = baseMapper.selectVoList(num);
        if (!infoVoList.isEmpty()) {
            throw new ServiceException("该维保单位编号已存在!");
        }
    }

    /**
     * 更新前的数据校验
     */
    private void validEntityBeforeUpdate(EMaintenanceInfo entity) {
        //TODO 做一些数据校验,如唯一约束
    }


    /**
     * 校验并批量删除维保单位基础信息;信息
     *
     * @param ids     待删除的主键集合
     * @param isValid 是否进行有效性校验
     * @return 是否删除成功
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if (isValid) {
            //TODO 做一些业务上的校验,判断是否需要校验
        }
        return baseMapper.deleteBatchIds(ids) > 0;
    }

    @Override
    public EMaintenanceInfoVo selectByCode(String code) {
        return baseMapper.selectVoOne(new LambdaQueryWrapper<EMaintenanceInfo>().eq(EMaintenanceInfo::getCode, code));
    }

    @Override
    public TableDataInfo<EMaintenanceStatisticsVo> queryPageStatisticsList(EMaintenanceStatisticsBo bo, PageQuery pageQuery) {
        EMaintenanceInfoBo maintenanceInfoBo = new EMaintenanceInfoBo();
        maintenanceInfoBo.setName(bo.getMaintenanceName());
        maintenanceInfoBo.setDeptId(bo.getDeptId());
        TableDataInfo<EMaintenanceInfoVo> dataInfo = this.queryPageList(maintenanceInfoBo, pageQuery);
        TableDataInfo<EMaintenanceStatisticsVo> result = new TableDataInfo<>();
        List<Long> maintenanceIds = new ArrayList<>();
        Set<String> maintenanceNameList = new HashSet<>();
        List<EMaintenanceStatisticsVo> statisticsList = dataInfo.getRows().stream()
            .peek(item -> {
                maintenanceIds.add(item.getId());
                maintenanceNameList.add(item.getName());
            })
            .map(this::buildMaintenanceStatistics).collect(Collectors.toList());
        result.setRows(statisticsList);
        result.setMsg(dataInfo.getMsg());
        result.setTotal(dataInfo.getTotal());
        result.setCode(dataInfo.getCode());
        if (CollectionUtils.isEmpty(result.getRows())) {
            return result;
        }
        // 工作统计
        List<EWorkData> workDataList = workDataMapper.selectList(Wrappers.<EWorkData>lambdaQuery()
            .in(EWorkData::getState, WorkStateEnum.complete())
            .in(EWorkData::getMaintenance, maintenanceNameList)
            .between(EWorkData::getFinishTime, bo.getStartTime(), bo.getEndTime()));
        Map<String, List<EWorkData>> maintenanceWorkMap = StreamUtils.groupByKey(workDataList, EWorkData::getMaintenance);

//        EWorkOperationRecordBo operationRecordBo = new EWorkOperationRecordBo();
//        operationRecordBo.setMaintenanceIds(maintenanceIds);
//        operationRecordBo.setStartTime(bo.getStartTime());
//        operationRecordBo.setEndTime(bo.getEndTime());
//        operationRecordBo.setWorkTypes(WorkType.getWorkTypes());
//        operationRecordBo.setOperationState(WorkStateEnum.COMPLETE.getState());
//        List<EWorkOperationRecord> operationRecords = workOperationRecordService.queryOperationRecords(operationRecordBo);
//        Map<Long, List<EWorkOperationRecord>> maintenanceRecordMap = operationRecords.stream()
//            .collect(Collectors.groupingBy(EWorkOperationRecord::getMaintenanceId));
        // 故障统计
        EFaultStatisticsBo faultStatisticsBo = new EFaultStatisticsBo();
        faultStatisticsBo.setMaintenanceIds(maintenanceIds);
        faultStatisticsBo.setStartTime(bo.getStartTime());
        faultStatisticsBo.setEndTime(bo.getEndTime());
        List<EFaultStatistics> faultStatisticsList = faultStatisticsService.queryFaultStatisticsList(faultStatisticsBo);
        Map<Long, List<EFaultStatistics>> maintenanceFaultMap = faultStatisticsList.stream()
            .collect(Collectors.groupingBy(EFaultStatistics::getMaintenanceId));
        Set<String> faultTypes = dictService.getAllDictByDictType(FAULT_TYPE_DICT_KEY).keySet();
        Set<String> faultExceptionTypes = dictService.getAllDictByDictType(FAULT_EXCEPTION_TYPE_DICT_KEY).keySet();

        // 运行次数
        EBehaviorStatisticsBo behaviorStatisticsBo = new EBehaviorStatisticsBo();
        behaviorStatisticsBo.setMaintenanceIds(maintenanceIds);
        behaviorStatisticsBo.setStartTime(bo.getStartTime());
        behaviorStatisticsBo.setEndTime(bo.getEndTime());
        List<EBehaviorStatistics> behaviorStatisticsList = behaviorStatisticsService.queryBehaviorStatisticsList(behaviorStatisticsBo);
        Map<Long, List<EBehaviorStatistics>> maintenanceBehaviorMap = behaviorStatisticsList.stream()
            .collect(Collectors.groupingBy(EBehaviorStatistics::getMaintenanceId));
        for (EMaintenanceStatisticsVo statistics : statisticsList) {
            // 工作统计
            workStatisticsHandler(maintenanceWorkMap, statistics);
            // 故障
            faultStatisticsHandler(maintenanceFaultMap, statistics, faultTypes, faultExceptionTypes);
            // 运行
            behaviorStatisticsHandler(maintenanceBehaviorMap, statistics);
            // 故障率 故障次数 / 运行次数 0.000000
            if (statistics.getBehaviorTotal() > 0) {
                BigDecimal faultRatio = NumberUtil.div(statistics.getFaultTotal(), statistics.getBehaviorTotal(), 6)
                    .multiply(new BigDecimal("10000")).setScale(2, RoundingMode.HALF_UP);
                statistics.setFaultRatio(faultRatio);
            }
            // 救援率 救援次数 /(救援次数 + 维修次数 + 维保次数)
            Integer workTotal = statistics.getRescueTotal() + statistics.getRepairTotal() + statistics.getMaintenanceTotal();
            if (workTotal > 0) {
                BigDecimal rescueRatio = NumberUtil.div(statistics.getRescueTotal(), workTotal, 4)
                    .multiply(new BigDecimal("100")).setScale(2, RoundingMode.HALF_UP);
                statistics.setRescueRatio(rescueRatio);
            }
        }
        return result;
    }

    /**
     * 行为处理
     *
     * @param maintenanceBehaviorMap 行为信息
     * @param statistics             统计信息
     */
    private void behaviorStatisticsHandler(Map<Long, List<EBehaviorStatistics>> maintenanceBehaviorMap,
                                           EMaintenanceStatisticsVo statistics) {
        if (maintenanceBehaviorMap.containsKey(statistics.getMaintenanceId())) {
            Integer total = maintenanceBehaviorMap.get(statistics.getMaintenanceId()).stream()
                .mapToInt(item -> item.getUpNum() + item.getDownNum()).sum();
            statistics.setBehaviorTotal(statistics.getBehaviorTotal() + total);
        }
    }

    /**
     * 故障信息处理
     *
     * @param maintenanceFaultMap 故障map
     * @param statistics          统计星系
     * @param faultTypes          故障类型
     * @param faultExceptionTypes 异常故障类型
     */
    private void faultStatisticsHandler(Map<Long, List<EFaultStatistics>> maintenanceFaultMap,
                                        EMaintenanceStatisticsVo statistics, Set<String> faultTypes,
                                        Set<String> faultExceptionTypes) {
        if (maintenanceFaultMap.containsKey(statistics.getMaintenanceId())) {
            for (EFaultStatistics faultStatistics : maintenanceFaultMap.get(statistics.getMaintenanceId())) {
                if (faultTypes.contains(faultStatistics.getFaultCode())) {
                    // 故障
                    statistics.setFaultTotal(statistics.getFaultTotal() + 1);
                }
                if (faultExceptionTypes.contains(faultStatistics.getFaultCode())) {
                    // 异常故障
                    statistics.setExceptionFaultTotal(statistics.getExceptionFaultTotal() + 1);
                }
            }
        }
    }

    /**
     * 工作统计处理
     *
     * @param maintenanceWorkMap   工作记录
     * @param statistics           统计信息
     */
    private void workStatisticsHandler(Map<String, List<EWorkData>> maintenanceWorkMap,
                                       EMaintenanceStatisticsVo statistics) {
        if (maintenanceWorkMap.containsKey(statistics.getMaintenanceName())) {
            for (EWorkData record : maintenanceWorkMap.get(statistics.getMaintenanceName())) {
                //  救援次数
                if (WorkType.RESCUE.getType().equals(record.getWorkType())) {
                    statistics.setRescueTotal(statistics.getRescueTotal() + 1);
                }
                // 维修次数
                if (WorkType.REPAIR.getType().equals(record.getWorkType())) {
                    statistics.setRepairTotal(statistics.getRepairTotal() + 1);
                }
                //  维保次数
                if (WorkType.MAINTENANCE.getType().equals(record.getWorkType())) {
                    statistics.setMaintenanceTotal(statistics.getMaintenanceTotal() + 1);
                }
            }
        }
    }

    /**
     * 构建维保统计
     *
     * @param item 维保信息
     * @return维保统计
     */
    private EMaintenanceStatisticsVo buildMaintenanceStatistics(EMaintenanceInfoVo item) {
        EMaintenanceStatisticsVo statistics = new EMaintenanceStatisticsVo();
        statistics.setMaintenanceId(item.getId());
        statistics.setMaintenanceName(item.getName());
        return statistics;
    }
}
