package com.common.service.impl;



import com.common.entity.AnalysisAnalysisRule;
import com.common.entity.AnalysisGroupByWindow;
import com.common.service.RuleExecutionTimeService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Set;
import  java.time.temporal.ChronoUnit;
/**
 * 规则执行时间管理服务实现（基于Redis）
 * 根据窗口类型（滚动、滑动、会话）动态计算下次执行时间
 */
@Slf4j
@Service
public class RuleExecutionTimeServiceImpl implements RuleExecutionTimeService {

    private static final String REDIS_KEY_PREFIX = "rule:next_execute:";
    private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Override
    public LocalDateTime getNextExecuteTime(String ruleId) {
        String key = REDIS_KEY_PREFIX + ruleId;
        String value = redisTemplate.opsForValue().get(key);

        if (value == null) {
            log.debug("规则首次执行，无下次执行时间记录，ruleId={}", ruleId);
            return null;
        }

        try {
            return LocalDateTime.parse(value, DATE_FORMATTER);
        } catch (Exception e) {
            log.error("解析下次执行时间失败，ruleId={}, value={}", ruleId, value, e);
            return null;
        }
    }

    @Override
    public void updateNextExecuteTime(AnalysisAnalysisRule rule, AnalysisGroupByWindow groupByWindow) {
        LocalDateTime nextTime = calculateNextExecuteTime(rule, groupByWindow);
        String ruleId = rule.getRuleId();

        String key = REDIS_KEY_PREFIX + ruleId;
        String value = nextTime.format(DATE_FORMATTER);

        // 设置过期时间：5天
        redisTemplate.opsForValue().set(key, value, Duration.ofDays(5));

        log.info("更新规则下次执行时间，ruleId={}, ruleName={}, windowType={}, nextExecuteTime={}",
                ruleId, rule.getRuleName(),
                groupByWindow != null ? groupByWindow.getWindowType() : "NONE",
                value);
    }

    @Override
    public void initRuleExecuteTime(AnalysisAnalysisRule rule, AnalysisGroupByWindow groupByWindow) {
        String ruleId = rule.getRuleId();
        String key = REDIS_KEY_PREFIX + ruleId;

        // 如果已存在，则不覆盖（防止重启后重新计算）
        if (redisTemplate.hasKey(key)) {
            log.info("规则执行时间已存在，跳过初始化，ruleId={}", ruleId);
            return;
        }

        // 计算初始执行时间（当前时间 + 窗口间隔）
        LocalDateTime nextTime = calculateNextExecuteTime(rule, groupByWindow);
        String value = nextTime.format(DATE_FORMATTER);

        redisTemplate.opsForValue().set(key, value, Duration.ofDays(5));

        log.info("初始化规则执行时间，ruleId={}, ruleName={}, windowType={}, nextExecuteTime={}",
                ruleId, rule.getRuleName(),
                groupByWindow != null ? groupByWindow.getWindowType() : "NONE",
                value);
    }

    @Override
    public void removeRuleExecuteTime(String ruleId) {
        String key = REDIS_KEY_PREFIX + ruleId;
        redisTemplate.delete(key);
        log.debug("删除规则执行时间记录，ruleId={}", ruleId);
    }

    @Override
    public void clearAllRuleExecuteTime() {
        String pattern = REDIS_KEY_PREFIX + "*";
        Set<String> keys = redisTemplate.keys(pattern);

        if (keys != null && !keys.isEmpty()) {
            redisTemplate.delete(keys);
            log.info("清空所有规则执行时间，共 {} 条", keys.size());
        }
    }

    /**
     * 根据窗口类型动态计算下次执行时间
     *
     * @param rule 分析规则
     * @param groupByWindow 窗口配置
     * @return 下次执行时间
     */
    private LocalDateTime calculateNextExecuteTime(AnalysisAnalysisRule rule, AnalysisGroupByWindow groupByWindow) {
        LocalDateTime now = LocalDateTime.now().truncatedTo(ChronoUnit.MINUTES);

        // 如果没有配置窗口，使用默认30秒
        if (groupByWindow == null) {
            log.warn("未配置窗口类型，使用默认执行间隔：60秒");
            return now.plusSeconds(60);
        }

        String windowType = groupByWindow.getWindowType();

        if (windowType == null || windowType.trim().isEmpty()) {
            log.warn("窗口类型为空，使用默认执行间隔：60秒");
            return now.plusSeconds(60);
        }

        switch (windowType.toUpperCase()) {
            case "TUMBLE":
                return calculateTumbleNextExecuteTime(now, groupByWindow);

            case "HOP":
                return calculateHopNextExecuteTime(now, groupByWindow);

            case "SESSION":
                return calculateSessionNextExecuteTime(now, groupByWindow);

            default:
                log.warn("未知窗口类型: {}，使用默认执行间隔：60秒", windowType);
                return now.plusSeconds(60);
        }
    }

    /**
     * 计算滚动窗口的下次执行时间
     *
     * 策略：执行间隔 = 窗口大小
     * 示例：窗口大小5分钟，则每5分钟执行一次
     */
    private LocalDateTime calculateTumbleNextExecuteTime(LocalDateTime now, AnalysisGroupByWindow groupByWindow) {
        Integer windowSize = groupByWindow.getTumbleWindowSize();
        String windowSizeUnit = groupByWindow.getTumbleWindowSizeUnit();

        if (windowSize == null || windowSize <= 0) {
            log.warn("滚动窗口大小配置无效，使用默认值：5分钟");
            windowSize = 5;
            windowSizeUnit = "m";
        }

        if (windowSizeUnit == null || windowSizeUnit.trim().isEmpty()) {
            windowSizeUnit = "m";
        }

        LocalDateTime nextTime;
        switch (windowSizeUnit.toLowerCase()) {
            case "s":
                nextTime = now.plusSeconds(windowSize);
                break;
            case "m":
                nextTime = now.plusMinutes(windowSize);
                break;
            case "h":
                nextTime = now.plusHours(windowSize);
                break;
            case "d":
                nextTime = now.plusDays(windowSize);
                break;
            default:
                log.warn("滚动窗口单位无效: {}，使用默认单位：分钟", windowSizeUnit);
                nextTime = now.plusMinutes(windowSize);
        }

        log.debug("滚动窗口下次执行时间: 窗口大小={}{}，nextTime={}",
                windowSize, windowSizeUnit, nextTime.format(DATE_FORMATTER));

        return nextTime;
    }

    /**
     * 计算滑动窗口的下次执行时间
     *
     * 策略：执行间隔 = 滑动步长（slide）
     * 示例：窗口大小10分钟，步长5分钟，则每5分钟执行一次
     */
    private LocalDateTime calculateHopNextExecuteTime(LocalDateTime now, AnalysisGroupByWindow groupByWindow) {
        Integer slide = groupByWindow.getHopWindowSlide();
        String slideUnit = groupByWindow.getHopWindowSizeUnit();

        if (slide == null || slide <= 0) {
            log.warn("滑动窗口步长配置无效，使用默认值：5分钟");
            slide = 5;
            slideUnit = "m";
        }

        if (slideUnit == null || slideUnit.trim().isEmpty()) {
            slideUnit = "m";
        }

        LocalDateTime nextTime;
        switch (slideUnit.toLowerCase()) {
            case "s":
                nextTime = now.plusSeconds(slide);
                break;
            case "m":
                nextTime = now.plusMinutes(slide);
                break;
            case "h":
                nextTime = now.plusHours(slide);
                break;
            case "d":
                nextTime = now.plusDays(slide);
                break;
            default:
                log.warn("滑动窗口步长单位无效: {}，使用默认单位：分钟", slideUnit);
                nextTime = now.plusMinutes(slide);
        }

        log.debug("滑动窗口下次执行时间: 步长={}{}，nextTime={}",
                slide, slideUnit, nextTime.format(DATE_FORMATTER));

        return nextTime;
    }

    /**
     * 计算会话窗口的下次执行时间
     *
     * 策略：执行间隔 = 会话超时时间（session window size）
     * 示例：超时时间30分钟，则每30分钟执行一次
     */
    private LocalDateTime calculateSessionNextExecuteTime(LocalDateTime now, AnalysisGroupByWindow groupByWindow) {
        Integer sessionTimeout = groupByWindow.getSessionWindowSize();
        String sessionTimeoutUnit = groupByWindow.getSessionWindowSizeUnit();

        if (sessionTimeout == null || sessionTimeout <= 0) {
            log.warn("会话窗口超时时间配置无效，使用默认值：30分钟");
            sessionTimeout = 30;
            sessionTimeoutUnit = "m";
        }

        if (sessionTimeoutUnit == null || sessionTimeoutUnit.trim().isEmpty()) {
            sessionTimeoutUnit = "m";
        }

        LocalDateTime nextTime;
        switch (sessionTimeoutUnit.toLowerCase()) {
            case "s":
                nextTime = now.plusSeconds(sessionTimeout);
                break;
            case "m":
                nextTime = now.plusMinutes(sessionTimeout);
                break;
            case "h":
                nextTime = now.plusHours(sessionTimeout);
                break;
            case "d":
                nextTime = now.plusDays(sessionTimeout);
                break;
            default:
                log.warn("会话窗口超时单位无效: {}，使用默认单位：分钟", sessionTimeoutUnit);
                nextTime = now.plusMinutes(sessionTimeout);
        }

        log.debug("会话窗口下次执行时间: 超时时间={}{}，nextTime={}",
                sessionTimeout, sessionTimeoutUnit, nextTime.format(DATE_FORMATTER));

        return nextTime;
    }
}
