package org.dromara.elevator.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.HttpUtil;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
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.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import org.dromara.common.mybatis.helper.DataBaseHelper;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.elevator.constant.AreaClientEnum;
import org.dromara.elevator.constant.StateConstant;
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.message.AuthBody;
import org.dromara.elevator.domain.message.AuthToken;
import org.dromara.elevator.domain.vo.*;
import org.dromara.elevator.mapper.*;
import org.dromara.elevator.service.*;
import org.dromara.elevator.service.message.AuthTokenService;
import org.dromara.system.domain.SysDept;
import org.dromara.system.domain.vo.SysUserVo;
import org.dromara.system.mapper.SysDeptMapper;
import org.dromara.system.mapper.SysUserMapper;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import static org.dromara.elevator.constant.ElevatorConstant.*;

/**
 * 电梯基础信息;Service业务层处理
 *
 * @author zy
 * @date 2024-07-26
 */
@RequiredArgsConstructor
@Service
public class EElevatorInfoServiceImpl implements IEElevatorInfoService {

    private final EElevatorInfoMapper baseMapper;
    private final SysDeptMapper deptMapper;
    private final EAnnualPlanMapper annualPlanMapper;
    private final EElevatorUserMapper elevatorUserMapper;
    private final DictService dictService;
    private final EMaintenanceInfoMapper maintenanceInfoMapper;
    private final ETenementInfoMapper tenementInfoMapper;
    private final EElevatorRelevancyDeviceMapper elevatorRelevancyDeviceMapper;
    private final IEWorkOperationRecordService workOperationRecordService;
    private final IEFaultStatisticsService faultStatisticsService;
    private final IEPassengerFeedbackService passengerFeedbackService;
    private final IEMaintenancePlanService maintenancePlanService;
    private final IEBehaviorMessageService behaviorMessageService;
    private final IEBlackBoxInfoService blackBoxInfoService;
    private final SysUserMapper sysUserMapper;
    private final AuthTokenService authTokenService;


    /**
     * 查询电梯基础信息;
     *
     * @param id 主键
     * @return 电梯基础信息;
     */
    @Override
    public EElevatorInfoVo queryById(Long id){
        EElevatorInfoVo elevatorInfo = baseMapper.selectVoById(id);
        if (Objects.nonNull(elevatorInfo)) {
            if (StringUtils.isNotBlank(elevatorInfo.getMaintenanceId())) {
                elevatorInfo.setMaintenanceInfo(maintenanceInfoMapper.selectVoById(Long.valueOf(elevatorInfo.getMaintenanceId())));
            }
            if (StringUtils.isNotBlank(elevatorInfo.getTenementId())) {
                elevatorInfo.setTenementInfo(tenementInfoMapper.selectVoById(Long.valueOf(elevatorInfo.getTenementId())));
            }
        }
        return elevatorInfo;
    }

    /**
     * 分页查询电梯基础信息;列表
     *
     * @param bo        查询条件
     * @param pageQuery 分页参数
     * @return 电梯基础信息;分页列表
     */
    @Override
    public TableDataInfo<EElevatorInfoVo> queryPageList(EElevatorInfoBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<EElevatorInfo> lqw = buildQueryWrapper(bo);
        Page<EElevatorInfoVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
    }

    /**
     * 查询符合条件的电梯基础信息;列表
     *
     * @param bo 查询条件
     * @return 电梯基础信息;列表
     */
    @Override
    public List<EElevatorInfoVo> queryList(EElevatorInfoBo bo) {
        LambdaQueryWrapper<EElevatorInfo> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }

    private LambdaQueryWrapper<EElevatorInfo> buildQueryWrapper(EElevatorInfoBo bo) {
        LambdaQueryWrapper<EElevatorInfo> lqw = buildBaseQueryWrapper(bo);
        lqw.orderByDesc(EElevatorInfo::getCreateTime);
        return lqw;
    }

    @NotNull
    private LambdaQueryWrapper<EElevatorInfo> buildBaseQueryWrapper(EElevatorInfoBo bo) {
        LambdaQueryWrapper<EElevatorInfo> lqw = Wrappers.lambdaQuery();
        lqw.eq(StringUtils.isNotBlank(bo.getProjectId()), EElevatorInfo::getProjectId, bo.getProjectId());
        lqw.eq(StringUtils.isNotBlank(bo.getElevatorNum()), EElevatorInfo::getElevatorNum, bo.getElevatorNum());
        lqw.like(StringUtils.isNotBlank(bo.getElevatorName()), EElevatorInfo::getElevatorName, bo.getElevatorName());
        lqw.eq(StringUtils.isNotBlank(bo.getElevatorAddress()), EElevatorInfo::getElevatorAddress, bo.getElevatorAddress());
        lqw.eq(StringUtils.isNotBlank(bo.getAreaCode()), EElevatorInfo::getAreaCode, bo.getAreaCode());
        lqw.eq(StringUtils.isNotBlank(bo.getBuildingId()), EElevatorInfo::getBuildingId, bo.getBuildingId());
        lqw.like(StringUtils.isNotBlank(bo.getBuildingName()), EElevatorInfo::getBuildingName, bo.getBuildingName());
        lqw.like(StringUtils.isNotBlank(bo.getHouseName()), EElevatorInfo::getHouseName, bo.getHouseName());
        lqw.like(StringUtils.isNotBlank(bo.getEmployName()), EElevatorInfo::getEmployName, bo.getEmployName());
        lqw.like(StringUtils.isNotBlank(bo.getTenementName()), EElevatorInfo::getTenementName, bo.getTenementName());
        lqw.like(StringUtils.isNotBlank(bo.getMaintenanceName()), EElevatorInfo::getMaintenanceName, bo.getMaintenanceName());
        lqw.eq(StringUtils.isNotBlank(bo.getHouseId()), EElevatorInfo::getHouseId, bo.getHouseId());
        lqw.eq(bo.getUseScenario() != null, EElevatorInfo::getUseScenario, bo.getUseScenario());
        lqw.eq(StringUtils.isNotBlank(bo.getEmployId()), EElevatorInfo::getEmployId, bo.getEmployId());
        lqw.eq(StringUtils.isNotBlank(bo.getTenementId()), EElevatorInfo::getTenementId, bo.getTenementId());
        lqw.eq(StringUtils.isNotBlank(bo.getMaintenanceId()), EElevatorInfo::getMaintenanceId, bo.getMaintenanceId());
        lqw.eq(StringUtils.isNotBlank(bo.getBrand()), EElevatorInfo::getBrand, bo.getBrand());
        lqw.eq(StringUtils.isNotBlank(bo.getModel()), EElevatorInfo::getModel, bo.getModel());
        lqw.eq(bo.getLoad() != null, EElevatorInfo::getLoad, bo.getLoad());
        lqw.eq(bo.getSpeed() != null, EElevatorInfo::getSpeed, bo.getSpeed());
        lqw.eq(bo.getStandNum() != null, EElevatorInfo::getStandNum, bo.getStandNum());
        lqw.eq(bo.getControlMode() != null, EElevatorInfo::getControlMode, bo.getControlMode());
        lqw.eq(bo.getInstallDate() != null, EElevatorInfo::getInstallDate, bo.getInstallDate());
        lqw.eq(StringUtils.isNotBlank(bo.getRegisterNumber()), EElevatorInfo::getRegisterNumber, bo.getRegisterNumber());
        lqw.eq(bo.getLng() != null, EElevatorInfo::getLng, bo.getLng());
        lqw.eq(bo.getLat() != null, EElevatorInfo::getLat, bo.getLat());
        lqw.eq(StringUtils.isNotBlank(bo.getQuality()), EElevatorInfo::getQuality, bo.getQuality());
        lqw.eq(bo.getAnnualDate() != null, EElevatorInfo::getAnnualDate, bo.getAnnualDate());
        lqw.eq(bo.getNextAnnual() != null, EElevatorInfo::getNextAnnual, bo.getNextAnnual());
        lqw.eq(StringUtils.isNotBlank(bo.getType()), EElevatorInfo::getType, bo.getType());
        lqw.eq(bo.getUseState() != null, EElevatorInfo::getUseState, bo.getUseState());
        lqw.eq(StringUtils.isNotBlank(bo.getRegisterCode()), EElevatorInfo::getRegisterCode, bo.getRegisterCode());
        lqw.eq(StringUtils.isNotBlank(bo.getRescue()), EElevatorInfo::getRescue, bo.getRescue());
        lqw.eq(StringUtils.isNotBlank(bo.getInnerCode()), EElevatorInfo::getInnerCode, bo.getInnerCode());
        lqw.eq(StringUtils.isNotBlank(bo.getPropertyUnit()), EElevatorInfo::getPropertyUnit, bo.getPropertyUnit());
        lqw.eq(StringUtils.isNotBlank(bo.getManufactureUnit()), EElevatorInfo::getManufactureUnit, bo.getManufactureUnit());
        lqw.eq(StringUtils.isNotBlank(bo.getSocialCreditCode()), EElevatorInfo::getSocialCreditCode, bo.getSocialCreditCode());
        lqw.eq(StringUtils.isNotBlank(bo.getFactoryNumber()), EElevatorInfo::getFactoryNumber, bo.getFactoryNumber());
        lqw.eq(StringUtils.isNotBlank(bo.getInstallUnit()), EElevatorInfo::getInstallUnit, bo.getInstallUnit());
        lqw.eq(StringUtils.isNotBlank(bo.getInspectionBody()), EElevatorInfo::getInspectionBody, bo.getInspectionBody());
        lqw.eq(StringUtils.isNotBlank(bo.getSpecialCode()), EElevatorInfo::getSpecialCode, bo.getSpecialCode());
        lqw.eq(StringUtils.isNotBlank(bo.getProductionBatch()), EElevatorInfo::getProductionBatch, bo.getProductionBatch());
        lqw.eq(bo.getLeaveFactoryDate() != null, EElevatorInfo::getLeaveFactoryDate, bo.getLeaveFactoryDate());
        lqw.eq(StringUtils.isNotBlank(bo.getManufacturerId()), EElevatorInfo::getManufacturerId, bo.getManufacturerId());
        lqw.eq(bo.getEnergyType() != null, EElevatorInfo::getEnergyType, bo.getEnergyType());
        lqw.in(CollectionUtils.isNotEmpty(bo.getElevatorNumList()), EElevatorInfo::getElevatorNum, bo.getElevatorNumList());

        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(EElevatorInfo::getDeptId, ids);
        });
        return lqw;
    }

    /**
     * 新增电梯基础信息;
     *
     * @param bo 电梯基础信息;
     * @return 是否新增成功
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean insertByBo(EElevatorInfoBo bo) {
        EElevatorInfo add = MapstructUtils.convert(bo, EElevatorInfo.class);
        validEntityBeforeSave(add);
        boolean flag = baseMapper.insert(add) > 0;

        // 插入年检计划数据
        EAnnualPlan annualPlan = new EAnnualPlan();
        annualPlan.setElevatorNum(bo.getElevatorNum());
        annualPlan.setAnnualDate(bo.getAnnualDate());
        annualPlan.setNextAnnual(bo.getNextAnnual());
        annualPlan.setState(StateConstant.NOT_REMIND);
        annualPlanMapper.insert(annualPlan);
        if (flag) {
            bo.setId(add.getId());
        }
        // 插入电梯和工作人员关系数据
        List<SysUserVo> userList = sysUserMapper.selectVoList();
        if (CollUtil.isNotEmpty(userList)) {
            List<EElevatorUser> elevatorUserList = new ArrayList<>();
            for (SysUserVo user : userList) {
                EElevatorUser elevatorUser = new EElevatorUser();
                elevatorUser.setIsConcern(0);
                elevatorUser.setUserId(String.valueOf(user.getUserId()));
                elevatorUser.setElevatorNum(bo.getElevatorNum());
                elevatorUser.setElevatorId(String.valueOf(bo.getId()));
                elevatorUser.setElevatorName(bo.getElevatorName());
                elevatorUser.setElevatorAddress(bo.getElevatorAddress());
                elevatorUser.setTenementName(bo.getTenementName());
                elevatorUserList.add(elevatorUser);
            }
            elevatorUserMapper.insertBatch(elevatorUserList);
        }

        // 插入电梯设备管理数据
        EElevatorRelevancyDevice device = new EElevatorRelevancyDevice();
        device.setElevatorNum(add.getElevatorNum());
        device.setElevatorName(add.getElevatorName());
        device.setRegisterCode(add.getRegisterCode());
        device.setTenementName(add.getTenementName());
        device.setMaintenanceName(add.getMaintenanceName());
        device.setDeptId(add.getDeptId());
        device.setIsScreen(0);
        device.setIsBlackBox(0);
        device.setIsQrcode(0);
        device.setIsCamera(0);
        elevatorRelevancyDeviceMapper.insert(device);

        return flag;
    }

    /**
     * 修改电梯基础信息;
     *
     * @param bo 电梯基础信息;
     * @return 是否修改成功
     */
    @Override
    public Boolean updateByBo(EElevatorInfoBo bo) {
        EElevatorInfo update = MapstructUtils.convert(bo, EElevatorInfo.class);
        validEntityBeforeUpdate(update);
        return baseMapper.updateById(update) > 0;
    }

    /**
     * 新增保存前的数据校验
     */
    private void validEntityBeforeSave(EElevatorInfo entity){
        //TODO 做一些数据校验,如唯一约束
        LambdaQueryWrapper<EElevatorInfo> num = Wrappers.lambdaQuery();
        num.eq(StringUtils.isNotBlank(entity.getElevatorNum()), EElevatorInfo::getElevatorNum, entity.getElevatorNum());
        List<EElevatorInfoVo> infoVoList = baseMapper.selectVoList(num);
        if (!infoVoList.isEmpty()) {
            throw new ServiceException("电梯编号已存在!");
        }
        // 获取物业小区等名称

    }

    /**
     * 修改保存前的数据校验
     */
    private void validEntityBeforeUpdate(EElevatorInfo 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 EElevatorInfoVo selectByElevatorNum(String code) {
        return baseMapper.selectVoOne(new LambdaQueryWrapper<EElevatorInfo>().eq(EElevatorInfo::getElevatorNum, code));
    }

    @Override
    public EElevatorInfoVo selectByRegisterCode(String registerCode) {
        return baseMapper.selectVoOne(new LambdaQueryWrapper<EElevatorInfo>().eq(EElevatorInfo::getRegisterCode, registerCode));
    }

    @Override
    public List<EElevatorInfoVo> selectByElevatorNumList(Collection<String> eleNumList) {
        return baseMapper.selectVoList(Wrappers.<EElevatorInfo>lambdaQuery()
            .in(CollectionUtils.isNotEmpty(eleNumList),EElevatorInfo::getElevatorNum, eleNumList));
    }

    @Override
    public List<EElevatorBrandStatisticsVo> brandStatisticsChartList(EElevatorInfoBo bo) {
        LambdaQueryWrapper<EElevatorInfo> lqw = buildQueryWrapper(bo);
        List<EElevatorInfoVo> elevatorList = baseMapper.selectVoList(lqw);
        List<EElevatorBrandStatisticsVo> res = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(elevatorList)) {
            Map<String, String> brandMap = dictService.getAllDictByDictType(BRAND_DICT_KEY);
            Map<String, List<EElevatorInfoVo>> brandEleMap = elevatorList.stream()
                .collect(Collectors.groupingBy(EElevatorInfoVo::getBrand));
            BigDecimal totalRatio = BigDecimal.ZERO;
            BigDecimal hundredPercent = new BigDecimal("100");
            Integer eleTotal = elevatorList.size();
            for (Map.Entry<String, List<EElevatorInfoVo>> entry : brandEleMap.entrySet()) {
                List<EElevatorInfoVo> statisticsList = entry.getValue();
                EElevatorBrandStatisticsVo brandStatistics = new EElevatorBrandStatisticsVo();
                brandStatistics.setBrandTotal(statisticsList.size());
                brandStatistics.setBrand(brandMap.get(entry.getKey()));
                // 计算占比
                BigDecimal brandRatio = calcPercentRatio(brandStatistics.getBrandTotal(), eleTotal);
                BigDecimal tempTotalRatio = NumberUtil.add(brandRatio, totalRatio);
                if (NumberUtil.isGreater(tempTotalRatio, hundredPercent)) {
                    // 超过最大百分百比例
                    brandRatio = NumberUtil.sub(hundredPercent, totalRatio);
                } else {
                    totalRatio = tempTotalRatio;
                }
                brandStatistics.setRatio(brandRatio);
                res.add(brandStatistics);
            }
        }
        res.sort(Comparator.comparing(EElevatorBrandStatisticsVo::getBrandTotal, Comparator.reverseOrder()));
        return res;
    }

    @Override
    public List<EElevatorUseScenarioStatisticsVo> scenarioStatisticsChartList(EElevatorInfoBo bo) {
        LambdaQueryWrapper<EElevatorInfo> lqw = buildQueryWrapper(bo);
        List<EElevatorInfoVo> elevatorList = baseMapper.selectVoList(lqw);
        List<EElevatorUseScenarioStatisticsVo> res = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(elevatorList)) {
            Map<String, String> useScenarioMap = dictService.getAllDictByDictType(USE_SCENARIO_DICT_KEY);
            Map<Integer, List<EElevatorInfoVo>> brandEleMap = elevatorList.stream()
                .collect(Collectors.groupingBy(EElevatorInfoVo::getUseScenario));
            BigDecimal totalRatio = BigDecimal.ZERO;
            BigDecimal hundredPercent = new BigDecimal("100");
            Integer eleTotal = elevatorList.size();
            for (Map.Entry<Integer, List<EElevatorInfoVo>> entry : brandEleMap.entrySet()) {
                List<EElevatorInfoVo> statisticsList = entry.getValue();
                EElevatorUseScenarioStatisticsVo useScenarioStatisticsVo = new EElevatorUseScenarioStatisticsVo();
                useScenarioStatisticsVo.setUseScenarioTotal(statisticsList.size());
                useScenarioStatisticsVo.setUseScenario(useScenarioMap.get(String.valueOf(entry.getKey())));
                // 计算占比
                BigDecimal useScenarioRatio = calcPercentRatio(useScenarioStatisticsVo.getUseScenarioTotal(), eleTotal);
                BigDecimal tempTotalRatio = NumberUtil.add(useScenarioRatio, totalRatio);
                if (NumberUtil.isGreater(tempTotalRatio, hundredPercent)) {
                    // 超过最大百分百比例
                    useScenarioRatio = NumberUtil.sub(hundredPercent, totalRatio);
                } else {
                    totalRatio = tempTotalRatio;
                }
                useScenarioStatisticsVo.setRatio(useScenarioRatio);
                res.add(useScenarioStatisticsVo);
            }
        }
        res.sort(Comparator.comparing(EElevatorUseScenarioStatisticsVo::getUseScenarioTotal, Comparator.reverseOrder()));
        return res;
    }

    @Override
    public TableDataInfo<EElevatorStateStatisticsVo> queryPageStateStatisticsList(EElevatorInfoBo bo, PageQuery pageQuery) {
        TableDataInfo<EElevatorInfoVo> dataInfo = this.queryPageList(bo, pageQuery);
        TableDataInfo<EElevatorStateStatisticsVo> result = new TableDataInfo<>();
        result.setMsg(dataInfo.getMsg());
        result.setTotal(dataInfo.getTotal());
        result.setCode(dataInfo.getCode());
        List<String> elevatorNumList = new ArrayList<>();
        List<EElevatorStateStatisticsVo> statisticsList = dataInfo.getRows().stream()
            .peek(item -> elevatorNumList.add(item.getElevatorNum()))
            .map(this::buildElevateStateStatistics).collect(Collectors.toList());
        result.setRows(statisticsList);
        if (CollectionUtils.isEmpty(statisticsList)) {
            return result;
        }
        // 维保 维修 救援
        EWorkOperationRecordBo operationRecordBo = new EWorkOperationRecordBo();
        operationRecordBo.setElevatorNumList(elevatorNumList);
        operationRecordBo.setStartTime(bo.getStartTime());
        operationRecordBo.setEndTime(bo.getEndTime());
        operationRecordBo.setStateList(Collections.singletonList(WorkStateEnum.COMPLETE.getState()));
        Map<String, List<EWorkOperationRecord>> elevatorOperationMap = workOperationRecordService.queryOperationRecords(operationRecordBo)
            .stream().collect(Collectors.groupingBy(EWorkOperationRecord::getElevatorNum));
        // 故障 行为次数
        Set<String> faultTypes = dictService.getAllDictByDictType(FAULT_TYPE_DICT_KEY).keySet();
        Set<String> faultExceptionTypes = dictService.getAllDictByDictType(FAULT_EXCEPTION_TYPE_DICT_KEY).keySet();
        EFaultStatisticsBo faultStatisticsBo = new EFaultStatisticsBo();
        faultStatisticsBo.setElevatorNumList(elevatorNumList);
        faultStatisticsBo.setStartTime(bo.getStartTime());
        faultStatisticsBo.setEndTime(bo.getEndTime());
        List<EFaultStatistics> faultStatisticsList = faultStatisticsService.queryFaultStatisticsList(faultStatisticsBo);
        Map<String, List<EFaultStatistics>> elevatorFaultMap = faultStatisticsList.stream()
            .collect(Collectors.groupingBy(EFaultStatistics::getElevatorNum));
        // 反馈次数
        EPassengerFeedbackBo passengerFeedbackBo = new EPassengerFeedbackBo();
        passengerFeedbackBo.setElevatorNumList(elevatorNumList);
        passengerFeedbackBo.setStartTime(bo.getStartTime());
        passengerFeedbackBo.setEndTime(bo.getEndTime());
        Map<String, List<EPassengerFeedbackVo>> feedbackMap = passengerFeedbackService.queryList(passengerFeedbackBo)
            .stream().collect(Collectors.groupingBy(EPassengerFeedbackVo::getElevatorNum));

        // TODO 工作好评率 困人次数 待补充
        for (EElevatorStateStatisticsVo statistics : statisticsList) {
            // 维保 维修 救援统计
            workStatisticsHandler(statistics, elevatorOperationMap);
            // 故障 统计
            faultStatisticsHandler(statistics, elevatorFaultMap, faultTypes, faultExceptionTypes);
            // 反馈次数
            feedbackStatisticsHandler(statistics, feedbackMap);
        }
        return result;
    }

    @Override
    public EElevatorComprehensiveStatisticsVo comprehensiveStatisticsList(EElevatorInfoBo bo) {
        EElevatorComprehensiveStatisticsVo result = new EElevatorComprehensiveStatisticsVo();

        List<EElevatorInfo> elevatorInfos = this.baseMapper.selectList(this.buildQueryWrapper(bo));
        List<String> elevatorNumList = elevatorInfos.stream().map(EElevatorInfo::getElevatorNum).collect(Collectors.toList());
        result.setTotal(elevatorInfos.size());
        // 故障统计
        comprehensiveFaultStatistics(result, bo, elevatorNumList);
        // 维保统计
        comprehensiveMaintenanceStatistics(result, bo, elevatorNumList);
        Integer normalTotal = result.getFaultTotal() > result.getExceptionFaultTotal()
            ? result.getTotal() - result.getFaultTotal() : result.getTotal() - result.getExceptionFaultTotal();
        result.setNormalTotal(normalTotal);
        return result;
    }

    @Override
    public ElevatorRealInfoVo queryElevatorRealInfo(String elevatorNum) {
        EElevatorInfoVo elevatorInfo = this.selectByElevatorNum(elevatorNum);
        if (Objects.isNull(elevatorInfo)) {
            return null;
        }
        ElevatorRealInfoVo result = new ElevatorRealInfoVo();

        EBehaviorMessage behaviorMessage = this.behaviorMessageService.queryLastBehaviorMessage(elevatorNum);
        if (Objects.nonNull(behaviorMessage)) {
            result.setDoorState(behaviorMessage.getDoorStatus());
            result.setFloor(behaviorMessage.getFloor());
            result.setSpeed(behaviorMessage.getSpeed());
            result.setRunState(behaviorMessage.getRunStatus());
            result.setType(11);
            result.setLevelStatus(behaviorMessage.getLevelStatus());
            if (Objects.nonNull(behaviorMessage.getCurrRunDis())) {
                result.setCurrRunDis(behaviorMessage.getCurrRunDis().abs());
                if (behaviorMessage.getCurrRunDis().compareTo(BigDecimal.ZERO) >= 0) {
                    result.setUpRunDis(behaviorMessage.getCurrRunDis().abs());
                } else {
                    result.setDownRunDis(behaviorMessage.getCurrRunDis().abs());
                }
            }
        }
        EBlackBoxInfo blackBoxInfo = blackBoxInfoService.queryBlackBoxInfo(elevatorInfo.getRegisterCode());
        if (Objects.nonNull(blackBoxInfo)) {
            result.setOnlineState(blackBoxInfo.getIsLine());
        }
        // TODO 故障上报状态 维保状态 待补充调整
        result.setMaintenanceState(0);
        result.setFaultReportState(1);
        return result;
    }

    @Override
    public List<EElevatorAgeStatisticsVo> eleAgeStatisticsChartList(EElevatorInfoBo bo) {
        LambdaQueryWrapper<EElevatorInfo> lqw = buildQueryWrapper(bo);
        List<EElevatorInfoVo> elevatorList = baseMapper.selectVoList(lqw);
        List<EElevatorAgeStatisticsVo> res = new ArrayList<>();
        res.add(new EElevatorAgeStatisticsVo("0-6年", 0));
        res.add(new EElevatorAgeStatisticsVo("6-15年", 6));
        res.add(new EElevatorAgeStatisticsVo("15年以上", 15));
        if (CollectionUtils.isEmpty(elevatorList)) {
            return res;
        }
        Map<Integer, EElevatorAgeStatisticsVo> eleAgeMap = StreamUtils.toMap(res, EElevatorAgeStatisticsVo::getAgeFlag, Function.identity());
        LocalDate now = LocalDate.now();
        for (EElevatorInfoVo elevator : elevatorList) {
            if (Objects.isNull(elevator.getInstallDate())) {
                continue;
            }
            long intervalYears = ChronoUnit.YEARS.between(elevator.getInstallDate(), now);
            Integer ageFlag = 15;
            if (intervalYears <= 6L) {
                ageFlag = 0;
            } else if (intervalYears <= 15L) {
                ageFlag = 6;
            }
            EElevatorAgeStatisticsVo statisticsVo = eleAgeMap.get(ageFlag);
            statisticsVo.setTotal(statisticsVo.getTotal() + 1);
        }
        return eleAgeMap.values().stream().sorted(Comparator.comparing(EElevatorAgeStatisticsVo::getAgeFlag)).collect(Collectors.toList());
    }

    @Override
    public String getAreaInfo(String code) {
        return HttpUtil.get(String.format("https://geo.datav.aliyun.com/areas_v3/bound/%s.json", code));
    }

    @Override
    public String getVideoMonitorInfo(String elevatorNum) {
        EElevatorInfoVo elevatorInfo = this.selectByElevatorNum(elevatorNum);
        if (Objects.isNull(elevatorInfo) || Objects.isNull(elevatorInfo.getEleId())) {
            return null;
        }
        AreaClientEnum areaClient = AreaClientEnum.getAreaClient(elevatorInfo.getAreaCode());
        if (Objects.isNull(areaClient)) {
            return null;
        }
        AuthBody authBody = new AuthBody();
        authBody.setClientId(areaClient.getClineId());
        authBody.setClientSecret(areaClient.getClientSecret());
        authBody.setRequestUrl(areaClient.getRequestTokenUrl());
        AuthToken tokenInfo = authTokenService.getToken(authBody);

        return String.format(areaClient.getMonitorUrl(), tokenInfo.getObj().getAccessToken(), elevatorInfo.getEleId());
    }

    @Override
    public Long selectCount(EElevatorInfoBo bo) {
        return this.baseMapper.selectCount(this.buildBaseQueryWrapper(bo));
    }

    /**
     * 维保统计
     *
     * @param result          统计结果
     * @param bo              查询参数
     * @param elevatorNumList 电梯编号列表
     */
    private void comprehensiveMaintenanceStatistics(EElevatorComprehensiveStatisticsVo result, EElevatorInfoBo bo,
                                                    List<String> elevatorNumList) {
        Map<String, String> maintenanceTypeMap = dictService.getAllDictByDictType(ELE_MAINTENANCE_PLAN_TYPE_KEY);
        List<EMaintenancePlan> maintenancePlanList;
        if (CollectionUtils.isEmpty(elevatorNumList)) {
            maintenancePlanList = new ArrayList<>();
        } else {
            // 维保计划
            EMaintenancePlanBo maintenancePlanBo = new EMaintenancePlanBo();
            maintenancePlanBo.setElevatorNumList(elevatorNumList);
            maintenancePlanBo.setStartTime(bo.getStartTime());
            maintenancePlanBo.setEndTime(bo.getEndTime());
            maintenancePlanList = maintenancePlanService.statisticsMaintenancePlanList(maintenancePlanBo);
        }
        Map<Integer, Map<String, List<EMaintenancePlan>>> maintenancePlanMap = maintenancePlanList.stream()
            .filter(item -> Objects.nonNull(item.getPlanType()))
            .collect(Collectors.groupingBy(EMaintenancePlan::getPlanType, Collectors.groupingBy(EMaintenancePlan::getElevatorNum)));

        List<EElevatorComprehensiveStatisticsVo.MaintenancePlanInfo> planInfoList = IntStream.range(1, 5)
            .mapToObj(planType -> {
                EElevatorComprehensiveStatisticsVo.MaintenancePlanInfo info = new EElevatorComprehensiveStatisticsVo.MaintenancePlanInfo();
                info.setMaintenanceType(planType);
                info.setMaintenanceName(maintenanceTypeMap.get(String.valueOf(planType)));
                Integer maintenanceTotal = 0;
                if (MapUtils.isNotEmpty(maintenancePlanMap.get(planType))) {
                    maintenanceTotal = maintenancePlanMap.get(planType).keySet().size();
                }
                info.setMaintenanceElevatorTotal(maintenanceTotal);
                // 总维保电梯数
                result.setMaintenanceTotal(result.getMaintenanceTotal()  + maintenanceTotal);
                return info;
            }).collect(Collectors.toList());
        result.setMaintenancePlanList(planInfoList);
        // 维保率  维保电梯总数/总电梯数
        BigDecimal maintenanceRatio = calcPercentRatio(result.getMaintenanceTotal(), result.getTotal());
        result.setMaintenanceRatio(maintenanceRatio);
    }

    /**
     * 故障统计
     *
     * @param result          故障信息
     * @param bo              查询参数
     * @param elevatorNumList 电梯编号
     */
    private void comprehensiveFaultStatistics(EElevatorComprehensiveStatisticsVo result, EElevatorInfoBo bo,
                                              List<String> elevatorNumList) {
        // 故障类型
        List<String> faultCodes = Arrays.asList("F01","F02","F03","F04","F05","F06","F07","F08","F09","F10","F11","F12");
        // 异常故障类型
        List<String> exceptionFaultCodes = Arrays.asList("E01","E02","E03","E04","E07","E08","E09","E10");
        Set<String> exceptionFaultCodeSet = new HashSet<>(exceptionFaultCodes);

        List<String> faultCodeList = new ArrayList<>();
        faultCodeList.addAll(faultCodes);
        faultCodeList.addAll(exceptionFaultCodes);

        Map<String, String> faultTypeMap = dictService.getAllDictByDictType(FAULT_TYPE_DICT_KEY);
        Map<String, String> exceptionFaultTypeMap = dictService.getAllDictByDictType(FAULT_EXCEPTION_TYPE_DICT_KEY);
        faultTypeMap.putAll(exceptionFaultTypeMap);
        List<EFaultStatistics> faultStatisticsList;
        if (CollectionUtils.isEmpty(elevatorNumList)) {
            faultStatisticsList = new ArrayList<>();
        } else {
            EFaultStatisticsBo faultStatisticsBo = new EFaultStatisticsBo();
            faultStatisticsBo.setElevatorNumList(elevatorNumList);
            faultStatisticsBo.setFaultCodeList(faultCodeList);
            faultStatisticsBo.setStartTime(bo.getStartTime());
            faultStatisticsBo.setEndTime(bo.getEndTime());
            faultStatisticsList = faultStatisticsService.queryFaultStatisticsList(faultStatisticsBo);
        }
        // 故障电梯编号
        Set<String> faultEleNums = new HashSet<>();
        // 异常故障电梯编号
        Set<String> exceptionFaultEleNums = new HashSet<>();
        // 故障编号分组 -> 电梯编号分组
        Map<String, Map<String, List<EFaultStatistics>>> elevatorFaultMap = faultStatisticsList.stream()
            .peek(item -> {
                if (exceptionFaultCodeSet.contains(item.getFaultCode())) {
                    exceptionFaultEleNums.add(item.getElevatorNum());
                } else {
                    faultEleNums.add(item.getElevatorNum());
                }
            })
            .collect(Collectors.groupingBy(EFaultStatistics::getFaultCode, Collectors.groupingBy(EFaultStatistics::getElevatorNum)));
        // 故障电梯总数
        result.setFaultTotal(faultEleNums.size());
        result.setExceptionFaultTotal(exceptionFaultEleNums.size());

        result.setFaultTotalRatio(calcPercentRatio(result.getFaultTotal(), result.getTotal()));
        result.setExceptionFaultRatio(calcPercentRatio(result.getExceptionFaultTotal(), result.getTotal()));

        // 故障
        buildComprehensiveFaultStatistics(result, Boolean.FALSE, faultCodes, faultTypeMap, elevatorFaultMap);
        // 异常故障
        buildComprehensiveFaultStatistics(result, Boolean.TRUE, exceptionFaultCodes, faultTypeMap, elevatorFaultMap);
    }

    /**
     * 构建故障统计信息
     *
     * @param result           统计信息
     * @param isExceptionFault 是否异常故障
     * @param faultCodes       故障编号
     * @param faultTypeMap     故障类型Map
     * @param elevatorFaultMap 电梯故障Map
     */
    private void buildComprehensiveFaultStatistics(EElevatorComprehensiveStatisticsVo result, Boolean isExceptionFault,
                                                   List<String> faultCodes, Map<String, String> faultTypeMap,
                                                   Map<String, Map<String, List<EFaultStatistics>>> elevatorFaultMap) {
        BigDecimal totalRatio = BigDecimal.ZERO;
        BigDecimal hundredPercent = new BigDecimal("100");
        for (String faultCode : faultCodes) {
            EElevatorComprehensiveStatisticsVo.FaultInfo faultInfo = new EElevatorComprehensiveStatisticsVo.FaultInfo();
            faultInfo.setFaultCode(faultCode);
            faultInfo.setFaultName(faultTypeMap.get(faultCode));
            if (elevatorFaultMap.containsKey(faultCode)) {
                // 故障电梯数
                faultInfo.setFaultElevatorTotal(elevatorFaultMap.get(faultCode).keySet().size());
                // 计算占比
                Integer faultTotal = isExceptionFault ?  result.getExceptionFaultTotal() : result.getFaultTotal();
                BigDecimal faultRatio = calcPercentRatio(faultInfo.getFaultElevatorTotal(), faultTotal);
                BigDecimal tempTotalRatio = NumberUtil.add(faultRatio, totalRatio);
                if (NumberUtil.isGreater(tempTotalRatio, hundredPercent)) {
                    // 超过最大百分百比例
                    faultRatio = NumberUtil.sub(hundredPercent, totalRatio);
                } else {
                    totalRatio = tempTotalRatio;
                }
                faultInfo.setFaultRatio(faultRatio);
            }
            if (isExceptionFault) {
                result.getExceptionFaultList().add(faultInfo);
            } else {
                result.getFaultList().add(faultInfo);
            }
        }

    }

    /**
     * 计算百分比
     *
     * @param v1 分子
     * @param v2 分母
     * @return 百分比
     */
    private BigDecimal calcPercentRatio(Number v1, Number v2) {
        if (Objects.isNull(v2) || Objects.equals(v2, 0)) {
            return BigDecimal.ZERO;
        }
        return NumberUtil.div(v1, v2, 4).multiply(new BigDecimal("100")).setScale(2, RoundingMode.HALF_UP);
    }

    /**
     * 反馈次数统计处理
     *
     * @param statistics  统计信息
     * @param feedbackMap 反馈信息Map
     */
    private void feedbackStatisticsHandler(EElevatorStateStatisticsVo statistics,
                                           Map<String, List<EPassengerFeedbackVo>> feedbackMap) {
        if (feedbackMap.containsKey(statistics.getElevatorNum())) {
            statistics.setFeedbackTotal(statistics.getFeedbackTotal() + feedbackMap.get(statistics.getElevatorNum()).size());
        }
    }

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

    /**
     * 工作统计处理
     *
     * @param statistics           统计信息
     * @param elevatorOperationMap 工作记录
     */
    private void workStatisticsHandler(EElevatorStateStatisticsVo statistics,
                                       Map<String, List<EWorkOperationRecord>> elevatorOperationMap) {
        if (elevatorOperationMap.containsKey(statistics.getElevatorNum())) {
            for (EWorkOperationRecord record : elevatorOperationMap.get(statistics.getElevatorNum())) {
                //  救援次数
                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 elevator 电梯信息
     * @return 电梯状况统计
     */
    private EElevatorStateStatisticsVo buildElevateStateStatistics(EElevatorInfoVo elevator) {
        EElevatorStateStatisticsVo statistics = new EElevatorStateStatisticsVo();
        statistics.setElevatorName(elevator.getElevatorName());
        statistics.setElevatorNum(elevator.getElevatorNum());
        statistics.setRegisterCode(elevator.getRegisterCode());
        statistics.setElevatorAddress(elevator.getElevatorAddress());
        statistics.setMaintenanceId(elevator.getMaintenanceId());
        statistics.setMaintenanceName(elevator.getMaintenanceName());
        statistics.setTenementId(elevator.getTenementId());
        statistics.setTenementName(elevator.getTenementName());
        return statistics;
    }
}
