package com.aucma.dms.service.impl;

import com.aucma.common.core.domain.model.LoginUser;
import com.aucma.common.utils.DateUtils;
import com.aucma.common.utils.SecurityUtils;
import com.aucma.common.utils.uuid.Seq;
import com.aucma.dms.domain.DmsBillsInspectInstance;
import com.aucma.dms.domain.DmsPlanInspect;
import com.aucma.dms.mapper.DmsBaseInspectRouteMapper;
import com.aucma.dms.mapper.DmsPlanInspectMapper;
import com.aucma.dms.service.IDmsBillsInspectInstanceService;
import com.aucma.dms.service.IDmsPlanInspectService;
import com.aucma.quartz.util.CronUtils;
import org.quartz.CronExpression;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.text.ParseException;
import java.util.Date;
import java.util.List;

/**
 * 巡检计划信息Service业务层处理
 *
 * @author Open Platform
 * @date 2024-01-17
 */
@Service
public class DmsPlanInspectServiceImpl implements IDmsPlanInspectService
{
    private static final Logger log = LoggerFactory.getLogger(DmsPlanInspectServiceImpl.class);
    
    @Autowired
    private DmsPlanInspectMapper dmsPlanInspectMapper;
/*    @Autowired
    private RemoteJobService remoteJobService;*/
    @Autowired
    private DmsBaseInspectRouteMapper dmsBaseInspectRouteMapper;

    @Autowired
    private IDmsBillsInspectInstanceService dmsBillsInspectInstanceService;
    @Autowired
    private DmsPlanInspectTxService dmsPlanInspectTxService;

    /**
     * 查询巡检计划信息
     *
     * @param planInspectId 巡检计划信息主键
     * @return 巡检计划信息
     */
    @Override
    public DmsPlanInspect selectDmsPlanInspectByPlanInspectId(Long planInspectId)
    {
        DmsPlanInspect dmsPlanInspect = dmsPlanInspectMapper.selectPlanInspectJoinJobByInspectId(planInspectId);
        Long timeLimit = dmsPlanInspect.getTimeLimit();
        if (timeLimit != null) {
            Long days = timeLimit / (24 * 60 * 60l);
            Long hours = timeLimit % (24 * 60 * 60l);
//            System.out.println(days);
//            System.out.println(hours);
            dmsPlanInspect.setTimeLimitDays(days);
            dmsPlanInspect.setTimeLimitHours(hours / (60 * 60l));
        }
        return dmsPlanInspect;
    }
    @Override
    public DmsPlanInspect selectDmsPlanInspectByPlanInspectId1(String planInspectId)
    {
        DmsPlanInspect dmsPlanInspect = dmsPlanInspectMapper.selectPlanInspectJoinJobByInspectId1(planInspectId);
        Long timeLimit = dmsPlanInspect.getTimeLimit();
        if (timeLimit != null) {
            Long days = timeLimit / (24 * 60 * 60l);
            Long hours = timeLimit % (24 * 60 * 60l);
//            System.out.println(days);
//            System.out.println(hours);
            dmsPlanInspect.setTimeLimitDays(days);
            dmsPlanInspect.setTimeLimitHours(hours / (60 * 60l));
        }
        return dmsPlanInspect;
    }

    /**
     * 查询巡检计划信息列表
     *
     * @param dmsPlanInspect 巡检计划信息
     * @return 巡检计划信息
     */
    @Override
    public List<DmsPlanInspect> selectDmsPlanInspectList(DmsPlanInspect dmsPlanInspect)
    {
        List<DmsPlanInspect> dmsPlanInspects = dmsPlanInspectMapper.selectDmsPlanInspectList(dmsPlanInspect);
        for (DmsPlanInspect planInspect : dmsPlanInspects) {
            // 只有当inspectRouteId不为空时才查询线路名称
            if (planInspect.getInspectRouteId() != null) {
                String routeName = dmsBaseInspectRouteMapper.selectRouteName(planInspect.getInspectRouteId());
                planInspect.setRouteName(routeName);
            }
        }


        for (DmsPlanInspect d : dmsPlanInspects) {
            Long timeLimit = d.getTimeLimit();
            d.setTimeLimitDays(timeLimit / (24 * 60 * 60L));
            d.setTimeLimitHours((timeLimit % (24 * 60 * 60L)) / (60 * 60l));
        }
        return dmsPlanInspects;
    }

    /**
     * 新增巡检计划信息
     *
     * @param dmsPlanInspect 巡检计划信息
     * @return 结果
     */
    @Override
    public int insertDmsPlanInspect(DmsPlanInspect dmsPlanInspect)
    {
        String day = "*";
        String month = "*";
        String week = "*";
        String year = "*";
        String cron = "";
        if (dmsPlanInspect.getDay()!=null){
            day = dmsPlanInspect.getDay().toString();
        }
        if (dmsPlanInspect.getMonth()!=null){
            month = dmsPlanInspect.getMonth().toString();
        }
        if (dmsPlanInspect.getWeek()!=null){
            Long weekDay = dmsPlanInspect.getWeek() + 1;
            week = weekDay.toString();
        }
        if (dmsPlanInspect.getYear()!=null){
            year = dmsPlanInspect.getYear().toString();
        }
        if (dmsPlanInspect.getInspectLevel().equals(1l)){
            cron = "0 0 "+dmsPlanInspect.getHour()+" "+day+" "+month+" ?";
        }
        if (dmsPlanInspect.getInspectLevel().equals(2l)){
            cron = "0 0 "+dmsPlanInspect.getHour()+" ?"+" "+month+" "+week;
        }
        if (dmsPlanInspect.getInspectLevel().equals(3l)){
            cron = "0 0 "+dmsPlanInspect.getHour()+" "+day+" "+ month+" ? "+year;
        }
        String planInspectCode = Seq.getId(Seq.planInspectSeqType, Seq.planInspectCode);
/*        SysJob job = new SysJob();
        job.setJobName("点巡检计划编号" + planInspectCode + "工单任务");
        job.setJobGroup("DEFAULT");
        job.setInvokeTarget("ryTask.getDmsBillsInstance(\"" + planInspectCode + "\")");
        job.setCronExpression(cron);
        job.setMisfirePolicy("1");
        job.setConcurrent("1");
        job.setStatus("1");
        R<Integer> jobIdR = remoteJobService.add(SecurityConstants.INNER, job);*/

        String time = cron;
        //通过cron表达式获取下一次执行时间
        Date nextExecution = CronUtils.getNextExecution(time);
        dmsPlanInspect.setPlanTime(nextExecution);
        dmsPlanInspect.setCronExpression(cron);
        dmsPlanInspect.setPlanInspectCode(planInspectCode);
//        dmsPlanInspect.setJobId(jobIdR.getData().longValue());

        Long timeLimitDays = dmsPlanInspect.getTimeLimitDays()==null?0L:dmsPlanInspect.getTimeLimitDays();
        Long timeLimitHours = dmsPlanInspect.getTimeLimitHours() == null?0L:dmsPlanInspect.getTimeLimitHours();
        dmsPlanInspect.setTimeLimit((timeLimitDays * 24 * 60 * 60)
                + (timeLimitHours * 60 * 60));
        dmsPlanInspect.setIsFlag("1");
        dmsPlanInspect.setCreateBy(SecurityUtils.getLoginUser()==null ? SecurityUtils.getUserId() :SecurityUtils.getLoginUser().getUser().getUserId());
        dmsPlanInspect.setCreateTime(DateUtils.getNowDate());
        //获取设备总数
        //Long aLong = dmsBaseInspectRouteMapper.selectAmountByInspectRouteId(dmsPlanInspect.getInspectRouteId());
        //dmsPlanInspect.setDeviceAmount(aLong);
        return dmsPlanInspectMapper.insertDmsPlanInspect(dmsPlanInspect);


    }

    /**
     * 修改巡检计划信息
     *
     * @param dmsPlanInspect 巡检计划信息
     * @return 结果
     */
    @Override
    public int updateDmsPlanInspect(DmsPlanInspect dmsPlanInspect)
    {
        // 根据巡检级别和周期字段重新构建cron表达式
        String day = "*";
        String month = "*";
        String week = "*";
        String year = "*";
        String cron = "";
        if (dmsPlanInspect.getDay()!=null){
            day = dmsPlanInspect.getDay().toString();
        }
        if (dmsPlanInspect.getMonth()!=null){
            month = dmsPlanInspect.getMonth().toString();
        }
        if (dmsPlanInspect.getWeek()!=null){
            Long weekDay = dmsPlanInspect.getWeek() + 1;
            week = weekDay.toString();
        }
        if (dmsPlanInspect.getYear()!=null){
            year = dmsPlanInspect.getYear().toString();
        }
        if (dmsPlanInspect.getInspectLevel().equals(1l)){
            cron = "0 0 "+dmsPlanInspect.getHour()+" "+day+" "+month+" ?";
        }
        if (dmsPlanInspect.getInspectLevel().equals(2l)){
            cron = "0 0 "+dmsPlanInspect.getHour()+" ?"+" "+month+" "+week;
        }
        if (dmsPlanInspect.getInspectLevel().equals(3l)){
            cron = "0 0 "+dmsPlanInspect.getHour()+" "+day+" "+ month+" ? "+year;
        }

        String time = cron;
        //通过cron表达式获取下一次执行时间
        Date nextExecution = CronUtils.getNextExecution(time);
        dmsPlanInspect.setPlanTime(nextExecution);
        dmsPlanInspect.setCronExpression(cron);

        Long timeLimitDays = dmsPlanInspect.getTimeLimitDays()==null?0L:dmsPlanInspect.getTimeLimitDays();
        Long timeLimitHours = dmsPlanInspect.getTimeLimitHours() == null?0L:dmsPlanInspect.getTimeLimitHours();
        dmsPlanInspect.setTimeLimit((timeLimitDays * 24 * 60 * 60)
                + (timeLimitHours * 60 * 60));

        LoginUser user = SecurityUtils.getLoginUser();
        dmsPlanInspect.setUpdateBy(user.getUserId());
        dmsPlanInspect.setUpdateTime(DateUtils.getNowDate());
        return dmsPlanInspectMapper.updateDmsPlanInspect(dmsPlanInspect);
    }

    /**
     * 批量删除巡检计划信息
     *
     * @param planInspectIds 需要删除的巡检计划信息主键
     * @return 结果
     */
    @Override
    public int deleteDmsPlanInspectByPlanInspectIds(Long[] planInspectIds)
    {
        return dmsPlanInspectMapper.deleteDmsPlanInspectByPlanInspectIds(planInspectIds);
    }

    /**
     * 删除巡检计划信息信息
     *
     * @param planInspectId 巡检计划信息主键
     * @return 结果
     */
    @Override
    public int deleteDmsPlanInspectByPlanInspectId(Long planInspectId)
    {
        return dmsPlanInspectMapper.deleteDmsPlanInspectByPlanInspectId(planInspectId);
    }

    @Override
    public DmsPlanInspect selectPlanInspectJoinJobByInspectId(Long planInspectId) {

        return dmsPlanInspectMapper.selectPlanInspectJoinJobByInspectId(planInspectId);
    }

    /**
     * 定时任务：每天凌晨1点一次性生成当天整天的巡检工单
     * 使用Mapper替代JdbcTemplate，生成已完成状态工单，工单中所有设备默认正常
     * 若有异常可在网页端维护工单明细中的设备状态
     * cron表达式：秒 分 时 日 月 周
     */
     @Scheduled(cron = "0 0 1 * * ?") // 生产策略：每天凌晨 1 点执行
//    @Scheduled(cron = "0 * * * * ?") // 测试策略：每分钟执行一次
    // @Transactional(rollbackFor = Exception.class)
    public void generateDailyInspectWorkOrders() {
        log.info("========== 开始执行巡检工单生成任务 ==========");
        int count = 0;
        int skipCount = 0;
        try {
            // 先把“今天整天内应生成”的计划全部拉出来，后续逐条处理。
            // 这里依赖SQL使用“小于次日0点”的上界，避免把次日00:00计划提前捞出来。
            // 注意：这里不做事务包裹，避免某一条失败影响整批调度。
            List<DmsPlanInspect> plans = dmsPlanInspectMapper.selectPendingInspectPlans();

            for (DmsPlanInspect plan : plans) {
                try {
                    // 先提取关键字段，便于后续日志统一打印。
                    Long planInspectId = plan.getPlanInspectId();
                    String planInspectCode = plan.getPlanInspectCode();
                    Long inspectRouteId = plan.getInspectRouteId();

                    // 计划编码为空通常属于脏数据，直接跳过，避免生成不可追踪工单。
                    if (planInspectCode == null || planInspectCode.isEmpty()) {
                        log.warn("巡检计划ID[{}]编号为空，跳过", planInspectId);
                        skipCount++;
                        continue;
                    }

                    // 预生成业务单号（仅用于业务展示）。
                    // 真正“是否允许生成”在 TxA 内部通过加锁 + 查重 + 唯一约束做最终判定。
                    String billsCode = Seq.getId(Seq.dmsBillsFaultInstanceSeqType, "XJ");
                    DmsBillsInspectInstance instance = new DmsBillsInspectInstance();
                    instance.setBillsInspectCode(billsCode);
                    instance.setPlanInspectId(planInspectId);
                    instance.setInspectRouteId(inspectRouteId);
                    instance.setInspectType(plan.getInspectType());
                    instance.setDeviceAmount(plan.getDeviceAmount());
                    instance.setPerformer(plan.getPerformer());
                    // 创建人优先继承计划创建人；兜底 -1 表示系统自动生成。
                    instance.setCreateBy(plan.getCreateBy() != null ? plan.getCreateBy() : -1L);

                    // TxA：生成工单（REQUIRES_NEW）。
                    // 返回 0 代表被并发任务抢先生成或命中唯一约束，视为“正常跳过”。
                    int insertRows = dmsBillsInspectInstanceService.insertCompletedInspectInstance(instance);
                    if (insertRows > 0) {
                        count++;
                        log.info("已为巡检计划[{}]生成已完成工单[{}]", planInspectCode, billsCode);
                        // TxB：推进下次执行时间（单独新事务）。
                        // 即便 TxB 失败，也不回滚已成功提交的工单（避免“误回滚”）。
                        updateInspectPlanNextTime(plan);
                    } else {
                        skipCount++;
                        log.debug("巡检计划[{}]已被其他任务生成或不满足生成条件，跳过", planInspectCode);
                    }

                } catch (Exception e) {
                    // 单条计划异常只记录，不中断整批调度任务。
                    log.error("为巡检计划生成工单失败 | planInspectId={}, planInspectCode={}, cronExpression={}, planTime={}, inspectType={}, inspectRouteId={}, 异常信息: {}",
                            plan.getPlanInspectId(), plan.getPlanInspectCode(), plan.getCronExpression(),
                            plan.getPlanTime(), plan.getInspectType(), plan.getInspectRouteId(),
                            e.getMessage(), e);
                }
            }
        } catch (Exception e) {
            log.error("查询待执行巡检计划列表失败(selectPendingInspectPlans), 异常信息: {}", e.getMessage(), e);
        }
        log.info("========== 巡检工单生成任务完成，生成数量: {}，跳过数量: {} ==========", count, skipCount);
    }

    /**
     * 更新巡检计划的下次执行时间
     * 优先使用cron表达式计算，无cron则默认+1天
     */
    private void updateInspectPlanNextTime(DmsPlanInspect plan) {
        try {
            Date nextTime = null;
            String cronExpression = plan.getCronExpression();
            if (cronExpression != null && !cronExpression.isEmpty()) {
                // 基于“计划当前时间”推进，而不是基于“当前系统时间”直接算一次，
                // 可以避免周/月计划在某些场景下被推进到错误时间点。
                nextTime = calculateNextPlanTime(cronExpression, plan.getPlanTime());
            }
            if (nextTime == null) {
                log.warn("巡检计划下次执行时间计算为null, 使用默认+1天 | planInspectId={}, planInspectCode={}, cronExpression={}",
                        plan.getPlanInspectId(), plan.getPlanInspectCode(), cronExpression);
                nextTime = new Date(System.currentTimeMillis() + 24 * 60 * 60 * 1000L);
            }
            // 使用独立事务推进计划时间，避免影响已成功提交的工单数据。
            dmsPlanInspectTxService.updatePlanNextTimeInNewTx(plan.getPlanInspectId(), nextTime);
            log.debug("更新巡检计划下次执行时间 | planInspectCode={}, nextTime={}", plan.getPlanInspectCode(), nextTime);
        } catch (Exception e) {
            log.error("更新巡检计划下次执行时间失败 | planInspectId={}, planInspectCode={}, cronExpression={}, 异常信息: {}",
                    plan.getPlanInspectId(), plan.getPlanInspectCode(), plan.getCronExpression(),
                    e.getMessage(), e);
        }
    }

    /**
     * 计算计划下次执行时间:
     * 1) 基于计划当前 planTime 推进；
     * 2) 若算出的时间仍不晚于当前时间，则继续推进，直到晚于当前时间。
     */
    private Date calculateNextPlanTime(String cronExpression, Date currentPlanTime) {
        try {
            CronExpression cron = new CronExpression(cronExpression);
            Date now = new Date();
            Date cursor = currentPlanTime != null ? currentPlanTime : now;
            Date next = cron.getNextValidTimeAfter(cursor);
            while (next != null && !next.after(now)) {
                next = cron.getNextValidTimeAfter(next);
            }
            return next;
        } catch (ParseException e) {
            log.error("cron表达式非法，无法计算下次执行时间 | cronExpression={}, 异常信息: {}", cronExpression, e.getMessage(), e);
            return null;
        }
    }
}
