package com.ruoyi.doubledefense.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.constant.DeletedEnum;
import com.ruoyi.common.core.domain.entity.TbSysArea;
import com.ruoyi.common.core.domain.entity.TbSysAreaUser;
import com.ruoyi.common.exception.base.BaseException;
import com.ruoyi.common.utils.JWTUtil;
import com.ruoyi.doubledefense.domain.CheckTaskInventory;
import com.ruoyi.doubledefense.domain.CheckTaskInventoryRecord;
import com.ruoyi.doubledefense.dto.res.CheckTasRiskUnitkListDTOForApp;
import com.ruoyi.doubledefense.dto.res.CheckTaskListDTOForApp;
import com.ruoyi.doubledefense.mapper.CheckTaskInventoryMapper;
import com.ruoyi.doubledefense.mapper.CheckTaskInventoryRecordMapper;
import com.ruoyi.doubledefense.service.ICheckTaskInventoryService;
import com.ruoyi.system.mapper.TbSysAreaMapper;
import com.ruoyi.system.service.ISysPostService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 隐患排查任务清单Service业务层处理
 *
 * @author ruoyi
 * @date 2023-08-03
 */
@Service
@Slf4j
public class CheckTaskInventoryServiceImpl extends ServiceImpl<CheckTaskInventoryMapper, CheckTaskInventory> implements ICheckTaskInventoryService {
    @Autowired
    private CheckTaskInventoryMapper checkTaskInventoryMapper;
    @Autowired
    private CheckTaskInventoryRecordMapper checkTaskInventoryRecordMapper;
    @Autowired
    private ISysPostService sysPostService;
    @Autowired
    private TbSysAreaMapper tbSysAreaMapper;

    /**
     * 查询隐患排查任务清单
     *
     * @param id 隐患排查任务清单主键
     * @return 隐患排查任务清单
     */
    @Override
    public CheckTaskInventory selectCheckTaskInventoryById(String id) {
        return checkTaskInventoryMapper.selectCheckTaskInventoryById(id);
    }

    /**
     * 查询隐患排查任务清单列表
     *
     * @param checkTaskInventory 隐患排查任务清单
     * @return 隐患排查任务清单
     */
    @Override
    public List<CheckTaskInventory> selectCheckTaskInventoryList(CheckTaskInventory checkTaskInventory) {
        checkTaskInventory.setIsDel(DeletedEnum.VALID.getValue());
        return checkTaskInventoryMapper.selectCheckTaskInventoryList(checkTaskInventory);
    }

    /**
     * 新增隐患排查任务清单
     *
     * @param checkTaskInventory 隐患排查任务清单
     * @return 结果
     */
    @Override
    public int insertCheckTaskInventory(CheckTaskInventory checkTaskInventory) {
        if (checkTaskInventory.getIsDel() == null)
            checkTaskInventory.setIsDel(DeletedEnum.VALID.getValue());

        if (checkTaskInventory.getCreateDate() == null)
            checkTaskInventory.setCreateDate(new Date());

        if (checkTaskInventory.getUpdateDate() == null)
            checkTaskInventory.setUpdateDate(new Date());

        return checkTaskInventoryMapper.insertCheckTaskInventory(checkTaskInventory);
    }

    @Override
    public int batchInsertCheckTaskInventory(List<CheckTaskInventory> checkTaskInventoryList) {
        return checkTaskInventoryMapper.batchInsertCheckTaskInventory(checkTaskInventoryList);
    }

    /**
     * 生成巡检任务清单：按指定周期生成
     *
     * @param checkTaskId    排查要求ID
     * @param checkCycleUnit 巡查周期单位：小时，天，月，年
     * @param count          频次
     * @return
     */
    @Override
    public List<CheckTaskInventory> createCheckTaskInventoryTime(String checkTaskId, String checkCycleUnit, int count) {
        List<CheckTaskInventory> list = new ArrayList<>();

        //下一天的：00:00:00
        Calendar calendar = Calendar.getInstance();
        calendar.clear();
        calendar.setTime(new Date());
        calendar.set(Calendar.HOUR_OF_DAY, 0); //时
        calendar.set(Calendar.MINUTE, 0); //分
        calendar.set(Calendar.SECOND, 0); //秒
        calendar.set(Calendar.MILLISECOND, 0); //毫秒
        calendar.add(Calendar.DAY_OF_MONTH, 1);
        Date startDateGh = calendar.getTime();

        //查询任务清单表中大于等于明天0点的记录
        CheckTaskInventory param = new CheckTaskInventory();
        param.setCheckTaskId(checkTaskId);
        param.setIsDel(DeletedEnum.VALID.getValue());
        param.setStartDateGt(startDateGh);
        List<CheckTaskInventory> checkTaskInventoryList = selectCheckTaskInventoryList(param);
        if (!checkTaskInventoryList.isEmpty()) {
            log.info("巡检任务明细已生成，无需重复生成");
            return list;
        }

        //巡查周期单位：小时，天，月，年
        if ("小时".equals(checkCycleUnit)) {
            list = hourTask(count);
        }
        //
        else if ("天".equals(checkCycleUnit)) {
            list = dayTask(count);
        }
        //
        else if ("月".equals(checkCycleUnit)) {
            list = monthTask(count);
        }
        //
        else if ("年".equals(checkCycleUnit)) {
            list = yeayTask(count);
        }
        return list;
    }

    /**
     * 每小时指定N次，生成1天内所有的任务
     *
     * @param count 执行的次数
     */
    private List<CheckTaskInventory> hourTask(int count) {
        List<CheckTaskInventory> list = new ArrayList<>();

        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.DAY_OF_MONTH, 1); //增加一天

        //循环0点到23点
        int total = 24;
        for (int i = 0; i < total; i++) {
            //1、得到下一天的开始时间和结束时间 00:00:00.000 23:59:59.999
            Date start, end = null;
            calendar.set(Calendar.HOUR_OF_DAY, i); //时
            calendar.set(Calendar.MINUTE, 0); //分
            calendar.set(Calendar.SECOND, 0); //秒
            calendar.set(Calendar.MILLISECOND, 0); //毫秒
            start = calendar.getTime();

            calendar.set(Calendar.MINUTE, 59); //分
            calendar.set(Calendar.SECOND, 59); //秒
            calendar.set(Calendar.MILLISECOND, 0); //毫秒
            end = calendar.getTime();

            //2、根据频次N，生成时间段的开始时间和结束时间
            list.addAll(splitByTime(start, end, count));

            //3、设置下一小时的开始时间
            if (i < total - 1) {
                calendar.set(Calendar.HOUR_OF_DAY, i + 1); //时
                calendar.set(Calendar.MINUTE, 0); //分
                calendar.set(Calendar.SECOND, 0); //秒
                calendar.set(Calendar.MILLISECOND, 0); //毫秒
            }
        }
        return list;
    }

    /**
     * 每天指定N次，生成1天内所有的任务
     *
     * @param count 执行的次数
     */
    private List<CheckTaskInventory> dayTask(int count) {
        //1、得到下一天的开始时间和结束时间 00:00:00.000 23:59:59.999
        Date start, end = null;
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.DATE, 1); //增加一天
        calendar.set(Calendar.HOUR_OF_DAY, 0); //时
        calendar.set(Calendar.MINUTE, 0); //分
        calendar.set(Calendar.SECOND, 0); //秒
        calendar.set(Calendar.MILLISECOND, 0); //毫秒
        start = calendar.getTime();
        calendar.set(Calendar.HOUR_OF_DAY, 23); //时
        calendar.set(Calendar.MINUTE, 59); //分
        calendar.set(Calendar.SECOND, 59); //秒
        calendar.set(Calendar.MILLISECOND, 0); //毫秒
        end = calendar.getTime();
        //2、根据频次N，生成时间段的开始时间和结束时间
        return splitByTime(start, end, count);
    }

    /**
     * 每月指定N次，生成1个月的任务
     * 3、月：1号      <= 任务1 <= 15号     ；16号     <= 任务2 <= (28/29/30/31)
     *
     * @param count 执行的次数
     */
    private List<CheckTaskInventory> monthTask(int count) {
        //1、得到下一天的开始时间和结束时间 00:00:00.000 23:59:59.999
        Date start, end = null;
        Calendar calendar = Calendar.getInstance();
        calendar.clear();
        calendar.setTime(new Date());
//        calendar.set(Calendar.YEAR,2024);
//        calendar.set(Calendar.MONTH,1);//月份取值范围[0,11]
        int dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH);
        int lastDayOfMonth = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
        //每个月的最后一天才可以生成，其他时段不可以生成
        if (dayOfMonth != lastDayOfMonth) {
            return new ArrayList<>();
        }

//        System.out.println(lastDayOfMonth + "\t" + dayOfMonth);

        //增加一天为下月第一天
        calendar.add(Calendar.DATE, 1); //增加一天
        calendar.set(Calendar.HOUR_OF_DAY, 0); //时
        calendar.set(Calendar.MINUTE, 0); //分
        calendar.set(Calendar.SECOND, 0); //秒
        calendar.set(Calendar.MILLISECOND, 0); //毫秒
        start = calendar.getTime();


        //通过下月第1天取本月最后一天
        calendar.add(Calendar.MONTH, 1); //增加一月
        calendar.set(Calendar.MILLISECOND, -1000); //偏移1毫秒
        end = calendar.getTime();

//        System.out.println(formatTime.format(start) + "\t" + formatTime.format(end));

        //2、根据频次N，生成时间段的开始时间和结束时间
        return splitByTime(start, end, count);
    }

    /**
     * 每年指定N次，生成1年的任务
     *
     * @param count 执行的次数
     */
    private List<CheckTaskInventory> yeayTask(int count) {
        //1、得到下一天的开始时间和结束时间 00:00:00.000 23:59:59.999
        Date start, end = null;
        Calendar calendar = Calendar.getInstance();
        calendar.clear();
        calendar.setTime(new Date());
//        calendar.set(Calendar.YEAR,2024);
//        calendar.set(Calendar.MONTH,1);//月份取值范围[0,11]
        int month = calendar.get(Calendar.MONTH);
        int dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH);
        int lastDayOfMonth = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
        //每年最后一个月的最后一天才可以生成，其他时段不可以生成
        if (month != 11 || dayOfMonth != lastDayOfMonth) {
            return new ArrayList<>();
        }

        //下年第一天
        calendar.add(Calendar.DATE, 1); //增加一天
        calendar.set(Calendar.HOUR_OF_DAY, 0); //时
        calendar.set(Calendar.MINUTE, 0); //分
        calendar.set(Calendar.SECOND, 0); //秒
        calendar.set(Calendar.MILLISECOND, 0); //毫秒
        start = calendar.getTime();
        //下年最后一天
        calendar.set(Calendar.MONTH, 11); //12月
        calendar.set(Calendar.DAY_OF_MONTH, 31); //31日
        calendar.set(Calendar.HOUR_OF_DAY, 23); //时
        calendar.set(Calendar.MINUTE, 59); //分
        calendar.set(Calendar.SECOND, 59); //秒
        calendar.set(Calendar.MILLISECOND, 0); //毫秒
        end = calendar.getTime();
        //2、根据频次N，生成时间段的开始时间和结束时间
        return splitByTime(start, end, count);
    }

    /**
     * 生成任务的开始时间和结束时间
     *
     * @param start 开始时间
     * @param end   结束时间
     */
    private List<CheckTaskInventory> splitByTime(Date start, Date end, int count) {
//        System.out.println(formatTime.format(start) + "\t" + formatTime.format(end));
        List<CheckTaskInventory> list = new ArrayList<>();
        CheckTaskInventory inventory = null;
        System.out.println("频次数量 = " + count);

        //1、计算相差的毫秒数
        long shiJianCha = end.getTime() - start.getTime();
        System.out.println("时间差（毫秒值）" + shiJianCha);

        //2、按频次平分时间
        long timeInterval = shiJianCha / count;
//        System.out.println("时间间隔（毫秒） = " + timeInterval);
//        System.out.println("时间间隔（秒） = " + (timeInterval / 1000));
        System.out.println("时间间隔（分） = " + (timeInterval / 1000 / 60));
//        System.out.println("时间间隔（小时） = " + (timeInterval / 1000 / 60 / 60));

        //3、设置开始时间
        Calendar calendar = Calendar.getInstance();
        calendar.clear();
        calendar.setTime(start);

//        int index = 1;
//        while (calendar.getTime().before(end)) {
        for (int index = 1; index <= count; index++) {
            Date startTime = calendar.getTime();
            //下一段的开始时间
            Date nextMillisecond = getNextMillisecond(calendar, timeInterval);

            //本段的结束时间：结束时间往前偏移1毫秒
            Calendar endCalendar = Calendar.getInstance();
            endCalendar.clear();
            endCalendar.setTime(nextMillisecond);
            Date endTime = getNextMillisecond(endCalendar, -1);
//            System.out.println(index + "\t" + formatTime.format(startTime) + "\t" + formatTime.format(endTime));
            inventory = new CheckTaskInventory();
            inventory.setStartDate(startTime);
            if (index == count) {
                inventory.setEndDate(end);
            } else {
                inventory.setEndDate(endTime);
            }
            list.add(inventory);
            //将第二段的开始的时间设置进去
            if (index < count)
                calendar.setTime(nextMillisecond);
        }
        return list;
    }

    /**
     * 日期偏移 单位：毫秒
     *
     * @param calendar
     * @param unit     需要偏移的单元（毫秒）
     * @return
     */
    private Date getNextMillisecond(Calendar calendar, long unit) {
        Calendar tempCalendar = Calendar.getInstance();
        tempCalendar.setTime(calendar.getTime());

        //处理整数溢出
        int maxValue = Integer.MAX_VALUE;
        //不会溢出直接偏移
        if (unit < maxValue) {
            tempCalendar.add(Calendar.MILLISECOND, Math.toIntExact(unit));
        }
        //会溢出，分次偏移
        else {
//            long page = unit / maxValue;
//            long xiaoShu = unit % maxValue;
//            page = xiaoShu == 0 ? page : page + 1;
//            for (long i = 0; i < page; i++)
            while (unit > 0) {
                if (unit > maxValue) {
                    tempCalendar.add(Calendar.MILLISECOND, maxValue);
                } else {
                    tempCalendar.add(Calendar.MILLISECOND, Math.toIntExact(unit));
                }
                unit = unit - maxValue;
            }

        }
        return tempCalendar.getTime();
    }

    /**
     * 修改隐患排查任务清单
     *
     * @param checkTaskInventory 隐患排查任务清单
     * @return 结果
     */
    @Override
    public int updateCheckTaskInventory(CheckTaskInventory checkTaskInventory) {
        return checkTaskInventoryMapper.updateCheckTaskInventory(checkTaskInventory);
    }

    /**
     * 批量删除隐患排查任务清单
     *
     * @param ids 需要删除的隐患排查任务清单主键
     * @return 结果
     */
    @Override
    public int deleteCheckTaskInventoryByIds(String[] ids) {
        return checkTaskInventoryMapper.deleteCheckTaskInventoryByIds(ids);
    }

    /**
     * 删除隐患排查任务清单信息
     *
     * @param id 隐患排查任务清单主键
     * @return 结果
     */
    @Override
    public int deleteCheckTaskInventoryById(String id) {
        return checkTaskInventoryMapper.deleteCheckTaskInventoryById(id);
    }

    /**
     * 查询用户巡检任务列表（区域纬度）
     *
     * @return
     */
    @Override
    public List<CheckTaskListDTOForApp> getUserCheckTaskListForApp() {
//        List<CheckTaskListDTOForApp> checkTaskListDTOForAppsAll = null;
        String pcUserId = JWTUtil.getPCUserId();
        // 只有内部用户有巡检任务
        if (StringUtils.isBlank(pcUserId)) {
            return new ArrayList<>();
        }
        //1、查询岗位列表
        List<Long> postIdList = sysPostService.selectPostListByUserId(Long.parseLong(pcUserId));
        //没有岗位时返回空任务
        if (postIdList.size() == 0) {
            return new ArrayList<>();
        }

        //2、岗位查询巡检任务(改为从记录表查询，一条SQL将已完成未完成全部统计出来)
        List<CheckTaskListDTOForApp> checkTaskListDTOForAppsAll = checkTaskInventoryRecordMapper.selectCheckTaskInventoryGroupByPostId(postIdList);
        if (checkTaskListDTOForAppsAll == null || checkTaskListDTOForAppsAll.isEmpty()) {
            return new ArrayList<>();
        }

        //3、已巡检任务数量
//        List<CheckTaskListDTOForApp> checkTaskListDTOForAppsDone = checkTaskInventoryMapper.selectCheckTaskInventoryCheckGroupByPostId(postIdList);
//        Map<Long, Long> checkTaskListDTOForAppsDoneMap = checkTaskListDTOForAppsDone.stream()
//                .collect(Collectors.toMap(CheckTaskListDTOForApp::getAreaId, CheckTaskListDTOForApp::getCompletedCheckTaskCount, (k1, k2) -> k1));

        //4、查询区域负责人
        Set<Long> areaIdList = checkTaskListDTOForAppsAll.parallelStream().map(CheckTaskListDTOForApp::getAreaId).distinct().collect(Collectors.toSet());
        List<TbSysAreaUser> sysUsers = tbSysAreaMapper.selectUserNickNameByAreaIdList(new ArrayList<>(areaIdList));
        Map<Long, List<TbSysAreaUser>> areaUserList = sysUsers.stream().collect(Collectors.groupingBy(TbSysAreaUser::getAreaId));

//            List<String> checkTaskIdList = checkTaskResultDTOS.stream().map(CheckTaskResultDTO::getId).collect(Collectors.toList());
//            List<CheckTaskPostDTO> checkTaskPostDTOS = null;
//            Map<String, List<CheckTaskPostDTO>> checkTaskPostMap = new HashMap<>();
//            if(!CollectionUtils.isEmpty(checkTaskPostDTOS)){
//                Map<String, List<CheckTaskPostDTO>> checkTaskPostMap =
//                        checkTaskPostDTOS.stream().collect(Collectors.groupingBy(CheckTaskPostDTO::getCheckTaskId));
//            }
//            for(CheckTaskResultDTO checkTaskResultDTO : checkTaskResultDTOS){
//                checkTaskResultDTO.setCheckTaskPostDTOList(checkTaskPostMap.get(checkTaskResultDTO.getId()));
//            }


        //5、设置已巡检任务数量、设置区域负责人
        for (CheckTaskListDTOForApp data : checkTaskListDTOForAppsAll) {
            Long areaId = data.getAreaId();
//            if (checkTaskListDTOForAppsDoneMap.containsKey(areaId)) {
//                data.setCompletedCheckTaskCount(checkTaskListDTOForAppsDoneMap.get(areaId));
//            } else {
//                data.setCompletedCheckTaskCount(0l);
//            }
            //设置区域负责人集合
            data.setAreaUserList(areaUserList.get(areaId));
        }

        return checkTaskListDTOForAppsAll;
    }

    /**
     * 查询区域下巡检清单列表（分析单元纬度）
     *
     * @param areaId 区域ID
     * @return
     */
    @Override
    public List<CheckTasRiskUnitkListDTOForApp> getUserCheckTaskRiskUnitListForApp(Long areaId) {
        String pcUserId = JWTUtil.getPCUserId();
        // 只有内部用户有巡检任务
        if (StringUtils.isBlank(pcUserId)) {
            return new ArrayList<>();
        }
        //1、查询岗位列表
        List<Long> postIdList = sysPostService.selectPostListByUserId(Long.parseLong(pcUserId));
        //没有岗位时返回空任务
        if (postIdList.size() == 0) {
            return new ArrayList<>();
        }


//        return checkTaskInventoryMapper.selectUserCheckTaskRiskUnitListForApp(areaId);
        return checkTaskInventoryRecordMapper.selectUserCheckTaskRiskUnitListForApp(areaId,postIdList);
    }

    /**
     * 巡检打卡
     *
     * @param areaCode 区域编码
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean register(String areaCode) {

        if (StringUtils.isBlank(areaCode)) {
            throw new BaseException("区域编码为空！");
        }

        TbSysArea tbSysArea = new TbSysArea();
        tbSysArea.setAreaCode(areaCode);
        List<TbSysArea> sysAreaList = tbSysAreaMapper.selectTbSysAreaList(tbSysArea);
        if (CollectionUtils.isEmpty(sysAreaList)) {
            throw new BaseException("请先在区域管理中录入区域编码【" + areaCode + "】！");
        }

        if (sysAreaList.size() != 1) {
            throw new BaseException("多个区域关联此区域编码，请在区域管理中检查！");
        }
        Long areaId = sysAreaList.get(0).getId();


        String pcUserId = JWTUtil.getPCUserId();
        //1、Token中用户ID
        // 只有内部用户有巡检任务
        if (StringUtils.isBlank(pcUserId)) {
            //TODO 提示语APP 2023年8月26日20:35:41
            throw new BaseException("只有内部用户有巡检任务");
        }
        //2、查询岗位列表
        List<Long> postIdList = sysPostService.selectPostListByUserId(Long.parseLong(pcUserId));
        //没有岗位时返回空任务
        if (postIdList.size() == 0)
            return true;

        //2、根据区域ID + 用户岗位（Token中用户ID关联人员岗位关联表）：查询任务清单中当前时间段的待巡检状态的记录
        CheckTaskInventory checkTaskInventory = new CheckTaskInventory();
        checkTaskInventory.setAreaId(areaId);
        checkTaskInventory.setStatus(0l);
        checkTaskInventory.setPostIdList(postIdList);
        checkTaskInventory.setStartDateLt(new Date());
        checkTaskInventory.setEndDateGt(new Date());
        List<CheckTaskInventory> list = checkTaskInventoryMapper.selectCheckTaskInventoryList(checkTaskInventory);
        //3、更新任务清单的状态为已完成
        for (CheckTaskInventory taskInventory : list) {
            //更新状态
            taskInventory.setStatus(1l);
            taskInventory.setCheckUserId(Long.parseLong(JWTUtil.getUserId()));
            taskInventory.setCheckTime(new Date());
            taskInventory.setUpdateDate(new Date());//更新完成时间

            int updateTaskInventoryCount = checkTaskInventoryMapper.updateById(taskInventory);
            CheckTaskInventoryRecord checkTaskInventoryRecord = new CheckTaskInventoryRecord();
            BeanUtils.copyProperties(taskInventory, checkTaskInventoryRecord);
            int updateTaskInventoryCountRecord = checkTaskInventoryRecordMapper.updateById(checkTaskInventoryRecord);
            if (updateTaskInventoryCount == 0 || updateTaskInventoryCountRecord == 0 || updateTaskInventoryCount != updateTaskInventoryCountRecord) {
                //TODO 提示语APP 2023年8月26日20:35:41
                throw new BaseException("系统错误：巡检失败，任务清单和清单明细更新记录不统一！");
            }
        }
        return true;
    }

    /**
     * 查询分析单元下排查任务清单列表
     *
     * @param riskUnitId 所属风险单元UUID
     * @return 隐患排查任务清单集合
     */
    @Override
    public List<CheckTaskInventory> selectCheckTaskInventoryListByRistUnitId(String riskUnitId) {
        return checkTaskInventoryMapper.selectCheckTaskInventoryListByRistUnitId(riskUnitId);
    }
}
