package com.qqchen.deploy.backend.schedule.service.impl;

import com.qqchen.deploy.backend.framework.enums.ResponseCode;
import com.qqchen.deploy.backend.framework.exception.BusinessException;
import com.qqchen.deploy.backend.framework.service.impl.BaseServiceImpl;
import com.qqchen.deploy.backend.framework.utils.RedisUtil;
import com.qqchen.deploy.backend.schedule.converter.ScheduleJobCategoryConverter;
import com.qqchen.deploy.backend.schedule.dto.JobDashboardDTO;
import com.qqchen.deploy.backend.schedule.dto.JobStatusDTO;
import com.qqchen.deploy.backend.schedule.dto.ScheduleJobDTO;
import com.qqchen.deploy.backend.schedule.dto.ScheduleJobLogDTO;
import com.qqchen.deploy.backend.schedule.entity.ScheduleJob;
import com.qqchen.deploy.backend.schedule.entity.ScheduleJobCategory;
import com.qqchen.deploy.backend.schedule.entity.ScheduleJobLog;
import com.qqchen.deploy.backend.schedule.query.ScheduleJobQuery;
import com.qqchen.deploy.backend.schedule.enums.ScheduleJobStatusEnum;
import com.qqchen.deploy.backend.schedule.job.DynamicJob;
import com.qqchen.deploy.backend.schedule.repository.IScheduleJobCategoryRepository;
import com.qqchen.deploy.backend.schedule.repository.IScheduleJobLogRepository;
import com.qqchen.deploy.backend.schedule.repository.IScheduleJobRepository;
import com.qqchen.deploy.backend.schedule.service.IScheduleJobService;
import com.qqchen.deploy.backend.workflow.converter.FormDefinitionConverter;
import com.qqchen.deploy.backend.workflow.entity.FormDefinition;
import com.qqchen.deploy.backend.workflow.repository.IFormDefinitionRepository;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * 定时任务Service实现
 *
 * @author qichen
 */
@Slf4j
@Service
public class ScheduleJobServiceImpl extends BaseServiceImpl<ScheduleJob, ScheduleJobDTO, ScheduleJobQuery, Long>
    implements IScheduleJobService {

    @Resource
    private IScheduleJobRepository jobRepository;

    @Resource
    private IScheduleJobCategoryRepository categoryRepository;

    @Resource
    private IScheduleJobLogRepository jobLogRepository;

    @Resource
    private ScheduleJobCategoryConverter categoryConverter;

    @Resource
    private IFormDefinitionRepository formDefinitionRepository;

    @Resource
    private FormDefinitionConverter formDefinitionConverter;

    @Resource
    private Scheduler scheduler;

    @Resource
    private RedisUtil redisUtil;

    @Resource
    private com.qqchen.deploy.backend.schedule.service.JobStatusRedisService jobStatusRedisService;

    @Override
    @Transactional
    public ScheduleJobDTO create(ScheduleJobDTO dto) {
        // 检查任务名称唯一性
        if (jobRepository.existsByJobNameAndDeletedFalse(dto.getJobName())) {
            throw new BusinessException(ResponseCode.DATA_ALREADY_EXISTS, new Object[] {"任务名称", dto.getJobName()});
        }
        return super.create(dto);
    }

    @Override
    @Transactional
    public ScheduleJobDTO update(Long id, ScheduleJobDTO dto) {
        // 1. 查询旧数据
        ScheduleJob oldJob = findEntityById(id);
        ScheduleJobStatusEnum oldStatus = oldJob.getStatus();

        // 2. 检查是否试图修改 status（强制拦截）
        if (dto.getStatus() != null && dto.getStatus() != oldStatus) {
            throw new BusinessException(ResponseCode.SCHEDULE_JOB_STATUS_CANNOT_UPDATE,
                new Object[] {"请使用 /start、/pause、/resume、/disable 接口管理任务状态"});
        }

        // 3. 强制保持原状态（防止前端传入）
        dto.setStatus(oldStatus);

        // 4. 更新数据库
        ScheduleJobDTO updated = super.update(id, dto);

        // 5. 只有非 DISABLED 状态才需要重新调度
        if (oldStatus != ScheduleJobStatusEnum.DISABLED) {
            try {
                JobKey jobKey = getJobKey(id);
                if (scheduler.checkExists(jobKey)) {
                    ScheduleJob job = findEntityById(id);

                    // 先删除旧的Job
                    scheduler.deleteJob(jobKey);

                    // 重新创建JobDetail（使用最新参数）
                    JobDetail jobDetail = JobBuilder.newJob(DynamicJob.class)
                        .withIdentity(jobKey)
                        .withDescription(job.getJobDescription())
                        .build();

                    // 设置最新的JobDataMap
                    jobDetail.getJobDataMap().put("jobId", job.getId());
                    jobDetail.getJobDataMap().put("jobName", job.getJobName());
                    jobDetail.getJobDataMap().put("beanName", job.getBeanName());
                    jobDetail.getJobDataMap().put("methodName", job.getMethodName());
                    jobDetail.getJobDataMap().put("methodParams", job.getMethodParams());

                    // 重新创建Trigger
                    Trigger trigger = TriggerBuilder.newTrigger()
                        .withIdentity(getTriggerKey(id))
                        .withSchedule(CronScheduleBuilder.cronSchedule(job.getCronExpression()))
                        .build();

                    // 重新调度
                    scheduler.scheduleJob(jobDetail, trigger);

                    // 如果原来是 PAUSED，重新调度后也要暂停
                    if (oldStatus == ScheduleJobStatusEnum.PAUSED) {
                        scheduler.pauseJob(jobKey);
                    }

                    log.info("任务配置已更新并重新调度：jobId={}, jobName={}", job.getId(), job.getJobName());
                }
            } catch (SchedulerException e) {
                log.error("更新任务调度失败：jobId={}", id, e);
                // 不抛异常，因为数据库已经更新成功，下次重启会生效
            }
        } else {
            log.info("任务配置已更新（DISABLED状态，不调度）：jobId={}", id);
        }

        return updated;
    }

    @Override
    public Page<ScheduleJobDTO> page(ScheduleJobQuery query) {
        Page<ScheduleJobDTO> page = super.page(query);

        // 填充关联信息
        List<ScheduleJobDTO> content = page.getContent().stream()
            .peek(job -> {
                // 填充分类信息
                if (job.getCategoryId() != null) {
                    Optional<ScheduleJobCategory> categoryOptional = categoryRepository.findById(job.getCategoryId());
                    categoryOptional.ifPresent(category -> job.setCategory(categoryConverter.toDto(category)));
                }

                // 填充表单信息
                if (job.getFormDefinitionId() != null) {
                    Optional<FormDefinition> formOptional = formDefinitionRepository.findById(job.getFormDefinitionId());
                    formOptional.ifPresent(form -> job.setFormDefinition(formDefinitionConverter.toDto(form)));
                }
            })
            .collect(Collectors.toList());

        return new PageImpl<>(content, page.getPageable(), page.getTotalElements());
    }

    @Override
    @Transactional
    public void startJob(Long jobId) {
        try {
            ScheduleJob job = findEntityById(jobId);

            // 创建JobDetail
            JobDetail jobDetail = JobBuilder.newJob(DynamicJob.class)
                .withIdentity(getJobKey(jobId))
                .withDescription(job.getJobDescription())
                .build();

            // 设置JobDataMap
            jobDetail.getJobDataMap().put("jobId", job.getId());
            jobDetail.getJobDataMap().put("jobName", job.getJobName());
            jobDetail.getJobDataMap().put("beanName", job.getBeanName());
            jobDetail.getJobDataMap().put("methodName", job.getMethodName());
            jobDetail.getJobDataMap().put("methodParams", job.getMethodParams());

            // 创建Trigger
            Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity(getTriggerKey(jobId))
                .withSchedule(CronScheduleBuilder.cronSchedule(job.getCronExpression()))
                .build();

            // 调度任务
            scheduler.scheduleJob(jobDetail, trigger);

            // 更新任务状态
            job.setStatus(ScheduleJobStatusEnum.ENABLED);
            jobRepository.save(job);

            log.info("任务启动成功：jobId={}, jobName={}", jobId, job.getJobName());

        } catch (SchedulerException e) {
            log.error("启动任务失败：jobId={}", jobId, e);
            throw new BusinessException(ResponseCode.SCHEDULE_JOB_START_FAILED);
        }
    }

    @Override
    @Transactional
    public void pauseJob(Long jobId) {
        try {
            scheduler.pauseJob(getJobKey(jobId));

            // 更新任务状态
            ScheduleJob job = findEntityById(jobId);
            job.setStatus(ScheduleJobStatusEnum.PAUSED);
            jobRepository.save(job);

            log.info("任务暂停成功：jobId={}", jobId);

        } catch (SchedulerException e) {
            log.error("暂停任务失败：jobId={}", jobId, e);
            throw new BusinessException(ResponseCode.SCHEDULE_JOB_PAUSE_FAILED);
        }
    }

    @Override
    @Transactional
    public void resumeJob(Long jobId) {
        try {
            scheduler.resumeJob(getJobKey(jobId));

            // 更新任务状态
            ScheduleJob job = findEntityById(jobId);
            job.setStatus(ScheduleJobStatusEnum.ENABLED);
            jobRepository.save(job);

            log.info("任务恢复成功：jobId={}", jobId);

        } catch (SchedulerException e) {
            log.error("恢复任务失败：jobId={}", jobId, e);
            throw new BusinessException(ResponseCode.SCHEDULE_JOB_RESUME_FAILED);
        }
    }

    @Override
    @Transactional
    public void disableJob(Long jobId) {
        ScheduleJob job = findEntityById(jobId);

        // 幂等性检查
        if (job.getStatus() == ScheduleJobStatusEnum.DISABLED) {
            log.debug("任务已经是禁用状态：jobId={}, jobName={}", jobId, job.getJobName());
            return;
        }

        try {
            // 从 Quartz 删除任务
            JobKey jobKey = getJobKey(jobId);
            if (scheduler.checkExists(jobKey)) {
                scheduler.deleteJob(jobKey);
                log.info("从Quartz删除任务：jobId={}, jobName={}", jobId, job.getJobName());
            }

            // 更新数据库状态
            job.setStatus(ScheduleJobStatusEnum.DISABLED);
            jobRepository.save(job);

            log.info("任务已禁用：jobId={}, jobName={}", jobId, job.getJobName());

        } catch (SchedulerException e) {
            log.error("禁用任务失败：jobId={}, jobName={}", jobId, job.getJobName(), e);
            throw new BusinessException(ResponseCode.SCHEDULE_JOB_DISABLE_FAILED);
        }
    }

    @Override
    @Transactional
    public void enableJob(Long jobId) {
        ScheduleJob job = findEntityById(jobId);
        if (job.getStatus() != ScheduleJobStatusEnum.DISABLED) {
            throw new BusinessException(ResponseCode.SCHEDULE_JOB_STATUS_CANNOT_UPDATE, new Object[] {"仅禁用状态任务可启用"});
        }
        try {
            // 创建并调度新Job
            JobDetail jobDetail = JobBuilder.newJob(DynamicJob.class)
                .withIdentity(getJobKey(jobId))
                .withDescription(job.getJobDescription())
                .build();
            jobDetail.getJobDataMap().put("jobId", job.getId());
            jobDetail.getJobDataMap().put("jobName", job.getJobName());
            jobDetail.getJobDataMap().put("beanName", job.getBeanName());
            jobDetail.getJobDataMap().put("methodName", job.getMethodName());
            jobDetail.getJobDataMap().put("methodParams", job.getMethodParams());

            Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity(getTriggerKey(jobId))
                .withSchedule(CronScheduleBuilder.cronSchedule(job.getCronExpression()))
                .build();

            scheduler.scheduleJob(jobDetail, trigger);
            job.setStatus(ScheduleJobStatusEnum.ENABLED);
            jobRepository.save(job);
            log.info("任务从DISABLED恢复启用成功：jobId={}, jobName={}", jobId, job.getJobName());
        } catch (SchedulerException e) {
            log.error("恢复启用任务失败：jobId={}, jobName={}", jobId, job.getJobName(), e);
            throw new BusinessException(ResponseCode.SCHEDULE_JOB_START_FAILED);
        }
    }

    @Override
    public void triggerJob(Long jobId) {
        // 检查任务状态
        ScheduleJob job = findEntityById(jobId);
        if (job.getStatus() == ScheduleJobStatusEnum.DISABLED) {
            log.warn("禁用状态的任务不允许触发：jobId={}, jobName={}", jobId, job.getJobName());
            throw new BusinessException(ResponseCode.SCHEDULE_JOB_DISABLED_CANNOT_TRIGGER);
        }

        try {
            scheduler.triggerJob(getJobKey(jobId));
            log.info("立即触发任务：jobId={}, jobName={}", jobId, job.getJobName());

        } catch (SchedulerException e) {
            log.error("触发任务失败：jobId={}, jobName={}", jobId, job.getJobName(), e);
            throw new BusinessException(ResponseCode.SCHEDULE_JOB_TRIGGER_FAILED);
        }
    }

    @Override
    public void updateCron(Long jobId, String cronExpression) {
        try {
            TriggerKey triggerKey = getTriggerKey(jobId);
            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);

            if (trigger == null) {
                throw new BusinessException(ResponseCode.SCHEDULE_JOB_TRIGGER_NOT_FOUND);
            }

            // 创建新的Trigger
            CronTrigger newTrigger = trigger.getTriggerBuilder()
                .withIdentity(triggerKey)
                .withSchedule(CronScheduleBuilder.cronSchedule(cronExpression))
                .build();

            // 重新调度
            scheduler.rescheduleJob(triggerKey, newTrigger);

            // 更新数据库
            ScheduleJob job = findEntityById(jobId);
            job.setCronExpression(cronExpression);
            jobRepository.save(job);

            log.info("更新任务Cron表达式成功：jobId={}, cron={}", jobId, cronExpression);

        } catch (SchedulerException e) {
            log.error("更新任务Cron表达式失败：jobId={}", jobId, e);
            throw new BusinessException(ResponseCode.SCHEDULE_JOB_UPDATE_CRON_FAILED);
        }
    }

    @Override
    public void loadAllEnabledJobs() {
        log.debug("开始加载所有启用的定时任务...");

        List<ScheduleJob> enabledJobs = jobRepository.findByStatusAndDeletedFalse(ScheduleJobStatusEnum.ENABLED);

        for (ScheduleJob job : enabledJobs) {
            try {
                startJob(job.getId());
                log.debug("加载任务成功：jobId={}, jobName={}", job.getId(), job.getJobName());
            } catch (Exception e) {
                log.error("加载任务失败：jobId={}, jobName={}", job.getId(), job.getJobName(), e);
            }
        }

        log.info("定时任务加载完成，成功加载 {} 个任务", enabledJobs.size());
    }

    @Override
    public JobDashboardDTO getDashboard() {
        // 1. 统计各状态任务数量
        long total = jobRepository.count();
        long enabled = jobRepository.countByStatusAndDeletedFalse(ScheduleJobStatusEnum.ENABLED);
        long disabled = jobRepository.countByStatusAndDeletedFalse(ScheduleJobStatusEnum.DISABLED);
        long paused = jobRepository.countByStatusAndDeletedFalse(ScheduleJobStatusEnum.PAUSED);

        // 2. 获取正在执行的任务（从Redis）
        List<JobDashboardDTO.RunningJobDTO> runningJobs = new ArrayList<>();
        List<ScheduleJob> allJobs = jobRepository.findAll();

        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

        for (ScheduleJob job : allJobs) {
            // ✅ 使用 JobStatusRedisService 读取 Hash 类型的状态数据
            JobStatusDTO statusDTO = jobStatusRedisService.getJobStatus(job.getId());

            if (statusDTO != null && "RUNNING".equals(statusDTO.getStatus())) {
                JobDashboardDTO.RunningJobDTO runningJob = JobDashboardDTO.RunningJobDTO.builder()
                    .jobId(job.getId())
                    .jobName(job.getJobName())
                    .startTime(statusDTO.getStartTime() != null ?
                        statusDTO.getStartTime().format(formatter) : null)
                    .progress(statusDTO.getProgress())
                    .message(statusDTO.getMessage())
                    .status(statusDTO.getStatus())
                    .build();
                runningJobs.add(runningJob);
            }
        }

        // 3. 获取最近10条执行日志
        PageRequest pageRequest = PageRequest.of(0, 10, Sort.by(Sort.Direction.DESC, "executeTime"));
        Page<ScheduleJobLog> logPage = jobLogRepository.findAll(pageRequest);
        List<ScheduleJobLogDTO> recentLogs = logPage.getContent().stream()
            .map(log -> {
                ScheduleJobLogDTO dto = new ScheduleJobLogDTO();
                dto.setId(log.getId());
                dto.setJobId(log.getJobId());
                dto.setJobName(log.getJobName());
                dto.setExecuteTime(log.getExecuteTime());
                dto.setFinishTime(log.getFinishTime());
                dto.setStatus(log.getStatus());
                dto.setResultMessage(log.getResultMessage());
                dto.setExceptionInfo(log.getExceptionInfo());
                return dto;
            })
            .collect(Collectors.toList());

        // 4. 构建返回结果
        JobDashboardDTO.JobSummaryDTO summary = JobDashboardDTO.JobSummaryDTO.builder()
            .total(total)
            .enabled(enabled)
            .disabled(disabled)
            .paused(paused)
            .running((long) runningJobs.size())
            .build();

        return JobDashboardDTO.builder()
            .summary(summary)
            .runningJobs(runningJobs)
            .recentLogs(recentLogs)
            .build();
    }

    /**
     * 获取JobKey
     */
    private JobKey getJobKey(Long jobId) {
        return JobKey.jobKey("job_" + jobId, "DEFAULT");
    }

    /**
     * 获取TriggerKey
     */
    private TriggerKey getTriggerKey(Long jobId) {
        return TriggerKey.triggerKey("trigger_" + jobId, "DEFAULT");
    }
}
