package com.gameplatform.server.service.cooldown;

import com.gameplatform.server.mapper.cooldown.MachineCooldownMapper;
import com.gameplatform.server.model.entity.cooldown.MachineCooldown;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * 机器冷却服务 - 支持数据库持久化
 * 实现同一台机器在10分钟内不会重复调用的机制
 * 
 * 优化点：
 * 1. 数据库持久化，服务重启不丢失状态
 * 2. 双重缓存机制，提高查询性能
 * 3. 分布式锁支持，避免并发问题
 */
@Service
public class MachineCooldownService {
    private static final Logger log = LoggerFactory.getLogger(MachineCooldownService.class);

    // 冷却时间：10分钟
    private static final int COOLDOWN_MINUTES = 10;

    // 内存缓存：machineId -> 最后操作时间（用于快速查询）
    private final ConcurrentMap<String, LocalDateTime> machineCooldownCache = new ConcurrentHashMap<>();
    
    private final MachineCooldownMapper machineCooldownMapper;
    
    public MachineCooldownService(MachineCooldownMapper machineCooldownMapper) {
        this.machineCooldownMapper = machineCooldownMapper;
        // 启动时加载活跃的冷却记录到缓存
        loadActiveCooldownsToCache();
    }

    /**
     * 启动时加载活跃的冷却记录到缓存
     */
    private void loadActiveCooldownsToCache() {
        try {
            List<MachineCooldown> activeCooldowns = machineCooldownMapper.findExpiredActiveCooldowns(
                LocalDateTime.now().plusMinutes(COOLDOWN_MINUTES), 1000);
            
            for (MachineCooldown cooldown : activeCooldowns) {
                if (cooldown.isActive()) {
                    machineCooldownCache.put(cooldown.getMachineId(), cooldown.getCooldownStartTime());
                }
            }
            
            log.info("加载了 {} 个活跃冷却记录到缓存", activeCooldowns.size());
        } catch (Exception e) {
            log.error("加载冷却记录到缓存失败", e);
        }
    }

    /**
     * 检查机器是否在冷却期内（优先从数据库查询确保准确性）
     * @param machineId 机器ID
     * @return true表示在冷却期内，false表示可以操作
     */
    public boolean isMachineInCooldown(String machineId) {
        if (machineId == null || machineId.trim().isEmpty()) {
            return false;
        }

        try {
            // 首先查询数据库获取最准确的状态
            MachineCooldown activeCooldown = machineCooldownMapper.findActiveCooldownByMachineId(machineId);
            
            if (activeCooldown == null || !activeCooldown.isActive()) {
                // 数据库中没有活跃冷却记录，清理缓存
                machineCooldownCache.remove(machineId);
                log.debug("机器{}没有活跃冷却记录，允许操作", machineId);
                return false;
            }

            // 更新缓存
            machineCooldownCache.put(machineId, activeCooldown.getCooldownStartTime());
            
            long remainingMinutes = activeCooldown.getRemainingMinutes();
            log.info("机器{}在冷却期内，剩余冷却时间：{}分钟，原因：{}", 
                    machineId, remainingMinutes + 1, activeCooldown.getReason());
            
            return true;
            
        } catch (Exception e) {
            log.error("检查机器{}冷却状态时发生异常，默认允许操作", machineId, e);
            // 发生异常时，回退到缓存检查
            return isMachineInCooldownFromCache(machineId);
        }
    }
    
    /**
     * 从缓存检查机器冷却状态（备用方法）
     */
    private boolean isMachineInCooldownFromCache(String machineId) {
        LocalDateTime lastOperationTime = machineCooldownCache.get(machineId);
        if (lastOperationTime == null) {
            return false;
        }

        LocalDateTime now = LocalDateTime.now();
        LocalDateTime cooldownExpireTime = lastOperationTime.plusMinutes(COOLDOWN_MINUTES);
        boolean inCooldown = now.isBefore(cooldownExpireTime);
        
        if (!inCooldown) {
            // 冷却已过期，清理缓存
            machineCooldownCache.remove(machineId);
        }
        
        return inCooldown;
    }

    /**
     * 将机器加入冷却队列（支持关联链接任务）
     * @param machineId 机器ID
     * @param reason 加入冷却的原因
     */
    @Transactional
    public void addMachineToCooldown(String machineId, String reason) {
        try {
            addMachineToCooldown(machineId, reason, null);
        } catch (RuntimeException ex) {
            // 当上层已完成占用（或并发占用导致唯一键冲突）时，吞掉异常以便上层逻辑继续
            log.warn("添加冷却记录失败或已存在，machineId={}，原因={}（忽略）", machineId, ex.getMessage());
        }
    }
    
    /**
     * 将机器加入冷却队列
     * @param machineId 机器ID
     * @param reason 加入冷却的原因
     * @param linkTaskId 关联的链接任务ID（可选）
     */
    @Transactional
    public void addMachineToCooldown(String machineId, String reason, Long linkTaskId) {
        if (machineId == null || machineId.trim().isEmpty()) {
            log.warn("尝试添加空的机器ID到冷却队列");
            return;
        }

        LocalDateTime now = LocalDateTime.now();
        LocalDateTime cooldownEndTime = now.plusMinutes(COOLDOWN_MINUTES);
        
        try {
            // 先移除该设备现有的活跃冷却记录（若存在非并发场景下的残留）
            machineCooldownMapper.removeMachineCooldown(machineId);

            // 创建新的冷却记录（依赖数据库唯一约束防止并发重复占用）
            MachineCooldown cooldown = new MachineCooldown(
                machineId, now, cooldownEndTime, reason, linkTaskId);
            machineCooldownMapper.insert(cooldown);

            // 更新缓存
            machineCooldownCache.put(machineId, now);

            log.info("机器{}已加入冷却队列，原因：{}，冷却时间：{}分钟，冷却结束时间：{}，关联任务：{}",
                    machineId, reason, COOLDOWN_MINUTES, cooldownEndTime, linkTaskId);

        } catch (Exception e) {
            log.error("将机器{}加入冷却队列失败：{}", machineId, e.getMessage(), e);
            // 抛出异常，交由上层流程回滚/重试，避免同一设备被并发占用
            throw new RuntimeException("设备正忙或分配冲突，请稍后重试");
        }
    }

    /**
     * 获取机器剩余冷却时间（分钟）
     * @param machineId 机器ID
     * @return 剩余冷却时间，如果不在冷却期则返回0
     */
    public long getRemainingCooldownMinutes(String machineId) {
        if (machineId == null || machineId.trim().isEmpty()) {
            return 0;
        }

        try {
            MachineCooldown activeCooldown = machineCooldownMapper.findActiveCooldownByMachineId(machineId);
            if (activeCooldown != null && activeCooldown.isActive()) {
                return activeCooldown.getRemainingMinutes();
            }
        } catch (Exception e) {
            log.warn("查询机器{}剩余冷却时间失败，回退到缓存查询", machineId, e);
            // 回退到缓存查询
            LocalDateTime lastOperationTime = machineCooldownCache.get(machineId);
            if (lastOperationTime != null) {
                LocalDateTime now = LocalDateTime.now();
                LocalDateTime cooldownExpireTime = lastOperationTime.plusMinutes(COOLDOWN_MINUTES);
                if (now.isBefore(cooldownExpireTime)) {
                    return java.time.Duration.between(now, cooldownExpireTime).toMinutes() + 1;
                }
            }
        }

        return 0;
    }

    /**
     * 手动移除机器的冷却状态（用于测试或管理员操作）
     * @param machineId 机器ID
     */
    @Transactional
    public void removeMachineFromCooldown(String machineId) {
        if (machineId == null || machineId.trim().isEmpty()) {
            return;
        }

        try {
            int updated = machineCooldownMapper.removeMachineCooldown(machineId);
            if (updated > 0) {
                log.info("已手动移除机器{}的冷却状态", machineId);
            } else {
                log.debug("机器{}不在数据库冷却队列中", machineId);
            }
        } catch (Exception e) {
            log.error("手动移除机器{}冷却状态失败", machineId, e);
        }
        
        // 清理缓存
        LocalDateTime removedTime = machineCooldownCache.remove(machineId);
        if (removedTime != null) {
            log.debug("已从缓存中移除机器{}的冷却状态", machineId);
        }
    }

    /**
     * 清理过期的冷却记录（可定期调用以释放内存）
     */
    @Transactional
    public void cleanupExpiredCooldowns() {
        LocalDateTime now = LocalDateTime.now();
        
        try {
            // 批量更新数据库中过期的冷却记录
            int dbCleanedCount = machineCooldownMapper.batchUpdateExpiredCooldowns(now);
            
            // 清理缓存中过期的记录
            int cacheCleanedCount = 0;
            var iterator = machineCooldownCache.entrySet().iterator();
            while (iterator.hasNext()) {
                var entry = iterator.next();
                String machineId = entry.getKey();
                LocalDateTime lastOperationTime = entry.getValue();
                LocalDateTime cooldownExpireTime = lastOperationTime.plusMinutes(COOLDOWN_MINUTES);

                if (now.isAfter(cooldownExpireTime)) {
                    iterator.remove();
                    cacheCleanedCount++;
                }
            }

            if (dbCleanedCount > 0 || cacheCleanedCount > 0) {
                log.info("清理过期冷却记录完成：数据库{}个，缓存{}个", dbCleanedCount, cacheCleanedCount);
            }
            
        } catch (Exception e) {
            log.error("清理过期冷却记录失败", e);
        }
    }

    /**
     * 获取当前冷却队列的大小
     * @return 冷却队列中的机器数量
     */
    public int getCooldownQueueSize() {
        try {
            return (int) machineCooldownMapper.countActiveCooldowns();
        } catch (Exception e) {
            log.warn("查询活跃冷却记录数量失败，返回缓存大小", e);
            return machineCooldownCache.size();
        }
    }
    
    /**
     * 获取机器的冷却历史记录
     * @param machineId 机器ID
     * @param limit 返回记录数量限制
     * @return 冷却历史记录列表
     */
    public List<MachineCooldown> getCooldownHistory(String machineId, int limit) {
        if (machineId == null || machineId.trim().isEmpty()) {
            return List.of();
        }
        
        try {
            return machineCooldownMapper.getCooldownHistory(machineId, limit, 0);
        } catch (Exception e) {
            log.error("查询机器{}冷却历史失败", machineId, e);
            return List.of();
        }
    }
}
