package com.gameplatform.server.service.device;

import com.gameplatform.server.mapper.agent.LinkTaskMapper;
import com.gameplatform.server.model.entity.agent.LinkTask;
import com.gameplatform.server.service.admin.SystemConfigService;
import com.gameplatform.server.service.external.ScriptClient;
import com.gameplatform.server.service.detection.GameCompletionDetectionService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import reactor.core.Exceptions;
import reactor.core.publisher.Mono;

import java.time.LocalDateTime;
import java.time.Duration;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeoutException;

@Service
@Slf4j
public class DeviceStatusCheckService {

    private final ScriptClient scriptClient;
    private final LinkTaskMapper linkTaskMapper;
    private final SystemConfigService systemConfigService;
    private final GameCompletionDetectionService completionDetectionService;

    public DeviceStatusCheckService(ScriptClient scriptClient, LinkTaskMapper linkTaskMapper, SystemConfigService systemConfigService, GameCompletionDetectionService completionDetectionService) {
        this.scriptClient = scriptClient;
        this.linkTaskMapper = linkTaskMapper;
        this.systemConfigService = systemConfigService;
        this.completionDetectionService = completionDetectionService;
    }

    /**
     * 检查设备状态并更新相关链接任务
     * @param machineId 设备ID
     * @param reason 检查原因（定时检查 或 选区请求）
     */
    @Transactional
    public void checkDeviceStatusAndUpdateTasks(String machineId, String reason) {
        log.debug("检查设备状态: 设备ID={}, 检查原因={}", machineId, reason);
        
        try {
            // 1. 获取设备状态（增加超时与错误兜底）
            Map<String, Object> deviceStatus = scriptClient
                    .getDeviceStatus(machineId)
                    .timeout(Duration.ofSeconds(5))
                    .onErrorResume(throwable -> {
                        Throwable root = Exceptions.unwrap(throwable);
                        if (root instanceof TimeoutException) {
                            log.warn("获取设备状态超时，设备ID: {}", machineId);
                        } else if (root instanceof InterruptedException) {
                            Thread.currentThread().interrupt();
                            log.warn("获取设备状态被中断，设备ID: {}", machineId);
                        } else {
                            log.error("获取设备状态失败，设备ID: {}", machineId, root);
                        }
                        return Mono.empty();
                    })
                    .blockOptional()
                    .orElse(null);
            if (deviceStatus == null || deviceStatus.isEmpty()) {
                log.warn("获取设备状态失败，设备ID: {}", machineId);
                return;
            }

            // 2. 解析设备状态
            DeviceStatusInfo statusInfo = parseDeviceStatus(deviceStatus);
            log.debug("设备 {} 状态解析结果: {}", machineId, statusInfo);
            
            // 3. 如果设备空闲，通过完成检测服务进行判定（含缓冲与二次确认）。
            // 若未能直接判定完成，则执行兜底：将该设备上 LOGGED_IN 的任务批量置为 COMPLETED，并写入原因。
            if (statusInfo.isIdle()) {
                String source = "TIMER_TASK";
                com.gameplatform.server.util.AuditLogger.debug("IdleDetected: device={}, source={}, status={}, points={}", machineId, source, statusInfo.getStatus(), statusInfo.getPoints());
                // 调用完成检测服务（包含登录缓冲与二次确认）
                boolean done = completionDetectionService.detectGameCompletion(machineId, statusInfo.getStatus(), source);
                if (!done) {
                    // 兜底完成：当设备空闲，直接将 LOGGED_IN 任务置为完成
                    String completionReason = "状态：已完成（空闲兜底）";
                    Integer points = statusInfo.getPoints();
                    try {
                        int affected = linkTaskMapper.completeLoggedInTasksByMachine(machineId, completionReason, points);
                        if (affected > 0) {
                            log.info("空闲兜底：设备 {} 完成了 {} 个任务，reason='{}' points={} ", machineId, affected, completionReason, points);
                        } else {
                            log.debug("空闲兜底：设备 {} 无需更新（无 LOGGED_IN 任务）", machineId);
                        }
                    } catch (Exception e) {
                        log.warn("空闲兜底完成失败 device={} err={}", machineId, e.getMessage());
                    }
                }
            } else {
                log.debug("设备 {} 状态为 [{}]，非空闲状态，跳过检查", machineId, statusInfo.getStatus());
            }
            
        } catch (Exception e) {
            Throwable root = Exceptions.unwrap(e);
            if (root instanceof InterruptedException) {
                Thread.currentThread().interrupt();
                log.warn("检查设备状态被中断，设备ID: {}", machineId);
                return;
            }
            log.error("检查设备状态时发生异常，设备ID: {}", machineId, e);
        }
    }

    /**
     * 解析设备状态响应
     */
    private DeviceStatusInfo parseDeviceStatus(Map<String, Object> deviceStatus) {
        DeviceStatusInfo info = new DeviceStatusInfo();
        
        try {
            // 解析 f0 (点数)
            Object f0Obj = deviceStatus.get("f0");
            if (f0Obj instanceof Map) {
                @SuppressWarnings("unchecked")
                Map<String, Object> f0 = (Map<String, Object>) f0Obj;
                if (f0.get("val") != null) {
                    String pointsStr = f0.get("val").toString();
                    info.setPoints(Integer.parseInt(pointsStr));
                }
            }
            
            // 解析 f1 (状态)
            Object f1Obj = deviceStatus.get("f1");
            if (f1Obj instanceof Map) {
                @SuppressWarnings("unchecked")
                Map<String, Object> f1 = (Map<String, Object>) f1Obj;
                if (f1.get("val") != null) {
                    String status = f1.get("val").toString();
                    info.setStatus(status);
                    String idleStatus = systemConfigService.getDeviceIdleStatus();
                    info.setIdle(idleStatus.equals(status));
                }
            }
            
        } catch (Exception e) {
            log.error("解析设备状态时发生异常", e);
        }
        
        return info;
    }

    /**
     * 更新已完成的任务
     */
    private void updateCompletedTasks(String machineId, Integer points) {
        // 查找使用该设备且状态为LOGGED_IN的链接任务
        List<LinkTask> loggedInTasks = linkTaskMapper.findByMachineIdAndStatus(machineId, "LOGGED_IN");
        
        if (loggedInTasks.isEmpty()) {
            log.debug("设备 {} 没有处于LOGGED_IN状态的链接任务，无需更新", machineId);
            return;
        }
        
        // 只有找到需要更新的任务时才输出INFO日志
        log.info("设备 {} 处于空闲状态，发现 {} 个LOGGED_IN状态的链接任务，开始更新为完成状态", machineId, loggedInTasks.size());
        
        // 更新所有相关任务为COMPLETED状态
        for (LinkTask task : loggedInTasks) {
            try {
                task.setStatus("COMPLETED");
                task.setReason("设备空闲兜底完成");
                task.setCompletedPoints(points);
                task.setUpdatedAt(LocalDateTime.now());
                
                int updated = linkTaskMapper.updateById(task);
                if (updated > 0) {
                    log.info("链接任务 {} (代码: {}) 已标记为完成，完成点数: {}", 
                            task.getId(), task.getCodeNo(), points);
                } else {
                    log.warn("更新链接任务 {} 失败", task.getId());
                }
                
            } catch (Exception e) {
                log.error("更新链接任务 {} 时发生异常", task.getId(), e);
            }
        }
    }

    /**
     * 设备状态信息
     */
    private static class DeviceStatusInfo {
        private String status;
        private Integer points;
        private boolean idle;

        public String getStatus() { return status; }
        public void setStatus(String status) { this.status = status; }

        public Integer getPoints() { return points; }
        public void setPoints(Integer points) { this.points = points; }

        public boolean isIdle() { return idle; }
        public void setIdle(boolean idle) { this.idle = idle; }

        @Override
        public String toString() {
            return String.format("DeviceStatusInfo{status='%s', points=%d, idle=%s}", 
                    status, points, idle);
        }
    }
}
