package org.dromara.elevator.job;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import lombok.RequiredArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.dromara.common.core.utils.DateUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.json.utils.JsonUtils;
import org.dromara.elevator.domain.EBehaviorStatistics;
import org.dromara.elevator.domain.bo.EBehaviorFloorStatisticsBo;
import org.dromara.elevator.domain.bo.EBehaviorStatisticsBo;
import org.dromara.elevator.domain.bo.EElevatorExaminationBo;
import org.dromara.elevator.domain.bo.EElevatorInfoBo;
import org.dromara.elevator.domain.vo.EElevatorExaminationVo;
import org.dromara.elevator.domain.vo.EElevatorInfoVo;
import org.dromara.elevator.domain.vo.ElevatorBehaviorStatisticsVo;
import org.dromara.elevator.service.IEBehaviorStatisticsService;
import org.dromara.elevator.service.IEElevatorExaminationService;
import org.dromara.elevator.service.IEElevatorInfoService;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

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

import static org.dromara.common.core.utils.DateUtils.YYYY_MM;
import static org.dromara.elevator.constant.ElevatorConstant.ELE_BEHAVIOR_STATISTICS_DAY_TYPE;

/**
 * 电梯体检定时器 每月1号处理上月数据
 *
 * @author ym
 * @version 1.0.0
 * @date 2024-10-10 15:56
 */
@Component
@RequiredArgsConstructor
public class ElevatorExaminationJob {

    private final IEElevatorInfoService elevatorInfoService;

    private final IEBehaviorStatisticsService behaviorStatisticsService;

    private final IEElevatorExaminationService elevatorExaminationService;

    /**
     * 上月数据统计
     */
    //@Scheduled(cron = "0 30 0 1 * ? ")
    public void elevatorExaminationJob() {
        // 上月开始结束时间
        Date lastMonth = DateUtil.lastMonth();
        Date startTime = DateUtil.beginOfMonth(lastMonth);
        Date endTime = DateUtil.endOfMonth(startTime);
        List<EElevatorInfoVo> elevatorList = elevatorInfoService.queryList(new EElevatorInfoBo());
        for (EElevatorInfoVo elevator : elevatorList) {
            EElevatorExaminationBo bo = new EElevatorExaminationBo();
            bo.setElevatorNum(elevator.getElevatorNum());
            bo.setDeptId(elevator.getDeptId());
            bo.setCreateBy(1L);
            bo.setExaminationTime(DateUtils.parseDateToStr(YYYY_MM, startTime));
            bo.setExaminationNum(bo.getExaminationTime() + elevator.getElevatorNum());
            this.handleDayStatistics(elevator, startTime, endTime, bo);
            this.handleWeekMonthStatistics(elevator, startTime, endTime, bo);

            EElevatorExaminationBo queryBo = new EElevatorExaminationBo();
            queryBo.setElevatorNum(elevator.getElevatorNum());
            queryBo.setStartTime(startTime);
            queryBo.setEndTime(endTime);
            EElevatorExaminationVo existExamInfo = elevatorExaminationService.queryExaminationInfo(queryBo);
            if (Objects.nonNull(existExamInfo)) {
                bo.setId(existExamInfo.getId());
            }
            this.elevatorExaminationService.insertOrupdate(bo);
        }
    }

    /**
     * 周月统计处理
     *
     * @param elevator      电梯信息
     * @param startTime     开始时间
     * @param endTime       结束时间
     * @param examinationBo 体检信息
     */
    private void handleWeekMonthStatistics(EElevatorInfoVo elevator, Date startTime,
                                           Date endTime, EElevatorExaminationBo examinationBo) {
        EBehaviorStatisticsBo bo = new EBehaviorStatisticsBo();
        bo.setElevatorNum(elevator.getElevatorNum());
        bo.setStartTime(startTime);
        bo.setEndTime(endTime);
        List<EBehaviorStatistics> behaviorStatisticsList = this.behaviorStatisticsService.queryBehaviorStatisticsList(bo);

        doHandlerMonthStatistics(startTime, endTime, examinationBo, behaviorStatisticsList);

        doHandlerWeekStatistics(examinationBo, behaviorStatisticsList);
    }

    /**
     * 周统计
     *
     * @param examinationBo          体检信息
     * @param behaviorStatisticsList 统计数据
     */
    private void doHandlerWeekStatistics(EElevatorExaminationBo examinationBo, List<EBehaviorStatistics> behaviorStatisticsList) {
        // 周统计
        // 上行次数
        List<Integer> weekUpNumList = new ArrayList<>();
        // 下行次数
        List<Integer> weekDownNumList = new ArrayList<>();
        // 开关门次数
        List<Integer> weekOpenCloseNumList = new ArrayList<>();
        // 停站次数
        List<Integer> weekStopStationNumList = new ArrayList<>();
        // 运行距离
        List<BigDecimal> weekRunMileageList = new ArrayList<>();
        Map<DayOfWeek, List<EBehaviorStatistics>> weekStatisticsMap = behaviorStatisticsList.stream()
            .collect(Collectors.groupingBy(item -> item.getStatisticsTime().getDayOfWeek()));
        Arrays.stream(DayOfWeek.values()).sorted(Comparator.comparing(Enum::ordinal))
            .map(weekStatisticsMap::get).forEach(statisticsList -> {
                Integer weekUpNum = 0;
                Integer weekDownNum = 0;
                Integer weekOpenCloseNum = 0;
                Integer weekStopStationNum = 0;
                BigDecimal weekRunMileage = BigDecimal.ZERO;
                if (CollectionUtils.isNotEmpty(statisticsList)) {
                    for (EBehaviorStatistics statistics : statisticsList) {
                        weekUpNum = weekUpNum + statistics.getUpNum();
                        weekDownNum = weekDownNum + statistics.getDownNum();
                        weekOpenCloseNum = weekOpenCloseNum + statistics.getOpenCloseNum();
                        weekStopStationNum = weekStopStationNum + statistics.getStopStationNum();
                        weekRunMileage = weekRunMileage.add(statistics.getMileage());
                    }
                }
                weekUpNumList.add(weekUpNum);
                weekDownNumList.add(weekDownNum);
                weekOpenCloseNumList.add(weekOpenCloseNum);
                weekStopStationNumList.add(weekStopStationNum);
                weekRunMileageList.add(weekRunMileage.setScale(2, RoundingMode.HALF_UP));
            });
        Map<String, Object> statisticsWeekMap = new HashMap<>();
        statisticsWeekMap.put("weekUpNumList", weekUpNumList);
        statisticsWeekMap.put("weekDownNumList", weekDownNumList);
        statisticsWeekMap.put("weekOpenCloseNumList", weekOpenCloseNumList);
        statisticsWeekMap.put("weekStopStationNumList", weekStopStationNumList);
        statisticsWeekMap.put("weekRunMileageList", weekRunMileageList);
        examinationBo.setStatisticsWeekData(JsonUtils.toJsonString(statisticsWeekMap));
    }

    /**
     * 月统计
     *
     * @param startTime              开始时间
     * @param endTime                结束时间
     * @param examinationBo          体检信息
     * @param behaviorStatisticsList 统计数据
     */
    private void doHandlerMonthStatistics(Date startTime, Date endTime, EElevatorExaminationBo examinationBo,
                                          List<EBehaviorStatistics> behaviorStatisticsList) {
        // 上行次数
        List<Integer> monthUpNumList = new ArrayList<>();
        // 下行次数
        List<Integer> monthDownNumList = new ArrayList<>();
        // 开关门次数
        List<Integer> monthOpenCloseNumList = new ArrayList<>();
        // 停站次数
        List<Integer> monthStopStationNumList = new ArrayList<>();
        // 运行距离
        List<BigDecimal> monthRunMileageList = new ArrayList<>();

        Map<LocalDate, EBehaviorStatistics> behaviorStatisticsMap = behaviorStatisticsList.stream()
            .collect(Collectors.toMap(EBehaviorStatistics::getStatisticsTime, Function.identity()));
        List<LocalDate> dayList = DateUtils.getDatesBetween(startTime, endTime);

        // 月统计
        for (LocalDate day : dayList) {
            Integer monthUpNum = 0;
            Integer monthDownNum = 0;
            Integer monthOpenCloseNum = 0;
            Integer monthStopStationNum = 0;
            BigDecimal monthRunMileage = BigDecimal.ZERO;
            EBehaviorStatistics statistics = behaviorStatisticsMap.get(day);
            if (Objects.nonNull(statistics)) {
                monthUpNum = statistics.getUpNum();
                monthDownNum = statistics.getDownNum();
                monthOpenCloseNum = statistics.getOpenCloseNum();
                monthStopStationNum = statistics.getStopStationNum();
                monthRunMileage = statistics.getMileage();
            }
            monthUpNumList.add(monthUpNum);
            monthDownNumList.add(monthDownNum);
            monthOpenCloseNumList.add(monthOpenCloseNum);
            monthStopStationNumList.add(monthStopStationNum);
            monthRunMileageList.add(monthRunMileage.setScale(2, RoundingMode.HALF_UP));
        }
        Map<String, Object> statisticsMonthMap = new HashMap<>();
        statisticsMonthMap.put("monthUpNumList", monthUpNumList);
        statisticsMonthMap.put("monthDownNumList", monthDownNumList);
        statisticsMonthMap.put("monthOpenCloseNumList", monthOpenCloseNumList);
        statisticsMonthMap.put("monthStopStationNumList", monthStopStationNumList);
        statisticsMonthMap.put("monthRunMileageList", monthRunMileageList);
        examinationBo.setStatisticsMonthData(JsonUtils.toJsonString(statisticsMonthMap));
    }

    /**
     * 日统计处理
     *
     * @param elevator      电梯信息
     * @param startTime     开始时间
     * @param endTime       结束时间
     * @param examinationBo 体检信息
     */
    private void handleDayStatistics(EElevatorInfoVo elevator, Date startTime,
                                     Date endTime, EElevatorExaminationBo examinationBo) {
        if (StringUtils.isBlank(elevator.getStandNum())) {
            return;
        }
        List<LocalDate> dayList = DateUtils.getDatesBetween(startTime, endTime);

        EBehaviorStatisticsBo bo = new EBehaviorStatisticsBo();
        bo.setElevatorNum(elevator.getElevatorNum());
        bo.setStatisticsType(ELE_BEHAVIOR_STATISTICS_DAY_TYPE);
        // 电梯层数
        Integer floorNum = Integer.valueOf(elevator.getStandNum().trim().split("/")[0]);
        // key 电梯楼层 跳过第一层比较
        Map<Integer, EBehaviorFloorStatisticsBo> floorStatisticsMap = IntStream.range(1, floorNum + 1)
            .mapToObj(EBehaviorFloorStatisticsBo::new)
            .collect(Collectors.toMap(EBehaviorFloorStatisticsBo::getFloor, Function.identity()));

        List<ElevatorBehaviorStatisticsVo> behaviorStatisticsList = new ArrayList<>();
        // 高峰日期 停站次数
        LocalDate peakDate = null;
        Integer peakNum = 0;
        for (LocalDate currDate : dayList) {
            Date currStartTime = DateUtils.toDate(currDate);
            bo.setStartTime(currStartTime);
            bo.setEndTime(DateUtil.endOfDay(currStartTime));
            ElevatorBehaviorStatisticsVo statisticsInfo = behaviorStatisticsService.queryElevatorStatisticsInfo(bo);
            behaviorStatisticsList.add(statisticsInfo);
            int stopStationNum = statisticsInfo.getStopStationNumList().stream().mapToInt(Integer::intValue).sum();
            if (stopStationNum >= peakNum) {
                peakNum = stopStationNum;
                peakDate = currDate;
            }
            // 不算第一层  索引1 => 2层
            floorStatisticsMap.forEach((floor, item) -> {
                Integer floorStopStationNum = statisticsInfo.getFloorStopStationNumList().get(floor - 1);
                item.setStopStationNum(floorStopStationNum + item.getStopStationNum());
            });
        }
        // 繁忙楼层 平均停梯次数
        // 默认2层 停站次数
        EBehaviorFloorStatisticsBo busyFloorInfo = floorStatisticsMap.values().stream()
            .max(Comparator.comparing(EBehaviorFloorStatisticsBo::getStopStationNum))
            .orElseGet(() -> new EBehaviorFloorStatisticsBo(2));

        Long intervalDay = DateUtil.betweenDay(startTime, endTime, Boolean.TRUE);
        Integer avgStopStationNum = NumberUtil.div(busyFloorInfo.getStopStationNum(), intervalDay, 0).intValue();

        examinationBo.setPeakDate(peakDate);
        examinationBo.setPeakRunNum(peakNum);
        examinationBo.setBusyFloor(busyFloorInfo.getFloor());
        examinationBo.setBusyFloorNum(avgStopStationNum);

        // 数据处理
        // 上行次数
        List<Integer> upNumList = new ArrayList<>();
        // 下行次数
        List<Integer> downNumList = new ArrayList<>();
        // 开关门次数
        List<Integer> openCloseNumList = new ArrayList<>();
        // 停站次数
        List<Integer> stopStationNumList = new ArrayList<>();
        // 运行距离
        List<BigDecimal> runMileageList = new ArrayList<>();
        for (Integer hour : behaviorStatisticsList.get(0).getTimeLineList()) {
            Integer upNum = 0;
            Integer downNum = 0;
            Integer openCloseNum = 0;
            Integer stopStationNum = 0;
            BigDecimal runMileage = BigDecimal.ZERO;
            for (ElevatorBehaviorStatisticsVo statistics : behaviorStatisticsList) {
                upNum = upNum + statistics.getUpNumList().get(hour);
                downNum = downNum + statistics.getDownNumList().get(hour);
                openCloseNum = openCloseNum + statistics.getOpenCloseNumList().get(hour);
                stopStationNum = stopStationNum  + statistics.getStopStationNumList().get(hour);
                runMileage = runMileage.add(statistics.getRunMileageList().get(hour));
            }
            upNumList.add(upNum);
            downNumList.add(downNum);
            openCloseNumList.add(openCloseNum);
            stopStationNumList.add(stopStationNum);
            runMileageList.add(runMileage.setScale(2, RoundingMode.HALF_UP));
        }
        Map<String, Object> statisticsDayMap = new HashMap<>();
        statisticsDayMap.put("upNumList", upNumList);
        statisticsDayMap.put("downNumList", downNumList);
        statisticsDayMap.put("openCloseNumList", openCloseNumList);
        statisticsDayMap.put("stopStationNumList", stopStationNumList);
        statisticsDayMap.put("runMileageList", runMileageList);
        examinationBo.setStatisticsDayData(JsonUtils.toJsonString(statisticsDayMap));
        // TODO 高峰时段待补充
    }

}
