package com.artfess.data.manager.impl;

import com.alibaba.fastjson.JSONObject;
import com.artfess.base.context.BaseContext;
import com.artfess.base.enums.DelStatusEnum;
import com.artfess.base.exception.BaseException;
import com.artfess.base.manager.impl.BaseManagerImpl;
import com.artfess.base.query.*;
import com.artfess.base.util.AuthenticationUtil;
import com.artfess.base.util.DateUtils;
import com.artfess.base.util.DictionaryUtils;
import com.artfess.base.util.StringUtil;
import com.artfess.base.util.time.DateFormatUtil;
import com.artfess.data.dao.BizExamPlanDao;
import com.artfess.data.manager.BizExamPlanManager;
import com.artfess.data.manager.BizExamSpaceDataManager;
import com.artfess.data.manager.BizTrainMaterialManager;
import com.artfess.data.manager.BizUserCheckManager;
import com.artfess.data.manager.BizUserTrainPlanManager;
import com.artfess.data.model.BizExamPlan;
import com.artfess.data.model.BizExamSpaceData;
import com.artfess.data.model.BizTrainMaterial;
import com.artfess.data.model.BizUserCheck;
import com.artfess.data.model.BizUserTrainPlan;
import com.artfess.data.vo.TrainReqVo;
import com.artfess.examine.dao.ExamOrgEvaluationDao;
import com.artfess.examine.dao.ExamUserEvaluationDetailDao;
import com.artfess.examine.manager.ExamEquipmentSysManager;
import com.artfess.examine.manager.ExamNoticeManager;
import com.artfess.examine.manager.ExamSubjectInfoManager;
import com.artfess.examine.model.ExamEquipmentSys;
import com.artfess.examine.model.ExamNotice;
import com.artfess.examine.model.ExamSubjectInfo;
import com.artfess.examine.vo.OrgVo;
import com.artfess.examine.vo.PositionVo;
import com.artfess.uc.api.model.IUser;
import com.artfess.uc.api.service.IUserService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.google.common.collect.Lists;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.stream.Collectors;

/**
 * 年度训练计划数据 服务实现类
 *
 * @author min.wu
 * @company 阿特菲斯信息技术有限公司
 * @since 2024-09-02
 */
@Service
public class BizExamPlanManagerImpl extends BaseManagerImpl<BizExamPlanDao, BizExamPlan> implements BizExamPlanManager {

    @Autowired
    private BizUserTrainPlanManager userTrainPlanManager;

    @Autowired
    private BizTrainMaterialManager materialManager;

    @Autowired
    private BizUserCheckManager userCheckManager;

    @Resource
    private ExamOrgEvaluationDao orgEvaluationDao;

    @Autowired
    private ExamSubjectInfoManager subjectInfoManager;

    @Autowired
    private BizExamSpaceDataManager spaceDataManager;

    @Autowired
    private ExamEquipmentSysManager equipmentSysManager;

    @Resource
    private ExamUserEvaluationDetailDao userEvaluationDetailDao;

    @Resource
    IUserService userServiceImpl;

    @Autowired
    private ExamNoticeManager noticeManager;

    @Autowired
    private BaseContext baseContext;

    @Resource(name = "bmpExecutorService")
    private ExecutorService executorService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean saveInfo(BizExamPlan t) {

        if ("1".equals(t.getPlanType())) {
            if (StringUtil.isEmpty(t.getYear())) {
                t.setYear(String.valueOf(LocalDate.now().getYear()));
            }
        } else if ("2".equals(t.getPlanType())) {
            Assert.hasText(t.getQuarter(), "请填写阶段名称");
        } else if ("3".equals(t.getPlanType())) {
            Assert.notNull(t.getMonth(), "请填写训练月份");
        } else if ("4".equals(t.getPlanType())) {
            Assert.notNull(t.getWeek(), "请填写训练周");
        } else {
            Assert.notNull(t.getStartTime(), "请填写训练开始时间");
            Assert.notNull(t.getEndTime(), "请填写训练结束时间");
        }
        t.setPlanStatus("0");
        t.setRealityNum(0L);
        boolean save = this.save(t);
        if (save) {
            processUserTrainPlans(t);
            int size = t.getUserTrainPlans().size();
            t.setTotalNum(Long.valueOf(size));
            processMaterials(t);
            return save;
        }

        return false;
    }

    private void processMaterials(BizExamPlan t) {
        LambdaQueryWrapper<BizTrainMaterial> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(BizTrainMaterial::getPlanId, t.getId());
        materialManager.remove(lambdaQueryWrapper);
        t.getMaterials().forEach(user -> {
            Assert.hasText(user.getIndexId(), "请选择训练指标");
            user.setPlanId(t.getId());
        });
        materialManager.saveBatch(t.getMaterials());
    }

    private void processUserTrainPlans(BizExamPlan t) {
        LambdaQueryWrapper<BizUserTrainPlan> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(BizUserTrainPlan::getPlanId, t.getId());
        userTrainPlanManager.remove(lambdaQueryWrapper);
        t.getUserTrainPlans().forEach(user -> {
            Assert.hasText(user.getUserId(), "请选择培训考生");
            user.setPlanId(t.getId());
            user.setStatus("0");
        });
        userTrainPlanManager.saveBatch(t.getUserTrainPlans());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateInfo(BizExamPlan t) {
        BizExamPlan bizExamPlan = this.get(t.getId());
        if (!"0".equals(bizExamPlan.getPlanStatus())) {
            throw new BaseException("已下发的计划不能进行修改");
        }
        if ("1".equals(t.getPlanType())) {
            if (StringUtil.isEmpty(t.getYear())) {
                t.setYear(String.valueOf(LocalDate.now().getYear()));
            }
        } else if ("2".equals(t.getPlanType())) {
            Assert.hasText(t.getQuarter(), "请填写阶段名称");
        } else if ("3".equals(t.getPlanType())) {
            Assert.notNull(t.getMonth(), "请填写训练月份");
        } else if ("4".equals(t.getPlanType())) {
            Assert.notNull(t.getWeek(), "请填写训练周");
        } else {
            Assert.notNull(t.getStartTime(), "请填写训练开始时间");
            Assert.notNull(t.getEndTime(), "请填写训练结束时间");
        }
        int size = t.getUserTrainPlans().size();
        t.setTotalNum((long)size);
        boolean save = this.updateById(t);
        if (save) {
            processUserTrainPlans(t);
            processMaterials(t);
            return save;
        }
        return false;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateStatus(String id) {
        BizExamPlan bizExamPlan = this.findById(id);
        Assert.notNull(bizExamPlan, "训练计划不存在");
        if (!"0".equals(bizExamPlan.getPlanStatus())) {
            throw new BaseException("已下发的计划不能再次下发");
        }
        Assert.notEmpty(bizExamPlan.getUserTrainPlans(), "请选择要参训的考生");
        bizExamPlan.setPlanStatus("1");
        boolean update = this.updateById(bizExamPlan);
        if (update) {
            executorService.execute(() -> {
                try {
                    if ("4".equals(bizExamPlan.getPlanType())) {
                        processUserChecks(bizExamPlan);
                    }
                    //计划通知公告
                    sendNotice(bizExamPlan);
                } catch (Exception e) {
                    log.error("生成考勤数据失败:{}", e);
                }
            });


        }
        return;
    }

    private void sendNotice(BizExamPlan bizExamPlan) {
        String planTypeName = null;
        if (null != DictionaryUtils.findByDictValue("jhlx", bizExamPlan.getPlanType())) {
            planTypeName = DictionaryUtils.findByDictValue("jhlx", bizExamPlan.getPlanType()).getName();
        }

        String userName = baseContext.getCurrentUserName();

        StringBuffer sb = new StringBuffer();
        StringBuffer titleSb = new StringBuffer();
        titleSb.append(bizExamPlan.getYear() +"年");
        sb.append(userName + "在" + DateFormatUtil.formaDatetTime(bizExamPlan.getCreateTime()) + "下发了" + bizExamPlan.getYear());
        if ("2".equals(bizExamPlan.getPlanType())) {
            sb.append("第" + bizExamPlan.getQuarter() );
            titleSb.append("第" + bizExamPlan.getQuarter()+"季度");
        } else if ("3".equals(bizExamPlan.getPlanType())) {
            sb.append(bizExamPlan.getMonth());
            titleSb.append(bizExamPlan.getMonth() +"月");
        } else if ("4".equals(bizExamPlan.getPlanType())) {
            sb.append(bizExamPlan.getMonth() + "月" + bizExamPlan.getWeek());
            titleSb.append(bizExamPlan.getMonth() + "月" + bizExamPlan.getWeek());
        }

        sb.append(planTypeName + "训练计划，请涉及训练的相关单位在" + bizExamPlan.getStartTime() + "至" + bizExamPlan.getEndTime() + "训练期间积极参与训练。");
        titleSb.append("训练计划通知");
        ExamNotice examNotice = new ExamNotice();
        examNotice.setTitle(titleSb.toString());
        examNotice.setContent(sb.toString());
        examNotice.setStatus("0");
        examNotice.setEndTime(LocalDateTime.of(bizExamPlan.getEndTime().getYear(), bizExamPlan.getEndTime().getMonth(), bizExamPlan.getEndTime().getDayOfMonth(), 23, 59, 59));
        examNotice.setType("2");
        noticeManager.save(examNotice);

    }

    private void processUserChecks(BizExamPlan bizExamPlan) {
        QueryWrapper<BizUserTrainPlan> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("plan_id_", bizExamPlan.getId());
        List<BizUserTrainPlan> list = userTrainPlanManager.list(queryWrapper);
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        if (null == bizExamPlan.getStartTime() || null == bizExamPlan.getEndTime()) {
            return;
        }
        List<LocalDate> dateList = DateUtils.getDatesBetween(bizExamPlan.getStartTime(), bizExamPlan.getEndTime());
        List<BizUserCheck> userCheckList = Lists.newArrayList();
        list.forEach(user -> {
            dateList.forEach(date -> {
                BizUserCheck userCheck = new BizUserCheck();
                userCheck.setSubjectId(bizExamPlan.getSubjectId());
                userCheck.setSubjectName(bizExamPlan.getSubjectName());
                userCheck.setPlanId(bizExamPlan.getId());
                userCheck.setOrgId(user.getOrgId());
                userCheck.setOrgName(user.getOrgName());
                userCheck.setCheckDate(DateFormatUtil.formatDate(date));
                userCheck.setStatus("0");
                userCheck.setMonth(date.getMonthValue());
                userCheck.setWeek(bizExamPlan.getWeek());
                userCheck.setUserId(user.getUserId());
                userCheck.setUserName(user.getUserName());
                userCheckList.add(userCheck);
            });
        });

        userCheckManager.saveBatch(userCheckList);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addUser(BizExamPlan t) {
        if (CollectionUtils.isEmpty(t.getUserTrainPlans())) {
            throw new BaseException("请添加要新增的考生");
        }

        List<BizUserTrainPlan> records = Lists.newArrayList();
        t.getUserTrainPlans().forEach(vo -> {
            QueryWrapper<BizUserTrainPlan> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("user_id_", vo.getUserId());
            queryWrapper.eq("plan_id_", t.getId());
            List<BizUserTrainPlan> list = userTrainPlanManager.list(queryWrapper);
            if (!CollectionUtils.isEmpty(list)) {
                throw new RuntimeException(vo.getUserName() + "已在培训计划中，不能重复添加");
            }

        });
        userTrainPlanManager.saveBatch(records);
    }

    //计划超时 定时任务修改状态 每天晚上23点执行
    @Override
    @Scheduled(cron = "0 0 23 * * ? ")
    public void updatePlanStatus() {
        LambdaQueryWrapper<BizExamPlan> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        String[] plans = new String[]{"0", "2"};
        lambdaQueryWrapper.notIn(BizExamPlan::getPlanStatus, Arrays.asList(plans));
        lambdaQueryWrapper.ge(BizExamPlan::getEndTime, LocalDate.now());
        lambdaQueryWrapper.eq(BizExamPlan::getIsDele, DelStatusEnum.N.getType());
        List<BizExamPlan> list = this.list(lambdaQueryWrapper);
        list.forEach(plan -> {
            plan.setPlanStatus("3");
        });
        boolean b = this.updateBatchById(list);
    }

    @Override
    public void saveList(List<BizExamPlan> list, String planType) {
        Assert.hasText(planType, "请选择计划类型");
        QueryWrapper<ExamSubjectInfo> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("is_dele_", DelStatusEnum.N.getType());
        List<ExamSubjectInfo> subjectInfos = subjectInfoManager.list(queryWrapper);
        Map<String, ExamSubjectInfo> subjectInfoMap = subjectInfos.stream().collect(Collectors.toMap(item -> item.getName(), item -> item));
        List<OrgVo> orgVos = orgEvaluationDao.orgList();
        Map<String, OrgVo> orgMap = orgVos.stream().collect(Collectors.toMap(item -> item.getName(), item -> item));

        QueryWrapper<ExamEquipmentSys> sysQueryWrapper = new QueryWrapper<>();
        sysQueryWrapper.eq("is_dele_", DelStatusEnum.N.getType());
        List<ExamEquipmentSys> sysList = equipmentSysManager.list(sysQueryWrapper);
        Map<String, ExamEquipmentSys> sysMap = sysList.stream().collect(Collectors.toMap(item -> item.getName(), item -> item));

        QueryWrapper<BizExamSpaceData> spaceDataQueryWrapper = new QueryWrapper<>();
        spaceDataQueryWrapper.eq("is_dele_", DelStatusEnum.N.getType());
        List<BizExamSpaceData> spaceDataList = spaceDataManager.list(spaceDataQueryWrapper);
        Map<String, BizExamSpaceData> spaceDateMap = spaceDataList.stream().collect(Collectors.toMap(item -> item.getName(), item -> item));

        List<PositionVo> positionVoList = userEvaluationDetailDao.getPositionList();
        Map<String, PositionVo> positionMap = positionVoList.stream().collect(Collectors.toMap(item -> item.getPositionName(), item -> item));

        List<IUser> userList = userServiceImpl.getUserListByGroup("role", "jly");
        Map<String, IUser> userMap = userList.stream().collect(Collectors.toMap(item -> item.getFullname(), item -> item));
        list.forEach(t -> {
            t.setPlanType(planType);
            if (null != DictionaryUtils.getDictInfo("xllb", t.getTrainType())) {
                String trainType = DictionaryUtils.getDictInfo("xllb", t.getTrainType()).getValue();
                t.setTrainType(trainType);
            }

            if (null != DictionaryUtils.getDictInfo("xljb", t.getTrainLevel())) {
                String trainLevel = DictionaryUtils.getDictInfo("xljb", t.getTrainLevel()).getValue();
                t.setTrainLevel(trainLevel);
            }

            if (null != DictionaryUtils.getDictInfo("jdmc", t.getQuarter())) {
                String type = DictionaryUtils.getDictInfo("jdmc", t.getQuarter()).getValue();
                t.setQuarter(type);
            }

            if (null != DictionaryUtils.getDictInfo("xlfs", t.getWay())) {
                String way = DictionaryUtils.getDictInfo("xlfs", t.getWay()).getValue();
                t.setWay(way);
            }

            Assert.notNull(t.getStartTime(), "请填写训练开始时间");
            Assert.notNull(t.getEndTime(), "请填写训练结束时间");
            if ("1".equals(planType)) {
                Assert.hasText(t.getYear(), "请填写年份");
            } else if ("2".equals(planType)) {
                Assert.hasText(t.getQuarter(), "请填写阶段名称");
            } else if ("3".equals(planType)) {
                Assert.notNull(t.getMonth(), "请填写训练月份");
            } else if ("4".equals(planType)) {
                Assert.notNull(t.getWeek(), "请填写训练周");
            }

            if (!CollectionUtils.isEmpty(sysMap) && null != sysMap.get(t.getMajorName())) {
                t.setMajorId(sysMap.get(t.getMajorName()).getId());
            }

            if (!CollectionUtils.isEmpty(subjectInfoMap) && null != subjectInfoMap.get(t.getSubjectName())) {
                t.setSubjectId(subjectInfoMap.get(t.getSubjectName()).getId());
            }

            if (!CollectionUtils.isEmpty(orgMap) && null != orgMap.get(t.getOrgName())) {
                t.setOrgId(orgMap.get(t.getOrgName()).getId());
            }

            if (!CollectionUtils.isEmpty(spaceDateMap) && null != spaceDateMap.get(t.getSpaceName())) {
                t.setSpaceId(spaceDateMap.get(t.getSpaceName()).getId());
            }

            if (!CollectionUtils.isEmpty(positionMap) && null != positionMap.get(t.getPost())) {
                t.setPostId(positionMap.get(t.getPost()).getPositionId());
            }

            if (!CollectionUtils.isEmpty(userMap) && null != positionMap.get(t.getCoachName())) {
                t.setCoachId(userMap.get(t.getCoachName()).getUserId());
            }
        });
        this.saveBatch(list);

    }

    @Override
    public List<JSONObject> planStatistical() {
        return this.baseMapper.planStatistical(AuthenticationUtil.getCurrentUserId());
    }

    @Override
    public List<JSONObject> trainPlanStylolitic(TrainReqVo reqVo) {
        return this.baseMapper.trainPlanStylolitic(reqVo);
    }

    @Override
    public List<JSONObject> orgYearTrainPlan(TrainReqVo reqVo) {
        return this.baseMapper.orgYearTrainPlan(reqVo);
    }

    @Override
    public BizExamPlan findById(String id) {

        BizExamPlan bizExamPlan = this.get(id);
        Assert.notNull(bizExamPlan, "训练计划不存在");

        LambdaQueryWrapper<BizUserTrainPlan> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(BizUserTrainPlan::getPlanId, id);
        List<BizUserTrainPlan> bizUserTrainPlans = userTrainPlanManager.list(lambdaQueryWrapper);


        LambdaQueryWrapper<BizTrainMaterial> materialLambdaQueryWrapper = new LambdaQueryWrapper<>();
        materialLambdaQueryWrapper.eq(BizTrainMaterial::getPlanId, id);
        List<BizTrainMaterial> trainMaterials = materialManager.list(materialLambdaQueryWrapper);
        bizExamPlan.setUserTrainPlans(bizUserTrainPlans);
        bizExamPlan.setMaterials(trainMaterials);
        return bizExamPlan;
    }

    @Override
    public PageList<BizExamPlan> myTrainPlan(QueryFilter<BizExamPlan> queryFilter) {
        PageBean pageBean = queryFilter.getPageBean();
        IPage<BizExamPlan> result = baseMapper.myTrainPlan(convert2IPage(pageBean), convert2Wrapper(queryFilter, currentModelClass()));
        return new PageList<BizExamPlan>(result);
    }

    @Override
    public PageList<BizExamPlan> monthPlanStatistical(QueryFilter<BizExamPlan> queryFilter) {
        PageBean pageBean = queryFilter.getPageBean();
        IPage<BizExamPlan> result = baseMapper.monthPlanStatistical(convert2IPage(pageBean), convert2Wrapper(queryFilter, currentModelClass()));
        return new PageList<BizExamPlan>(result);
    }

    @Override
    public PageList<BizExamPlan> findByPage(QueryFilter<BizExamPlan> queryFilter) {
        List<String> currentAndChildOrgIds = baseContext.getCurrentAndChildOrgIds();
        queryFilter.addFilter("org_id_", currentAndChildOrgIds, QueryOP.IN, FieldRelation.OR);
        PageBean pageBean = queryFilter.getPageBean();
        IPage<BizExamPlan> result = baseMapper.findByPage(convert2IPage(pageBean), convert2Wrapper(queryFilter, currentModelClass()));
        return new PageList<BizExamPlan>(result);
    }

    @Override
    @Transactional(readOnly = true)
    public PageList<BizExamPlan> queryInfo(QueryFilter<BizExamPlan> queryFilter) {
        PageList<BizExamPlan> query = this.query(queryFilter);

        // 如果查询结果为空，则直接返回
        if (CollectionUtils.isEmpty(query.getRows())) {
            return query;
        }

        // 提取所有计划的 ID
        List<String> planIds = query.getRows().stream()
                .map(BizExamPlan::getId)
                .collect(Collectors.toList());

        // 获取这些计划的所有用户训练数据
        LambdaQueryWrapper<BizUserTrainPlan> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(BizUserTrainPlan::getPlanId, planIds);

        // 按计划 ID 分组用户训练数据
        Map<String, List<BizUserTrainPlan>> userPlanMap = userTrainPlanManager.list(queryWrapper)
                .stream()
                .collect(Collectors.groupingBy(BizUserTrainPlan::getPlanId));

        // 遍历每个考试计划，并计算 "剩余时长"
        query.getRows().forEach(e -> {
            if (userPlanMap.containsKey(e.getId())) {
                int userNum = userPlanMap.get(e.getId()).size();
                // 获取该计划的训练总时长
                BigDecimal hour = e.getHour();

                // 计算所有用户的训练总时长，确保每个用户的训练时长不超过计划的最大时长
                BigDecimal userHour = Optional.ofNullable(userPlanMap.get(e.getId()))
                        .orElse(Collections.emptyList())  // 如果为空，返回空列表
                        .stream()
                        .map(plan -> {
                            // 如果已训练时长大于计划时长，使用计划时长
                            BigDecimal planHour = plan.getHour();
                            return (planHour != null && planHour.compareTo(hour) > 0) ? hour : planHour;
                        })
                        .filter(Objects::nonNull)
                        .reduce(BigDecimal.ZERO, BigDecimal::add);  // 求和

                // 计算剩余时长，结果保留两位小数
                BigDecimal remainingDuration = userHour.divide(BigDecimal.valueOf(userNum), 2, RoundingMode.HALF_UP);
                e.setRemainingDuration(remainingDuration);
            }
        });

        return query;
    }
}
