package com.erp.task.service;

import com.erp.task.dto.*;
import com.erp.task.entity.ScheduledTask;
import com.erp.task.enums.TaskStatus;
import com.erp.task.exception.TaskException;
import com.erp.task.repository.ScheduledTaskRepository;
import com.erp.task.scheduler.TaskScheduler;
import com.erp.task.util.CronUtils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
import com.erp.common.core.model.PageResponse;

/**
 * 定时任务服务
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class ScheduledTaskService {

    private final ScheduledTaskRepository taskRepository;
    private final TaskScheduler taskScheduler;
    private final ObjectMapper objectMapper;

    /**
     * 创建任务
     */
    @Transactional
    public ScheduledTaskDTO create(TaskCreateRequest request) {
        // 验证Cron表达式
        if (!CronUtils.isValidQuartzCron(request.getCronExpression())) {
            throw TaskException.invalidCron(request.getCronExpression());
        }

        // 检查重复
        if (taskRepository.existsByTaskNameAndTaskGroupAndIdNot(
                request.getTaskName(), request.getTaskGroup(), -1L)) {
            throw TaskException.alreadyExists(request.getTaskName(), request.getTaskGroup());
        }

        ScheduledTask task = ScheduledTask.builder()
                .taskName(request.getTaskName())
                .description(request.getDescription())
                .taskGroup(request.getTaskGroup())
                .cronExpression(request.getCronExpression())
                .taskClass(request.getTaskClass())
                .methodName(request.getMethodName())
                .taskParams(request.getTaskParams())
                .status(TaskStatus.DRAFT)
                .concurrent(request.getConcurrent())
                .sync(request.getSync())
                .taskType(request.getTaskType())
                .xxlJobId(request.getXxlJobId())
                .maxRetries(request.getMaxRetries())
                .retryInterval(request.getRetryInterval())
                .timeout(request.getTimeout())
                .alertEmails(request.getAlertEmails())
                .alertPhones(request.getAlertPhones())
                .alertEnabled(request.getAlertEnabled())
                .misfirePolicy(request.getMisfirePolicy())
                .owner(request.getOwner())
                .remark(request.getRemark())
                .build();

        // 计算下次执行时间
        task.setNextFireTime(CronUtils.getNextFireTime(request.getCronExpression()).orElse(null));

        ScheduledTask saved = taskRepository.save(task);
        log.info("创建任务成功: {}", saved.getId());
        return toDTO(saved);
    }

    /**
     * 更新任务
     */
    @Transactional
    public ScheduledTaskDTO update(TaskUpdateRequest request) {
        ScheduledTask task = taskRepository.findById(request.getId())
                .orElseThrow(() -> TaskException.notFound(request.getId()));

        // 如果更新了Cron表达式，验证
        if (request.getCronExpression() != null && !request.getCronExpression().equals(task.getCronExpression())) {
            if (!CronUtils.isValidQuartzCron(request.getCronExpression())) {
                throw TaskException.invalidCron(request.getCronExpression());
            }
            task.setCronExpression(request.getCronExpression());
            task.setNextFireTime(CronUtils.getNextFireTime(request.getCronExpression()).orElse(null));
        }

        // 如果任务在运行，需要先停止再更新
        boolean wasRunning = task.getStatus() == TaskStatus.RUNNING;
        if (wasRunning) {
            taskScheduler.pauseJob(task);
        }

        // 更新字段
        if (request.getTaskName() != null) task.setTaskName(request.getTaskName());
        if (request.getDescription() != null) task.setDescription(request.getDescription());
        if (request.getTaskGroup() != null) task.setTaskGroup(request.getTaskGroup());
        if (request.getTaskClass() != null) task.setTaskClass(request.getTaskClass());
        if (request.getMethodName() != null) task.setMethodName(request.getMethodName());
        if (request.getTaskParams() != null) task.setTaskParams(request.getTaskParams());
        if (request.getConcurrent() != null) task.setConcurrent(request.getConcurrent());
        if (request.getSync() != null) task.setSync(request.getSync());
        if (request.getTaskType() != null) task.setTaskType(request.getTaskType());
        if (request.getXxlJobId() != null) task.setXxlJobId(request.getXxlJobId());
        if (request.getMaxRetries() != null) task.setMaxRetries(request.getMaxRetries());
        if (request.getRetryInterval() != null) task.setRetryInterval(request.getRetryInterval());
        if (request.getTimeout() != null) task.setTimeout(request.getTimeout());
        if (request.getAlertEmails() != null) task.setAlertEmails(request.getAlertEmails());
        if (request.getAlertPhones() != null) task.setAlertPhones(request.getAlertPhones());
        if (request.getAlertEnabled() != null) task.setAlertEnabled(request.getAlertEnabled());
        if (request.getMisfirePolicy() != null) task.setMisfirePolicy(request.getMisfirePolicy());
        if (request.getOwner() != null) task.setOwner(request.getOwner());
        if (request.getRemark() != null) task.setRemark(request.getRemark());

        ScheduledTask saved = taskRepository.save(task);

        // 如果之前在运行，重新启动
        if (wasRunning) {
            taskScheduler.resumeJob(saved);
        }

        log.info("更新任务成功: {}", saved.getId());
        return toDTO(saved);
    }

    /**
     * 删除任务
     */
    @Transactional
    public void delete(Long id) {
        ScheduledTask task = taskRepository.findById(id)
                .orElseThrow(() -> TaskException.notFound(id));

        // 先停止任务
        if (task.getStatus() == TaskStatus.RUNNING) {
            taskScheduler.deleteJob(task);
        }

        task.setStatus(TaskStatus.DELETED);
        taskRepository.save(task);
        log.info("删除任务: {}", id);
    }

    /**
     * 根据ID查询
     */
    public ScheduledTaskDTO getById(Long id) {
        ScheduledTask task = taskRepository.findById(id)
                .orElseThrow(() -> TaskException.notFound(id));
        return toDTO(task);
    }

    /**
     * 分页查询
     */
    public PageResponse<ScheduledTaskDTO> query(TaskQueryRequest request) {
        Sort sort = Sort.by(Sort.Direction.fromString(request.getSortOrder()), request.getSortBy());
        Pageable pageable = PageRequest.of(request.getPage() - 1, request.getPageSize(), sort);

        TaskStatus status = null;
        if (request.getStatus() != null && !request.getStatus().isEmpty()) {
            try {
                status = TaskStatus.valueOf(request.getStatus());
            } catch (Exception e) {
                log.warn("解析任务状态失败: status={}, error={}", request.getStatus(), e.getMessage());
            }
        }

        Page<ScheduledTask> page = taskRepository.findByConditions(
                request.getTaskName(),
                request.getTaskGroup(),
                status,
                request.getTaskType(),
                request.getOwner(),
                pageable);

        List<ScheduledTaskDTO> records = page.getContent().stream()
                .map(this::toDTO)
                .collect(Collectors.toList());

        return PageResponse.of(records, page.getTotalElements(), request.getPage(), request.getPageSize());
    }

    /**
     * 启动任务
     */
    @Transactional
    public ScheduledTaskDTO start(Long id) {
        ScheduledTask task = taskRepository.findById(id)
                .orElseThrow(() -> TaskException.notFound(id));

        if (task.getStatus() == TaskStatus.RUNNING) {
            return toDTO(task);
        }

        taskScheduler.scheduleJob(task);
        task.setStatus(TaskStatus.RUNNING);
        task.setNextFireTime(CronUtils.getNextFireTime(task.getCronExpression()).orElse(null));

        ScheduledTask saved = taskRepository.save(task);
        log.info("启动任务: {}", id);
        return toDTO(saved);
    }

    /**
     * 停止任务
     */
    @Transactional
    public ScheduledTaskDTO stop(Long id) {
        ScheduledTask task = taskRepository.findById(id)
                .orElseThrow(() -> TaskException.notFound(id));

        if (task.getStatus() != TaskStatus.RUNNING) {
            return toDTO(task);
        }

        taskScheduler.pauseJob(task);
        task.setStatus(TaskStatus.STOPPED);

        ScheduledTask saved = taskRepository.save(task);
        log.info("停止任务: {}", id);
        return toDTO(saved);
    }

    /**
     * 暂停任务
     */
    @Transactional
    public ScheduledTaskDTO pause(Long id) {
        ScheduledTask task = taskRepository.findById(id)
                .orElseThrow(() -> TaskException.notFound(id));

        taskScheduler.pauseJob(task);
        task.setStatus(TaskStatus.PAUSED);

        ScheduledTask saved = taskRepository.save(task);
        log.info("暂停任务: {}", id);
        return toDTO(saved);
    }

    /**
     * 恢复任务
     */
    @Transactional
    public ScheduledTaskDTO resume(Long id) {
        ScheduledTask task = taskRepository.findById(id)
                .orElseThrow(() -> TaskException.notFound(id));

        taskScheduler.resumeJob(task);
        task.setStatus(TaskStatus.RUNNING);
        task.setNextFireTime(CronUtils.getNextFireTime(task.getCronExpression()).orElse(null));

        ScheduledTask saved = taskRepository.save(task);
        log.info("恢复任务: {}", id);
        return toDTO(saved);
    }

    /**
     * 执行一次任务
     */
    @Transactional
    public void executeOnce(Long id) {
        ScheduledTask task = taskRepository.findById(id)
                .orElseThrow(() -> TaskException.notFound(id));
        taskScheduler.executeOnce(task);
    }

    /**
     * 获取所有运行中的任务
     */
    public List<ScheduledTaskDTO> getRunningTasks() {
        return taskRepository.findAllRunning().stream()
                .map(this::toDTO)
                .collect(Collectors.toList());
    }

    /**
     * 获取任务统计
     */
    public TaskStatsDTO getStats() {
        List<Object[]> statusCounts = taskRepository.countByStatus();
        List<Object[]> groupCounts = taskRepository.countByGroup();

        Map<String, Long> statusMap = new HashMap<>();
        Map<String, Long> groupMap = new HashMap<>();
        long total = 0, running = 0, stopped = 0;

        for (Object[] row : statusCounts) {
            String status = row[0].toString();
            Long count = (Long) row[1];
            statusMap.put(status, count);
            total += count;
            if ("RUNNING".equals(status)) running = count;
            if ("STOPPED".equals(status)) stopped = count;
        }

        for (Object[] row : groupCounts) {
            groupMap.put(row[0].toString(), (Long) row[1]);
        }

        LocalDateTime today = LocalDateTime.now().withHour(0).withMinute(0).withSecond(0);
        Long todayExecutions = 0L;
        Long todaySuccess = 0L;
        Long todayFailed = 0L;
        Long todayAvgDuration = 0L;

        // 统计数据从执行日志表获取，这里简化处理
        Map<String, Long> weeklyExecutions = new HashMap<>();

        return TaskStatsDTO.builder()
                .totalTasks(total)
                .runningTasks(running)
                .stoppedTasks(stopped)
                .todayExecutions(todayExecutions)
                .todaySuccess(todaySuccess)
                .todayFailed(todayFailed)
                .todayAvgDuration(todayAvgDuration)
                .weeklyExecutions(weeklyExecutions)
                .groupTaskCount(groupMap)
                .healthRate(total > 0 ? (double) (total - stopped) / total * 100 : 100.0)
                .alertTasks(0L)
                .build();
    }

    /**
     * 获取最近执行时间
     */
    public List<LocalDateTime> getNextFireTimes(Long id, int n) {
        ScheduledTask task = taskRepository.findById(id)
                .orElseThrow(() -> TaskException.notFound(id));
        return CronUtils.getNextFireTimes(task.getCronExpression(), n);
    }

    /**
     * 验证Cron表达式
     */
    public boolean validateCron(String cronExpression) {
        return CronUtils.isValidQuartzCron(cronExpression);
    }

    /**
     * DTO转换
     */
    private ScheduledTaskDTO toDTO(ScheduledTask task) {
        ScheduledTaskDTOBuilder builder = ScheduledTaskDTO.builder()
                .id(task.getId())
                .taskName(task.getTaskName())
                .description(task.getDescription())
                .taskGroup(task.getTaskGroup())
                .cronExpression(task.getCronExpression())
                .taskClass(task.getTaskClass())
                .methodName(task.getMethodName())
                .taskParams(task.getTaskParams())
                .status(task.getStatus())
                .concurrent(task.getConcurrent())
                .sync(task.getSync())
                .taskType(task.getTaskType())
                .xxlJobId(task.getXxlJobId())
                .maxRetries(task.getMaxRetries())
                .retryInterval(task.getRetryInterval())
                .timeout(task.getTimeout())
                .alertEmails(task.getAlertEmails())
                .alertPhones(task.getAlertPhones())
                .alertEnabled(task.getAlertEnabled())
                .misfirePolicy(task.getMisfirePolicy())
                .nextFireTime(task.getNextFireTime())
                .prevFireTime(task.getPrevFireTime())
                .executeCount(task.getExecuteCount())
                .owner(task.getOwner())
                .remark(task.getRemark())
                .tenantId(task.getTenantId())
                .createdAt(task.getCreatedAt())
                .updatedAt(task.getUpdatedAt());

        // 解析任务参数
        if (task.getTaskParams() != null && !task.getTaskParams().isEmpty()) {
            try {
                @SuppressWarnings("unchecked")
                Map<String, Object> paramsMap = objectMapper.readValue(task.getTaskParams(), Map.class);
                builder.taskParamsMap(paramsMap);
            } catch (JsonProcessingException e) {
                log.warn("解析任务参数失败: {}", task.getTaskParams());
            }
        }

        return builder.build();
    }
}
