package cn.iocoder.yudao.module.mes.service.taskmanagement;

import cn.iocoder.yudao.module.iot.controller.admin.device.enums.TaskTypeEnum;
import cn.iocoder.yudao.module.iot.controller.admin.device.scheduled.scheduler.TaskSchedulerManager;
import cn.iocoder.yudao.module.mes.dal.dataobject.deviceledger.DeviceLedgerDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.dvsubject.DvSubjectDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.planmaintenance.PlanMaintenanceDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.subjectplan.SubjectPlanDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.ticketmanagement.TicketManagementDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.ticketresults.TicketResultsDO;
import cn.iocoder.yudao.module.mes.dal.mysql.deviceledger.DeviceLedgerMapper;
import cn.iocoder.yudao.module.mes.dal.mysql.dvsubject.DvSubjectMapper;
import cn.iocoder.yudao.module.mes.dal.mysql.planmaintenance.PlanMaintenanceMapper;
import cn.iocoder.yudao.module.mes.dal.mysql.subjectplan.SubjectPlanMapper;
import cn.iocoder.yudao.module.mes.dal.mysql.ticketmanagement.TicketManagementMapper;
import cn.iocoder.yudao.module.mes.dal.mysql.ticketresults.TicketResultsMapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.cronutils.model.Cron;
import com.cronutils.model.CronType;
import com.cronutils.model.definition.CronDefinitionBuilder;
import com.cronutils.model.time.ExecutionTime;
import com.cronutils.parser.CronParser;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.transaction.annotation.Transactional;

import java.text.SimpleDateFormat;
import java.time.Duration;
import java.time.ZonedDateTime;
import java.util.*;
import java.util.stream.Collectors;

import cn.iocoder.yudao.module.mes.controller.admin.taskmanagement.vo.*;
import cn.iocoder.yudao.module.mes.dal.dataobject.taskmanagement.TaskManagementDO;
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 cn.iocoder.yudao.module.mes.dal.mysql.taskmanagement.TaskManagementMapper;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;

/**
 * 设备类型 Service 实现类
 *
 * @author 内蒙必硕
 */
@Service
@Validated
@Slf4j
public class TaskManagementServiceImpl implements TaskManagementService {

    @Resource
    private TaskManagementMapper taskManagementMapper;

    @Resource
    private TicketManagementMapper ticketManagementMapper;

    @Resource
    private TicketResultsMapper ticketResultsMapper;

    @Resource
    private DeviceLedgerMapper deviceLedgerMapper;

    @Resource
    private PlanMaintenanceMapper planMaintenanceMapper;

    @Resource
    private SubjectPlanMapper subjectPlanMapper;

    @Resource
    private DvSubjectMapper dvSubjectMapper;

    @Resource
    private TaskSchedulerManager taskSchedulerManager;

    @Override
    public Long createTaskManagement(TaskManagementSaveReqVO createReqVO) {
        String cronExpr = createReqVO.getCronExpression();

        // 校验 cron 表达式最小间隔
        isCronIntervalAtLeastOneHour(cronExpr);


        // 插入
        TaskManagementDO taskManagement = BeanUtils.toBean(createReqVO, TaskManagementDO.class);
        taskManagementMapper.insert(taskManagement);
        // 返回
        return taskManagement.getId();
    }

    private void isCronIntervalAtLeastOneHour(String cronExpr) {

        try {
            if (StringUtils.isBlank(cronExpr)){
                return;
            }

            CronParser parser = new CronParser(CronDefinitionBuilder.instanceDefinitionFor(CronType.QUARTZ));
            Cron cron = parser.parse(cronExpr);
            cron.validate();

            ExecutionTime executionTime = ExecutionTime.forCron(cron);

            ZonedDateTime now = ZonedDateTime.now();
            ZonedDateTime next1 = executionTime.nextExecution(now).get();
            ZonedDateTime next2 = executionTime.nextExecution(next1).get();

            Duration duration = Duration.between(next1, next2);

            if (duration.toHours() < 1) {
                throw exception(TASK_CORN_NOT_LE_HOUR);
            }

        } catch (Exception e) {
            throw exception(TASK_CORN_NOT_LE_HOUR);
        }

    }

    @Override
    public void updateTaskManagement(TaskManagementSaveReqVO updateReqVO) {
        // 校验存在
        validateTaskManagementExists(updateReqVO.getId());
        // 更新
        TaskManagementDO updateObj = BeanUtils.toBean(updateReqVO, TaskManagementDO.class);
        taskManagementMapper.updateById(updateObj);
    }

    @Override
    public void deleteTaskManagement(List<Long> idList ) {
        for (Long id : idList) {
            // 校验存在
            validateTaskManagementExists(id);
        }
        // 删除
        taskManagementMapper.deleteByIds(idList);
    }

    private void validateTaskManagementExists(Long id) {
        if (taskManagementMapper.selectById(id) == null) {
            throw exception(TASK_MANAGEMENT_NOT_EXISTS);
        }
    }

    @Override
    public TaskManagementDO getTaskManagement(Long id) {
        return taskManagementMapper.selectById(id);
    }

    @Override
    public PageResult<TaskManagementDO> getTaskManagementPage(TaskManagementPageReqVO pageReqVO) {

        PageResult<TaskManagementDO> taskManagementDOPageResult = taskManagementMapper.selectPage(pageReqVO);
        for (TaskManagementDO taskManagementDO : taskManagementDOPageResult.getList()) {
            PlanMaintenanceDO planMaintenanceDO = planMaintenanceMapper.selectById(taskManagementDO.getProjectForm());
            if (planMaintenanceDO !=null) {
                taskManagementDO.setProjectFormName(planMaintenanceDO.getPlanName());
            }
        }


        return taskManagementDOPageResult;
    }

    @Override
    public void createTicket(Long id) {

        //检验数据是否存在
        validateTaskManagementExists(id);
        TaskManagementDO taskManagementDO = taskManagementMapper.selectById(id);
        if (taskManagementDO == null){
            throw exception(TASK_MANAGEMENT_NOT_EXISTS);
        }

        // 将逗号分隔的字符串转换为Long类型的List
        List<Long> idList = Arrays.stream(taskManagementDO.getDeviceList().split(","))
                .map(String::trim) // 去除可能存在的空格
                .map(Long::valueOf)
                .collect(Collectors.toList());

        for (Long deviceId : idList) {
            TicketManagementDO ticketManagementDO = new TicketManagementDO();
            DeviceLedgerDO deviceLedgerDO = deviceLedgerMapper.selectById(deviceId);
            ticketManagementDO.setTaskId(taskManagementDO.getId());
            ticketManagementDO.setPlanNo(generatePrefixedOrderNo());
            ticketManagementDO.setPlanId(taskManagementDO.getProjectForm());
            ticketManagementDO.setDeviceName(deviceLedgerDO.getDeviceName());
            ticketManagementDO.setPlanType(taskManagementDO.getTaskType());
            ticketManagementDO.setConfigName(taskManagementDO.getName());
            ticketManagementDO.setTaskEndTime(taskManagementDO.getEndDate().atStartOfDay());
            ticketManagementMapper.insert(ticketManagementDO);

            List<SubjectPlanDO> subjectPlanDOList = subjectPlanMapper.selectList(Wrappers.<SubjectPlanDO>lambdaQuery().eq(SubjectPlanDO::getPlanId, ticketManagementDO.getPlanId()));
            for (SubjectPlanDO subjectPlanDO : subjectPlanDOList) {
                DvSubjectDO dvSubjectDO = dvSubjectMapper.selectById(subjectPlanDO.getSubjectId());

                TicketResultsDO ticketResultsDO = new TicketResultsDO();
                ticketResultsDO.setInspectionItemName(dvSubjectDO.getSubjectName());
                ticketResultsDO.setInspectionMethod(dvSubjectDO.getInspectionMethod());
                ticketResultsDO.setJudgmentCriteria(dvSubjectDO.getJudgmentCriteria());
                ticketResultsDO.setManagementId(ticketManagementDO.getId());
                ticketResultsDO.setDeviceId(deviceId);
                ticketResultsMapper.insert(ticketResultsDO);
            }

        }

    }

    @Override
    public void updateTaskManagementEnabled(TaskManagementUpdateEnabledReqVO updateEnabledReqVO) {
        // 1. 校验任务是否存在
        TaskManagementDO task = taskManagementMapper.selectById(updateEnabledReqVO.getId());
        if (task == null) {
            throw exception(TASK_MANAGEMENT_NOT_EXISTS);
        }
        if (StringUtils.isBlank(task.getCronExpression())) {
            throw exception(TASK_CORN_NOT_EXISTS);
        }

        // 2. 如果状态没有变化，直接返回
        if (Objects.equals(task.getEnabled(), updateEnabledReqVO.getEnabled())) {
            return;
        }

        // 3. 执行状态更新操作
        executeEnableUpdate(task, updateEnabledReqVO.getEnabled());

    }

    /**
     * 执行启用/禁用操作
     */
    private void executeEnableUpdate(TaskManagementDO task, Boolean enabled) {
        // 更新数据库状态
        updateTaskStatusInDB(task.getId(), enabled);

        // 处理定时任务
        if (enabled) {
            enableTaskSchedule(task);
        } else {
            disableTaskSchedule(task);
        }
    }


    /**
     * 启用任务调度
     */
    private void enableTaskSchedule(TaskManagementDO task) {
        // 验证Cron表达式
        if (StringUtils.isBlank(task.getCronExpression())) {
            log.warn("任务{}没有配置Cron表达式，无法启用调度", task.getId());
            return;
        }

        // 启动定时任务
        try {
            boolean success = taskSchedulerManager.startWorkOrderTask(
                    task.getId(),
                    task.getCronExpression()
            );

            if (success) {
                log.info("任务{}调度启动成功", task.getId());
            } else {
                log.error("任务{}调度启动失败", task.getId());
                // 可以记录失败日志或发送通知
            }
        } catch (Exception e) {
            log.error("任务{}调度启动异常", task.getId(), e);
            // 可以考虑回滚数据库状态
        }
    }

    /**
     * 禁用任务调度
     */
    private void disableTaskSchedule(TaskManagementDO task) {
        try {
            boolean success = taskSchedulerManager.stopWorkOrderTask(task.getId());

            if (success) {
                log.info("任务{}调度停止成功", task.getId());
            } else {
                log.warn("任务{}调度停止失败，可能任务不存在或已停止", task.getId());
            }
        } catch (Exception e) {
            log.error("任务{}调度停止异常", task.getId(), e);
        }
    }
    /**
     * 更新数据库状态
     */
    private void updateTaskStatusInDB(Long taskId, Boolean enabled) {
        TaskManagementDO updateObj = new TaskManagementDO();
        updateObj.setId(taskId);
        updateObj.setEnabled(enabled);
        taskManagementMapper.updateById(updateObj);
    }


    /**
     * 带前缀的时间戳单号
     */
    public static String generatePrefixedOrderNo() {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
        String date = sdf.format(new Date());
        String randomNum = String.format("%06d", new Random().nextInt(1000000));
        return "E" + date + randomNum;
    }
}