package cn.iocoder.yudao.module.system.service.statistics;

import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
import cn.iocoder.yudao.module.system.constant.robot.RobotTaskChcheConstant;
import cn.iocoder.yudao.module.system.controller.admin.statistics.dto.RobotWorkHourStatisticsDTO;
import cn.iocoder.yudao.module.system.controller.admin.statistics.vo.RobotWorkingHoursStatisticsPageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.statistics.vo.RobotWorkingHoursStatisticsSaveReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.log.RobotTaskDetailActionLogDO;
import cn.iocoder.yudao.module.system.dal.dataobject.robot.RobotInformationDO;
import cn.iocoder.yudao.module.system.dal.dataobject.statistics.RobotWorkingHoursStatisticsDO;
import cn.iocoder.yudao.module.system.dal.mysql.statistics.RobotWorkingHoursStatisticsMapper;
import cn.iocoder.yudao.module.system.enums.robot.RobotStatusEnum;
import cn.iocoder.yudao.module.system.enums.robot.statistics.DurationTypeEnum;
import cn.iocoder.yudao.module.system.service.log.RobotTaskDetailActionLogService;
import cn.iocoder.yudao.module.system.service.robot.RobotInformationService;
import cn.iocoder.yudao.module.system.util.redis.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

import org.springframework.transaction.annotation.Propagation;
import org.springframework.validation.annotation.Validated;
import org.springframework.transaction.annotation.Transactional;

import java.text.SimpleDateFormat;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;


import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.ROBOT_WORKING_HOURS_STATISTICS_NOT_EXISTS;

/**
 * 车辆工作时长统计 Service 实现类
 *
 * @author 陈宾顺
 */
@Service
@Validated
@Slf4j
public class RobotWorkingHoursStatisticsServiceImpl extends ServiceImpl<RobotWorkingHoursStatisticsMapper, RobotWorkingHoursStatisticsDO> implements RobotWorkingHoursStatisticsService {

    @Resource
    private RobotWorkingHoursStatisticsMapper robotWorkingHoursStatisticsMapper;

    @Resource
    private RobotTaskDetailActionLogService taskDetailActionLogService;

    @Resource
    private RobotInformationService informationService;

    @Resource
    private RedisUtil redisUtil;

    /**
     * 统计工作时长
     */
    @Override
    public void statisticsRobotDuration() {
        List<RobotInformationDO> robots = informationService.getAllRobot();
        if (ObjectUtil.isEmpty(robots)) {
            log.info("统计工作时长--暂无车辆");
            return;
        }

        List<RobotWorkingHoursStatisticsDO> statisticss = new ArrayList<>();
        for (RobotInformationDO robot : robots) {
            String freeTimeKey = RobotTaskChcheConstant.ROBOT_LAST_FREE_TIME + robot.getRobotNo();
            Object freeTimeObject = redisUtil.get(freeTimeKey);

            if (ObjectUtil.isNotEmpty(freeTimeObject)
                    && RobotStatusEnum.STAND_BY.getType().equals(robot.getRobotStatus())) {
                RobotWorkingHoursStatisticsDO statisticsDO = new RobotWorkingHoursStatisticsDO();
                statisticsDO.setDuration(getMinutes(freeTimeObject));
                if (statisticsDO.getDuration() == 0) {
                    continue;
                }
                statisticsDO.setRobotNo(robot.getRobotNo());
                Long mapId = informationService.getRobotMapIdByRobotNo(robot.getRobotNo());
                if (ObjectUtil.isNotEmpty(mapId)) {
                    statisticsDO.setPositionMapId(mapId);
                }
                statisticsDO.setDurationType(DurationTypeEnum.FREE_TIME.getType());
                statisticss.add(statisticsDO);
            }

            if (RobotStatusEnum.STAND_BY.getType().equals(robot.getRobotStatus())) {
                String timeStr = DateUtils.getYearMonthDayHourMinuteSecon();
                redisUtil.set(freeTimeKey, timeStr);
            }
        }

        if (ObjectUtil.isNotEmpty(statisticss)) {
            robotWorkingHoursStatisticsMapper.insertBatch(statisticss);
        }

        List<RobotTaskDetailActionLogDO> actionLogList = taskDetailActionLogService.getUnStatisticsDurationLog();
        if (ObjectUtil.isEmpty(actionLogList)) {
            log.info("统计工作时长--没有需要统计的任务信息");
            return;
        }

        List<RobotWorkingHoursStatisticsDO> statisticsDOS = getWorkingHoursStatisticsByActionLog(actionLogList);
        if (ObjectUtil.isNotEmpty(statisticsDOS)) {
            robotWorkingHoursStatisticsMapper.insertBatch(statisticsDOS);
        }


    }

    /**
     * 创建这台车的空闲时间
     * 别改事务传播
     *
     * @param robotNo
     */
    @Override
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
    public void createFreeTime(String robotNo) {
        String freeTimeKey = RobotTaskChcheConstant.ROBOT_LAST_FREE_TIME + robotNo;
        Object freeTimeObject = redisUtil.get(freeTimeKey);
        if (ObjectUtil.isEmpty(freeTimeObject)) {
            log.info("统计车辆空闲时间，没有车辆的初始空闲时间");
            return;
        }
        RobotWorkingHoursStatisticsDO statisticsDO = new RobotWorkingHoursStatisticsDO();
        statisticsDO.setDuration(getMinutes(freeTimeObject));
        statisticsDO.setRobotNo(robotNo);
        Long mapId = informationService.getRobotMapIdByRobotNo(robotNo);
        if (ObjectUtil.isNotEmpty(mapId)) {
            statisticsDO.setPositionMapId(mapId);
        }
        statisticsDO.setDurationType(DurationTypeEnum.FREE_TIME.getType());
        robotWorkingHoursStatisticsMapper.insert(statisticsDO);
        redisUtil.del(freeTimeKey);
    }

    /**
     * 统计时长
     *
     * @param type
     * @param positionMapId
     * @return
     */
    @Override
    public List<RobotWorkHourStatisticsDTO> robotWorkHourStatistics(String type, String positionMapId) {
        List<RobotWorkingHoursStatisticsDO> data = robotWorkingHoursStatisticsMapper.getRobotWorkHourStatistics(type, positionMapId);
        if (ObjectUtil.isEmpty(data)) {
            return null;
        }

        Map<String, Map<Integer, Long>> robotMap = data.stream()
                .collect(Collectors.groupingBy(
                        RobotWorkingHoursStatisticsDO::getRobotNo,
                        Collectors.groupingBy(
                                RobotWorkingHoursStatisticsDO::getDurationType, Collectors.summingLong(RobotWorkingHoursStatisticsDO::getDuration)
                        )
                ));

        List<RobotWorkHourStatisticsDTO> list = new ArrayList<>();

        robotMap.forEach((k, v) -> {
            RobotWorkHourStatisticsDTO statisticsDTO = new RobotWorkHourStatisticsDTO();
            statisticsDTO.setRobotNo(k);
            Long freeTime = v.get(DurationTypeEnum.FREE_TIME.getType());
            String freeTimeStr = ObjectUtil.isNotEmpty(freeTime) ? String.format("%.1f", (freeTime.doubleValue() / 60)) : "0";
            Long taskTime = v.get(DurationTypeEnum.TASK_TIME.getType());
            String taskTimeStr = ObjectUtil.isNotEmpty(taskTime) ? String.format("%.1f", (taskTime.doubleValue() / 60)) : "0";
            Long chargeTime = v.get(DurationTypeEnum.CHARGE_TIME.getType());
            String chargeTimeStr = ObjectUtil.isNotEmpty(chargeTime) ? String.format("%.1f", (chargeTime.doubleValue() / 60)) : "0";

            statisticsDTO.setFreeTimeNum(freeTimeStr);
            statisticsDTO.setTaskTimeNum(taskTimeStr);
            statisticsDTO.setChargeTimeNum(chargeTimeStr);

            list.add(statisticsDTO);
        });

        return list;
    }

    /**
     * 获取这个时间距离当前时间的分钟
     *
     * @param freeTimeObject
     * @return
     */
    public Long getMinutes(Object freeTimeObject) {
        String freeTimeStr = freeTimeObject.toString();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND);
        LocalDateTime formattedDateTime = LocalDateTime.parse(freeTimeStr, formatter);
        Duration duration = Duration.between(formattedDateTime, LocalDateTime.now());
        return duration.toMinutes();
    }

    private List<RobotWorkingHoursStatisticsDO> getWorkingHoursStatisticsByActionLog(List<RobotTaskDetailActionLogDO> actionLogList) {
        List<RobotWorkingHoursStatisticsDO> statisticsDOList = new ArrayList<>();

        List<Long> logIds = new ArrayList<>();

        Map<String, RobotWorkingHoursStatisticsDO> map = new HashMap<>();
        for (RobotTaskDetailActionLogDO v : actionLogList) {
            RobotWorkingHoursStatisticsDO statisticsDO = new RobotWorkingHoursStatisticsDO();
            if (ObjectUtil.isNotEmpty(v.getDuration())) {
                statisticsDO.setDuration(v.getDuration());
            } else if (ObjectUtil.isNotEmpty(v.getStartTime()) && ObjectUtil.isNotEmpty(v.getEndTime())) {
                Duration duration = Duration.between(v.getStartTime(), v.getEndTime());
                statisticsDO.setDuration(duration.toMinutes());
            } else if (ObjectUtil.isEmpty(v.getStartTime()) || ObjectUtil.isEmpty(v.getEndTime())) {
                continue;
            }
            statisticsDO.setRobotNo(v.getRobotNo());
            statisticsDO.setPositionMapId(v.getPositionMapId());
            Integer durationTypeByTaskType = DurationTypeEnum.getDurationTypeByTaskType(v.getCommandType());
            statisticsDO.setDurationType(durationTypeByTaskType);
            logIds.add(v.getId());
            String key = statisticsDO.getRobotNo() + "-" + statisticsDO.getPositionMapId() + "-" + statisticsDO.getDurationType();
            RobotWorkingHoursStatisticsDO statistics = map.get(key);
            if (ObjectUtil.isNotEmpty(statistics)) {
                statistics.setDuration(statistics.getDuration()+statisticsDO.getDuration());
            } else {
                statisticsDOList.add(statisticsDO);
            }
        }

        if (ObjectUtil.isEmpty(logIds)) {
            return statisticsDOList;
        }

        taskDetailActionLogService.alreadyCounted(logIds);
        return statisticsDOList;
    }

    @Override
    public Long createRobotWorkingHoursStatistics(RobotWorkingHoursStatisticsSaveReqVO createReqVO) {
        // 插入
        RobotWorkingHoursStatisticsDO robotWorkingHoursStatistics = BeanUtils.toBean(createReqVO, RobotWorkingHoursStatisticsDO.class);
        robotWorkingHoursStatisticsMapper.insert(robotWorkingHoursStatistics);
        // 返回
        return robotWorkingHoursStatistics.getId();
    }

    @Override
    public void updateRobotWorkingHoursStatistics(RobotWorkingHoursStatisticsSaveReqVO updateReqVO) {
        // 校验存在
        validateRobotWorkingHoursStatisticsExists(updateReqVO.getId());
        // 更新
        RobotWorkingHoursStatisticsDO updateObj = BeanUtils.toBean(updateReqVO, RobotWorkingHoursStatisticsDO.class);
        robotWorkingHoursStatisticsMapper.updateById(updateObj);
    }

    @Override
    public void deleteRobotWorkingHoursStatistics(Long id) {
        // 校验存在
        validateRobotWorkingHoursStatisticsExists(id);
        // 删除
        robotWorkingHoursStatisticsMapper.deleteById(id);
    }

    private void validateRobotWorkingHoursStatisticsExists(Long id) {
        if (robotWorkingHoursStatisticsMapper.selectById(id) == null) {
            throw exception(ROBOT_WORKING_HOURS_STATISTICS_NOT_EXISTS);
        }
    }

    @Override
    public RobotWorkingHoursStatisticsDO getRobotWorkingHoursStatistics(Long id) {
        return robotWorkingHoursStatisticsMapper.selectById(id);
    }

    @Override
    public PageResult<RobotWorkingHoursStatisticsDO> getRobotWorkingHoursStatisticsPage(RobotWorkingHoursStatisticsPageReqVO pageReqVO) {
        return robotWorkingHoursStatisticsMapper.selectPage(pageReqVO);
    }

}
