package com.erp.report.export.scheduler;

import com.erp.report.export.dto.*;
import com.erp.report.export.service.ReportExportService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.TriggerContext;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;

/**
 * 定时导出调度器
 * 
 * 管理定时导出任务的生命周期
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class ExportScheduler {

    private final ReportExportService reportExportService;

    /** 定时任务存储 */
    private final Map<String, ScheduledTaskInfo> scheduledTasks = new ConcurrentHashMap<>();

    /**
     * 创建定时导出任务
     *
     * @param taskId 任务ID
     * @param request 导出请求
     * @param cronExpression Cron表达式
     * @param tenantId 租户ID
     */
    public void createScheduledTask(String taskId, BaseExportRequest request, 
            String cronExpression, Long tenantId) {
        
        ScheduledTaskInfo taskInfo = new ScheduledTaskInfo();
        taskInfo.setTaskId(taskId);
        taskInfo.setTenantId(tenantId);
        taskInfo.setCronExpression(cronExpression);
        taskInfo.setRequest(request);
        taskInfo.setCreatedAt(LocalDateTime.now());
        taskInfo.setLastRunAt(null);
        taskInfo.setNextRunAt(calculateNextRunTime(cronExpression));
        taskInfo.setActive(true);
        
        scheduledTasks.put(taskId, taskInfo);
        
        log.info("创建定时导出任务, taskId={}, cron={}, tenantId={}", taskId, cronExpression, tenantId);
    }

    /**
     * 取消定时导出任务
     *
     * @param taskId 任务ID
     * @return 是否取消成功
     */
    public boolean cancelTask(String taskId) {
        ScheduledTaskInfo taskInfo = scheduledTasks.remove(taskId);
        if (taskInfo != null) {
            taskInfo.setActive(false);
            log.info("取消定时导出任务, taskId={}", taskId);
            return true;
        }
        return false;
    }

    /**
     * 获取任务信息
     *
     * @param taskId 任务ID
     * @return 任务信息
     */
    public ScheduledTaskInfo getTaskInfo(String taskId) {
        return scheduledTasks.get(taskId);
    }

    /**
     * 获取租户的所有定时任务
     *
     * @param tenantId 租户ID
     * @return 任务列表
     */
    public List<ScheduledTaskInfo> getTasksByTenant(Long tenantId) {
        return scheduledTasks.values().stream()
                .filter(task -> tenantId.equals(task.getTenantId()))
                .toList();
    }

    /**
     * 执行定时导出任务
     *
     * @param taskId 任务ID
     */
    public void executeTask(String taskId) {
        ScheduledTaskInfo taskInfo = scheduledTasks.get(taskId);
        if (taskInfo == null || !taskInfo.isActive()) {
            log.warn("定时任务不存在或已停用, taskId={}", taskId);
            return;
        }

        BaseExportRequest request = taskInfo.getRequest();
        log.info("执行定时导出任务, taskId={}, type={}", taskId, request);

        try {
            taskInfo.setLastRunAt(LocalDateTime.now());
            taskInfo.setNextRunAt(calculateNextRunTime(taskInfo.getCronExpression()));

            switch (taskInfo.getRequest().getClass().getSimpleName()) {
                case "OrderExportRequest":
                    reportExportService.exportOrders((OrderExportRequest) request);
                    break;
                case "GoodsExportRequest":
                    reportExportService.exportGoods((GoodsExportRequest) request);
                    break;
                case "InventoryExportRequest":
                    reportExportService.exportInventory((InventoryExportRequest) request);
                    break;
                case "SupplierExportRequest":
                    reportExportService.exportSuppliers((SupplierExportRequest) request);
                    break;
                default:
                    log.warn("未知导出请求类型, taskId={}", taskId);
            }

            log.info("定时导出任务执行成功, taskId={}", taskId);
        } catch (Exception e) {
            log.error("定时导出任务执行失败, taskId={}", taskId, e);
        }
    }

    /**
     * 每天凌晨检查并清理过期任务
     */
    @Scheduled(cron = "0 0 1 * * ?")
    public void cleanupExpiredTasks() {
        log.info("开始清理过期定时任务...");
        
        List<String> expiredTaskIds = scheduledTasks.entrySet().stream()
                .filter(entry -> {
                    ScheduledTaskInfo task = entry.getValue();
                    // 超过30天未运行的任务视为过期
                    return task.getLastRunAt() != null && 
                           task.getLastRunAt().isBefore(LocalDateTime.now().minusDays(30));
                })
                .map(Map.Entry::getKey)
                .toList();

        for (String taskId : expiredTaskIds) {
            cancelTask(taskId);
            log.info("清理过期任务, taskId={}", taskId);
        }
    }

    /**
     * 计算下次执行时间
     */
    private LocalDateTime calculateNextRunTime(String cronExpression) {
        try {
            CronTrigger trigger = new CronTrigger(cronExpression);
            java.util.Date next = trigger.nextExecutionTime(new TriggerContext() {
                @Override
                public java.time.Instant lastScheduledExecution() { return null; }
                @Override
                public java.time.Instant lastActualExecution() { return null; }
                @Override
                public java.time.Instant lastCompletion() { return null; }
            });
            return next != null ? next.toInstant().atZone(java.time.ZoneId.systemDefault()).toLocalDateTime() : null;
        } catch (Exception e) {
            log.error("计算下次执行时间失败, cron={}", cronExpression, e);
            return LocalDateTime.now().plusDays(1);
        }
    }

    /**
     * 定时任务信息
     */
    @lombok.Data
    @lombok.NoArgsConstructor
    @lombok.AllArgsConstructor
    public static class ScheduledTaskInfo {
        private String taskId;
        private Long tenantId;
        private String cronExpression;
        private BaseExportRequest request;
        private LocalDateTime createdAt;
        private LocalDateTime lastRunAt;
        private LocalDateTime nextRunAt;
        private boolean active;
        private int totalExecutions;
        private int successExecutions;
        private int failedExecutions;
    }
}
