package org.dromara.elevator.service.impl;

import cn.hutool.core.util.ObjectUtil;
import org.apache.commons.collections4.CollectionUtils;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.DateUtils;
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.StateConstant;
import org.dromara.elevator.constant.WorkType;
import org.dromara.elevator.domain.*;
import org.dromara.elevator.domain.vo.EMaintenanceStateStatisticsVo;
import org.dromara.elevator.domain.vo.WorkStateStatisticsVo;
import org.dromara.elevator.mapper.EElevatorInfoMapper;
import org.dromara.elevator.mapper.ERelevancyCodeMapper;
import org.dromara.elevator.mapper.EWorkDataMapper;
import org.dromara.system.domain.SysDept;
import org.dromara.system.mapper.SysDeptMapper;
import org.springframework.stereotype.Service;
import org.dromara.elevator.domain.bo.EMaintenancePlanBo;
import org.dromara.elevator.domain.vo.EMaintenancePlanVo;
import org.dromara.elevator.mapper.EMaintenancePlanMapper;
import org.dromara.elevator.service.IEMaintenancePlanService;
import org.springframework.transaction.annotation.Transactional;

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;

/**
 * 维保计划;Service业务层处理
 *
 * @author zy
 * @date 2024-09-03
 */
@RequiredArgsConstructor
@Service
public class EMaintenancePlanServiceImpl implements IEMaintenancePlanService {

    private final EMaintenancePlanMapper baseMapper;
    private final EElevatorInfoMapper elevatorInfoMapper;
    private final ERelevancyCodeMapper relevancyCodeMapper;
    private final EWorkDataMapper workDataMapper;
    private final SysDeptMapper deptMapper;

    /**
     * 查询维保计划;
     *
     * @param id 主键
     * @return 维保计划;
     */
    @Override
    public EMaintenancePlanVo queryById(Long id){
        return baseMapper.selectVoById(id);
    }

    /**
     * 分页查询维保计划;列表
     *
     * @param bo        查询条件
     * @param pageQuery 分页参数
     * @return 维保计划;分页列表
     */
    @Override
    public TableDataInfo<EMaintenancePlanVo> queryPageList(EMaintenancePlanBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<EMaintenancePlan> lqw = buildQueryWrapper(bo);
        Page<EMaintenancePlanVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
    }

    /**
     * 查询符合条件的维保计划;列表
     *
     * @param bo 查询条件
     * @return 维保计划;列表
     */
    @Override
    public List<EMaintenancePlanVo> queryList(EMaintenancePlanBo bo) {
        LambdaQueryWrapper<EMaintenancePlan> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }

    private LambdaQueryWrapper<EMaintenancePlan> buildQueryWrapper(EMaintenancePlanBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<EMaintenancePlan> lqw = Wrappers.lambdaQuery();
        lqw.eq(StringUtils.isNotBlank(bo.getElevatorNum()), EMaintenancePlan::getElevatorNum, bo.getElevatorNum());
        lqw.eq(bo.getPlanTime() != null, EMaintenancePlan::getPlanTime, bo.getPlanTime());
        lqw.eq(bo.getPlanType() != null, EMaintenancePlan::getPlanType, bo.getPlanType());
        lqw.eq(bo.getState() != null, EMaintenancePlan::getState, bo.getState());
        lqw.eq(bo.getFinishState() != null, EMaintenancePlan::getFinishState, bo.getFinishState());

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

    /**
     * 新增维保计划;
     *
     * @param bo 维保计划;
     * @return 是否新增成功
     */
    @Override
    public Boolean insertByBo(EMaintenancePlanBo bo) {
        EMaintenancePlan add = MapstructUtils.convert(bo, EMaintenancePlan.class);
        validEntityBeforeSave(add);
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setId(add.getId());
        }
        return flag;
    }

    /**
     * 修改维保计划;
     *
     * @param bo 维保计划;
     * @return 是否修改成功
     */
    @Override
    public Boolean updateByBo(EMaintenancePlanBo bo) {
        EMaintenancePlan update = MapstructUtils.convert(bo, EMaintenancePlan.class);
        validEntityBeforeSave(update);
        return baseMapper.updateById(update) > 0;
    }

    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(EMaintenancePlan entity){
        //TODO 做一些数据校验,如唯一约束
    }

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

    /**
     * 生成维保计划
     * @param bo
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean createMaintenancePlan(EMaintenancePlanBo bo) {
        // 获取电梯信息
        EElevatorInfo elevatorInfo = elevatorInfoMapper.selectOne(Wrappers.<EElevatorInfo>lambdaQuery()
            .eq(EElevatorInfo::getElevatorNum, bo.getElevatorNum()));
        if (Objects.isNull(elevatorInfo)) {
            throw new ServiceException("未查询到该电梯信息!");
        }
        // 定期
        if (StateConstant.FIXED.equals(bo.getPlanType())) {
            // 获取维保计划集合
            Map<String, Object> map = getMaintenancePlan(bo, elevatorInfo);
            workDataMapper.insertBatch((Collection<EWorkData>) map.get("workList"));
            return baseMapper.insertBatch((Collection<EMaintenancePlan>) map.get("allPlan"));

        } else {
            // 按需 维保计划
            EMaintenancePlan maintenance = new EMaintenancePlan();
            maintenance.setElevatorNum(bo.getElevatorNum());
            maintenance.setPlanTime(bo.getPlanTime());
            maintenance.setPlanType(StateConstant.AS_NEED);
            maintenance.setState(StateConstant.NOT_REMIND);
            maintenance.setDeptId(elevatorInfo.getDeptId());

            // 工作数据
            EWorkData workData = new EWorkData();
            workData.setWorkType(WorkType.MAINTENANCE.getType());
            workData.setExpectTime(bo.getPlanTime());
            workData.setElevatorName(elevatorInfo.getElevatorName());
            workData.setElevatorNum(elevatorInfo.getElevatorNum());
            workData.setElevatorAddress(elevatorInfo.getElevatorAddress());
            workData.setRegisterCode(elevatorInfo.getRegisterCode());
            workData.setDeptId(elevatorInfo.getDeptId());
            workData.setState(1);
            workData.setDescription("自动生成");
            workData.setCreatedTime(new Date());
            // 获取二维码信息
            LambdaQueryWrapper<ERelevancyCode> query = new LambdaQueryWrapper<>();
            query.eq(ERelevancyCode::getElevatorNum, elevatorInfo.getElevatorNum());
            ERelevancyCode code = relevancyCodeMapper.selectOne(query);
            workData.setQrCode(code.getQrCode());
            workDataMapper.insert(workData);

            return baseMapper.insert(maintenance) > 0;
        }
    }

    /**
     * 获取维保计划集合
     * @param bo
     * @return
     */
    private Map<String, Object> getMaintenancePlan(EMaintenancePlanBo bo, EElevatorInfo elevatorInfo) {
        Map<String, Object> map = new HashMap<>();
        List<EMaintenancePlan> allPlan = new ArrayList<>();
        List<EWorkData> workList = new ArrayList<>();

        LocalDate annualDate = bo.getPlanTime().plusYears(1);
        // 年度
        EMaintenancePlan annual = new EMaintenancePlan();
        annual.setState(StateConstant.NOT_REMIND);
        annual.setPlanType(StateConstant.YEAR);
        annual.setElevatorNum(bo.getElevatorNum());
        annual.setPlanTime(annualDate);
        annual.setDeptId(elevatorInfo.getDeptId());
        allPlan.add(annual);

        buildWorkData(workList, elevatorInfo, annualDate, StateConstant.YEAR);

        // 半年
        List<LocalDate> halfYearlyDates = new ArrayList<>();
        // 从开始日期到结束日期每六个月添加日期
        LocalDate currentDate = bo.getPlanTime();
        while (!currentDate.isAfter(annualDate)) {
            if (currentDate != bo.getPlanTime()) {
                halfYearlyDates.add(currentDate);
            }
            currentDate = currentDate.plusMonths(6);
        }
        for (LocalDate halfYear:halfYearlyDates) {
            EMaintenancePlan half = new EMaintenancePlan();
            half.setState(StateConstant.NOT_REMIND);
            half.setPlanType(StateConstant.HALF_YEAR);
            half.setElevatorNum(bo.getElevatorNum());
            half.setPlanTime(halfYear);
            half.setDeptId(elevatorInfo.getDeptId());
            allPlan.add(half);

            buildWorkData(workList, elevatorInfo, halfYear, StateConstant.HALF_YEAR);
        }

        // 季度
        List<LocalDate> quarterlyDates = new ArrayList<>();
        // 从开始日期到结束日期每三个月添加日期
        LocalDate currentDateQuarter = bo.getPlanTime();
        while (!currentDateQuarter.isAfter(annualDate)) {
            if (currentDateQuarter != bo.getPlanTime()) {
                quarterlyDates.add(currentDateQuarter);
            }
            currentDateQuarter = currentDateQuarter.plusMonths(3);
        }
        for (LocalDate quarterly:quarterlyDates) {
            EMaintenancePlan quarter = new EMaintenancePlan();
            quarter.setState(StateConstant.NOT_REMIND);
            quarter.setPlanType(StateConstant.QUARTER);
            quarter.setElevatorNum(bo.getElevatorNum());
            quarter.setPlanTime(quarterly);
            quarter.setDeptId(elevatorInfo.getDeptId());
            allPlan.add(quarter);

            buildWorkData(workList, elevatorInfo, quarterly, StateConstant.QUARTER);
        }

        // 半月
        List<LocalDate> monthDates = new ArrayList<>();
        // 从开始日期到结束日期每半个月添加日期
        LocalDate currentDatemonth = bo.getPlanTime();
        while (!currentDatemonth.isAfter(annualDate)) {
            monthDates.add(currentDatemonth);
            currentDatemonth = currentDatemonth.plusDays(15);
        }
        for (LocalDate month:monthDates) {
            EMaintenancePlan mon = new EMaintenancePlan();
            mon.setState(StateConstant.NOT_REMIND);
            mon.setPlanType(StateConstant.HALF_MONTH);
            mon.setElevatorNum(bo.getElevatorNum());
            mon.setPlanTime(month);
            mon.setDeptId(elevatorInfo.getDeptId());
            allPlan.add(mon);

            buildWorkData(workList, elevatorInfo, month, StateConstant.HALF_MONTH);
        }

        map.put("allPlan", allPlan);
        map.put("workList", workList);
        return map;
    }

    /**
     * 构建工作数据
     * @param workList
     * @param elevatorInfo
     * @param annualDate
     */
    private void buildWorkData(List<EWorkData> workList, EElevatorInfo elevatorInfo, LocalDate annualDate, Integer planType) {
        EWorkData workData = new EWorkData();
        workData.setWorkType(WorkType.MAINTENANCE.getType());
        workData.setExpectTime(annualDate);
        workData.setElevatorName(elevatorInfo.getElevatorName());
        workData.setElevatorNum(elevatorInfo.getElevatorNum());
        workData.setElevatorAddress(elevatorInfo.getElevatorAddress());
        workData.setRegisterCode(elevatorInfo.getRegisterCode());
        workData.setDeptId(elevatorInfo.getDeptId());
        workData.setPlanType(planType);
        workData.setState(1);
        workData.setDescription("自动生成");
        workData.setCreatedTime(new Date());
        // 获取二维码信息
        LambdaQueryWrapper<ERelevancyCode> query = new LambdaQueryWrapper<>();
        query.eq(ERelevancyCode::getElevatorNum, elevatorInfo.getElevatorNum());
        ERelevancyCode code = relevancyCodeMapper.selectOne(query);
        workData.setQrCode(code.getQrCode());
        workList.add(workData);
    }

    @Override
    public List<EMaintenanceStateStatisticsVo> statisticsList(EMaintenancePlanBo bo) {
        bo.setFinishState(StateConstant.NOT_FINISH);
        LambdaQueryWrapper<EMaintenancePlan> lqw = buildQueryWrapper(bo);
        List<EMaintenancePlanVo> maintenancePlanList = baseMapper.selectVoList(lqw);
        Map<Integer, EMaintenanceStateStatisticsVo> statisticsMap = initStatisticsList();
        if (CollectionUtils.isNotEmpty(maintenancePlanList)) {
            LocalDate now = LocalDate.now();
            for (EMaintenancePlanVo maintenancePlan : maintenancePlanList) {
                Integer index = compareOverdueTime(now, maintenancePlan.getPlanTime());
                EMaintenanceStateStatisticsVo statistics = statisticsMap.get(index);
                if (!statistics.getElevatorNumList().contains(maintenancePlan.getElevatorNum())) {
                    statistics.setElevatorTotal(statistics.getElevatorTotal() + 1);
                    statistics.getElevatorNumList().add(maintenancePlan.getElevatorNum());
                }
            }
        }
        List<EMaintenanceStateStatisticsVo> res = new ArrayList<>(statisticsMap.values());
        res.sort(Comparator.comparing(EMaintenanceStateStatisticsVo::getMaintenanceStatus));
        return res;
    }

    @Override
    public List<EMaintenancePlan> statisticsMaintenancePlanList(EMaintenancePlanBo bo) {
        LocalDate start = DateUtils.toLocalDate(bo.getStartTime());
        LocalDate end = DateUtils.toLocalDate(bo.getEndTime());
        return  this.baseMapper.selectList(Wrappers.<EMaintenancePlan>lambdaQuery()
            .in(CollectionUtils.isNotEmpty(bo.getElevatorNumList()), EMaintenancePlan::getElevatorNum, bo.getElevatorNumList())
            .between(EMaintenancePlan::getPlanTime, start, end));
    }

    @Override
    public List<WorkStateStatisticsVo> completeStatisticsList(EMaintenancePlanBo bo) {
        if (Objects.isNull(bo)) {
            bo = new EMaintenancePlanBo();
        }
        List<WorkStateStatisticsVo> result = new ArrayList<>();
        WorkStateStatisticsVo unCompleteStatistics = new WorkStateStatisticsVo();
        bo.setFinishState(0);
        unCompleteStatistics.setStatisticsType(0);
        unCompleteStatistics.setTotal(this.baseMapper.selectCount(this.buildQueryWrapper(bo)).intValue());
        result.add(unCompleteStatistics);
        WorkStateStatisticsVo completeStatistics = new WorkStateStatisticsVo();
        bo.setFinishState(1);
        completeStatistics.setStatisticsType(1);
        completeStatistics.setTotal(this.baseMapper.selectCount(this.buildQueryWrapper(bo)).intValue());
        result.add(completeStatistics);
        return result;
    }

    /**
     * 比较超期时间
     *
     * @param now      当前时间
     * @param planTime 计划时间
     * @return 状态
     */
    private Integer compareOverdueTime(LocalDate now, LocalDate planTime) {
        if (planTime.isBefore(now)) {
            // 计划时间在今天之前  已超期
            long overdueTime = ChronoUnit.MONTHS.between(planTime, now);
            if (overdueTime < 1L) {
                return StateConstant.LESS_MONTH;
            }
            if (overdueTime < 3L) {
                return StateConstant.ONE_THREE_MONTH;
            }
            if (overdueTime < 6L) {
                return StateConstant.THREE_SIX_MONTH;
            }
            if (overdueTime < 12L) {
                return StateConstant.SIX_TWELVE_MONTH;
            }
            return StateConstant.OVER_TWELVE_NORMAL;
        }
        return  StateConstant.NORMAL;
    }

    /**
     * 初始化维保状态统计
     *
     * @return 维保状态统计
     */
    private Map<Integer, EMaintenanceStateStatisticsVo> initStatisticsList() {
        return IntStream.range(0, 7).mapToObj(index -> {
            EMaintenanceStateStatisticsVo statistics = new EMaintenanceStateStatisticsVo();
            statistics.setElevatorNumList(new HashSet<>(8));
            statistics.setMaintenanceStatus(index);
            statistics.setElevatorTotal(0);
            return statistics;
        }).collect(Collectors.toMap(EMaintenanceStateStatisticsVo::getMaintenanceStatus, Function.identity()));
    }
}
