package com.fs.watch.scheduled;

import com.fs.common.utils.DateUtils;
import com.fs.watch.domain.WatchMedicationTask;
import com.fs.watch.domain.WatchMsgSet;
import com.fs.watch.domain.WatchSendMsgLog;
import com.fs.watch.domain.WatchSendMsgSet;
import com.fs.watch.domain.vo.WatchUserAndHealthVo;
import com.fs.watch.domain.vo.WatchUserAndMedicationVo;
import com.fs.watch.param.WatchSetUpSendParam;
import com.fs.watch.service.*;
import com.fs.watch.service.impl.WatchSendMsgLogServiceImpl;
import com.fs.watch.utils.MyDateTimeUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.stereotype.Component;
import org.springframework.scheduling.support.CronTrigger;

import javax.annotation.PostConstruct;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.ScheduledFuture;
import java.util.stream.Collectors;

@Component
@Slf4j
public class DynamicSendMedicationTask {

    private final ThreadPoolTaskScheduler taskScheduler;
    private final WatchSendMsgSetService watchSendMsgSetService;
    private final WatchMedicationTaskService watchMedicationTaskService;
    private final WatchMsgSetService watchMsgSetService;
    private final DeviceSetUpService deviceSetUpService;
    private final ThreadPoolTaskExecutor threadPoolTaskExecutor;
    private final WatchSendMsgLogService watchSendMsgLogService;

    // 使用构造器注入
    @Autowired
    public DynamicSendMedicationTask(ThreadPoolTaskScheduler taskScheduler,
                                     WatchSendMsgSetService watchSendMsgSetService,
                                     WatchMedicationTaskService watchMedicationTaskService,
                                     WatchMsgSetService watchMsgSetService,
                                     DeviceSetUpService deviceSetUpService,
                                     ThreadPoolTaskExecutor threadPoolTaskExecutor,

                                     WatchSendMsgLogService watchSendMsgLogService) {
        this.taskScheduler = taskScheduler;
        this.watchSendMsgSetService = watchSendMsgSetService;
        this.watchMedicationTaskService = watchMedicationTaskService;
        this.watchMsgSetService = watchMsgSetService;
        this.deviceSetUpService = deviceSetUpService;
        this.threadPoolTaskExecutor = threadPoolTaskExecutor;
        log.info("ThreadPoolTaskScheduler 初始化完成");
        this.watchSendMsgLogService = watchSendMsgLogService;
    }

    private ScheduledFuture<?> startTask;
    private ScheduledFuture<?> endTask;
    private ScheduledFuture<?> periodicTask1;
    private ScheduledFuture<?> periodicTask2;
    private ScheduledFuture<?> periodicTask3;


    // 在项目启动时执行此方法，自动从数据库获取参数并执行调度任务
    @PostConstruct
    public void init() {
        // 从数据库获取参数，假设我们只查询一条记录
        WatchSendMsgSet set = watchSendMsgSetService.selectWatchSendMsgSetByType("medication");  // 根据你的实际情况修改
        if (set != null) {
            WatchSetUpSendParam param = new WatchSetUpSendParam();
            param.setFlag(set.getStatus() == 1);
            param.setStart(set.getStart());
            param.setEnd(set.getEnd());
            log.info("项目启动时，自动执行用药设置调度任务，参数: {}", param);
            scheduleTask(param);

        } else {
            log.warn("数据库未找到调度参数");
        }
    }

    /**
     * 调度任务，根据参数决定是开启还是关闭任务
     *
     * @param param 请求参数
     */
    public void scheduleTask(WatchSetUpSendParam param) {
        log.info("scheduleTask 被调用，参数: {}", param);

        // 参数校验
        if (param == null) {
            throw new IllegalArgumentException("参数不能为空");
        }

        if (!Boolean.TRUE.equals(param.getFlag())) {
            // 如果 flag 为 false，取消任务
            cancelAllTasks();
            return;
        }

        // flag 为 true，继续调度任务
        LocalTime startTime;
        LocalTime endTime;

        try {
            startTime = LocalTime.parse(param.getStart());
            endTime = LocalTime.parse(param.getEnd());
            log.info("解析后的开始时间: {}, 结束时间: {}", startTime, endTime);
        } catch (Exception e) {
            log.error("时间格式错误，正确格式为 'HH:mm:ss': {}", e.getMessage());
            throw new IllegalArgumentException("时间格式错误");
        }

        if (startTime.isAfter(endTime)) {
            throw new IllegalArgumentException("开始时间不能晚于结束时间");
        }

        // 取消之前的任务（如果有）
        cancelAllTasks();

        // 检查当前时间是否在开始时间和结束时间之间
        LocalTime now = LocalTime.now();
        if (now.isAfter(startTime) && now.isBefore(endTime)) {
            log.info("用药提醒当前时间在开始时间和结束时间之间，立即启动周期性任务");
            startPeriodicTask();
        }

        // 生成 CRON 表达式并调试输出
        log.info("生成的开始任务 CRON 表达式: {}", String.format("%d %d %d * * ?", startTime.getSecond(), startTime.getMinute(), startTime.getHour()));
        log.info("生成的结束任务 CRON 表达式: {}", String.format("%d %d %d * * ?", endTime.getSecond(), endTime.getMinute(), endTime.getHour()));

        // 调度每天开始和结束的任务
        startTask = taskScheduler.schedule(() -> {
            log.info("用药提醒开始任务触发，当前时间: {}", LocalTime.now());
            startPeriodicTask();
        }, toDailyTrigger(startTime));

        endTask = taskScheduler.schedule(() -> {
            log.info("用药提醒结束任务触发，当前时间: {}", LocalTime.now());
            stopPeriodicTask();
        }, toDailyTrigger(endTime));
    }

    private static String addMinutes(String timeStr, int minutesToAdd) {
        // 设置时间格式
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm:ss");
        // 解析传入的时间字符串
        LocalTime time = LocalTime.parse(timeStr, formatter);
        // 加上（或减去，如果minutesToAdd为负数）指定的分钟数
        LocalTime newTime = minutesToAdd >= 0 ? time.plusMinutes(minutesToAdd) : time.minusMinutes(-minutesToAdd);
        // 格式化新的时间并返回
        return newTime.format(formatter);
    }

    /**
     * 开始周期性任务
     */
    private void startPeriodicTask() {
        // 从数据库获取参数，假设我们只查询一条记录
        WatchSendMsgSet set = watchSendMsgSetService.selectWatchSendMsgSetByType("medication");
        if (periodicTask1 == null || periodicTask1.isCancelled()) {
            log.info("第一次用药提醒周期性任务未启动，开始启动...");
            String time = addMinutes(set.getStart(), 2);//开始后两分钟执行
            CronTrigger trigger1 = toDailyCronTrigger(LocalTime.parse(time));
            periodicTask1 = taskScheduler.schedule(this::sendMedication1, trigger1);
        } else {
            System.out.println("第一次用药提醒周期性任务已经存在且正在运行，当前时间: " + LocalTime.now());
        }
        if (periodicTask2 == null || periodicTask2.isCancelled()) {
            log.info("第二次用药提醒周期性任务未启动，开始启动...");
            CronTrigger trigger2 = new CronTrigger("0 0 12 * * ?");
            periodicTask2 = taskScheduler.schedule(this::sendMedication2, trigger2);
        } else {
            System.out.println("第二次用药提醒周期性任务已经存在且正在运行，当前时间: " + LocalTime.now());
        }
        if (periodicTask3 == null || periodicTask3.isCancelled()) {
            log.info("第三次用药提醒周期性任务未启动，开始启动...");
            String time = addMinutes(set.getEnd(), -60);//结束前1小时执行
            CronTrigger trigger3 = toDailyCronTrigger(LocalTime.parse(time));
            periodicTask3 = taskScheduler.schedule(this::sendMedication3, trigger3);
        } else {
            System.out.println("第三次用药提醒周期性任务已经存在且正在运行，当前时间: " + LocalTime.now());
        }
    }

    /**
     * 停止周期性任务
     */
    private void stopPeriodicTask() {
        if (periodicTask1 != null) {
            periodicTask1.cancel(false);
            periodicTask1 = null;
            log.info("第一次用药提醒周期性任务已停止，当前时间: {}", LocalTime.now());
        }
        if (periodicTask2 != null) {
            periodicTask2.cancel(false);
            periodicTask2 = null;
            log.info("第二次用药提醒周期性任务已停止，当前时间: {}", LocalTime.now());
        }
        if (periodicTask3 != null) {
            periodicTask3.cancel(false);
            periodicTask3 = null;
            log.info("第三次用药提醒周期性任务已停止，当前时间: {}", LocalTime.now());
        }
    }

    /**
     * 取消所有任务
     */
    private void cancelAllTasks() {
        if (startTask != null) {
            startTask.cancel(false);
            startTask = null;
        }
        if (endTask != null) {
            endTask.cancel(false);
            endTask = null;
        }
        stopPeriodicTask(); // 同时停止周期性任务
    }

    /**
     * 用药提醒的任务逻辑 没有服用剂量 即暂时提醒三天 一天最多提醒三次
     */
    private void sendMedication1() {
        log.info("用药提醒任务执行... 当前时间: {}", LocalTime.now());
        //1.查询需要发送的设备
        List<WatchUserAndMedicationVo> vos = new ArrayList<>(watchMedicationTaskService.medicationTaskList(1));
        sendMsg(vos);
    }

    /**
     * 第二次提醒
     */
    private void sendMedication2() {
        log.info("用药提醒任务执行... 当前时间: {}", LocalTime.now());
        //1.查询需要发送的设备
        List<WatchUserAndMedicationVo> vos = new ArrayList<>(watchMedicationTaskService.medicationTaskList(2));
        sendMsg(vos);
    }

    private void sendMsg(List<WatchUserAndMedicationVo> vos) {
        //2.获取提醒消息(目前只有一条)
        List<WatchMsgSet> watchMsgSets = watchMsgSetService.selectWatchMsgSetList(new WatchMsgSet(0, 1)); // 查询用药提醒设置
        WatchMsgSet msgSet = watchMsgSets.get(0);
        //3.发送消息
        if (!vos.isEmpty() && msgSet != null) {
            vos.forEach(vo ->
                    threadPoolTaskExecutor.execute(() -> {
                                long startTime = System.currentTimeMillis();
                                final String deviceId = vo.getDeviceId();
                                //查询是否已经发送
                                WatchSendMsgLog watchSendMsgLog = watchSendMsgLogService.selectLastByMsgTypeAndDeviceIdAndDetailsType(1, deviceId, vo.getProductName());
                                if (watchSendMsgLog != null) {
                                    if (watchSendMsgLog.getStatus() == 0) {
                                        Long hour = MyDateTimeUtils.getTimeDif(watchSendMsgLog.getOperTime(), DateUtils.getNowDate(), "hour");
                                        //大于2小时 才提醒
                                        if (hour != null) {
                                            if (hour < 2) return;
                                        }
                                    }
                                }
                                try {
                                    send(vo, msgSet);
                                } catch (Exception e) {
                                    log.error("Error processing message for device {}: {}", vo.getDeviceId(), e.getMessage(), e);
                                } finally {
                                    log.info("MedicationTask for device {} completed in {} ms", deviceId, System.currentTimeMillis() - startTime);
                                }
                            }

                    )
            );
        }
    }

    /**
     * 第三次提醒
     */
    private void sendMedication3() {
        log.info("用药提醒任务执行... 当前时间: {}", LocalTime.now());
        //1.查询需要发送的设备
        List<WatchUserAndMedicationVo> vos = new ArrayList<>(watchMedicationTaskService.medicationTaskList(3));
        sendMsg(vos);
    }

    /**
     * 发送消息
     *
     * @param vo
     * @param msgSet
     */
    private void send(WatchUserAndMedicationVo vo, WatchMsgSet msgSet) {
        String deviceIdsStr = vo.getDeviceId();
        List<String> deviceIds = Arrays.stream(deviceIdsStr.split(",")).collect(Collectors.toList());
        deviceIds.forEach(deviceId ->
            deviceSetUpService.message(deviceId, msgSet.getMsgTitle(), msgSet.getMsgDescription() + vo.getProductName(), "medication", 3, null, vo.getProductName())
        );
        //修改腕表用药提醒表
        WatchMedicationTask watchMedicationTask = new WatchMedicationTask();
        BeanUtils.copyProperties(vo, watchMedicationTask);
        watchMedicationTask.setActualNum(watchMedicationTask.getActualNum() + 1);
        if (Objects.equals(watchMedicationTask.getActualNum(), watchMedicationTask.getRequiredNum())) {
            watchMedicationTask.setStatus(1);
        }
        watchMedicationTaskService.updateWatchMedicationTask(watchMedicationTask);

    }

    /**
     * 将 LocalTime 转换为每日触发器
     *
     * @param time 目标时间
     * @return Runnable 的触发时间
     */
    private Trigger toDailyTrigger(LocalTime time) {
        // Cron 表达式格式: 秒 分 时 日 月 星期
        String cronExpression = String.format("%d %d %d * * ?",
                time.getSecond(),
                time.getMinute(),
                time.getHour());
        return new CronTrigger(cronExpression);
    }

    private CronTrigger toDailyCronTrigger(LocalTime time) {
        // Cron 表达式格式: 秒 分 时 日 月 星期
        String cronExpression = String.format("%d %d %d * * ?",
                time.getSecond(),
                time.getMinute(),
                time.getHour());
        return new CronTrigger(cronExpression);
    }
}
