package com.gameplatform.server.device;

import com.gameplatform.server.model.dto.device.DeviceStatusResponse;
import com.gameplatform.server.model.entity.agent.LinkTask;
import com.gameplatform.server.model.entity.history.DeviceStatusTransition;
import com.gameplatform.server.mapper.history.LinkTaskStatusHistoryMapper;
import com.gameplatform.server.model.entity.history.LinkTaskStatusHistory;
import com.gameplatform.server.service.cooldown.MemoryMachineCooldownService;
import com.gameplatform.server.service.link.DeviceAllocationService;
import com.gameplatform.server.mapper.agent.LinkTaskMapper;
import com.gameplatform.server.mapper.history.DeviceStatusTransitionMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.gameplatform.server.service.admin.SystemConfigService;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.time.LocalDateTime;
import java.time.Duration;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;

/**
 * 设备状态分组统计：
 * - RUNNING：已上号（LOGGED_IN 任务或脚本返回“已运行”）
 * - USING：登录中/选区中（USING 任务或脚本返回数字/“正在登录中”）
 * - IDLE_COOLDOWN：处于冷却或脚本返回“已打完”（刚完成）
 * - IDLE_FREE：脚本返回“空的/空闲”或其他均视为空闲可用
 */
@Service
public class DeviceStats {
    // 设备分组统计日志记录器
    private static final Logger log = LoggerFactory.getLogger(DeviceStats.class);
    // 审计专用日志（输出到独立文件）
    private static final Logger auditLog = LoggerFactory.getLogger("com.gameplatform.server.audit");

    public enum Category {
        RUNNING,
        USING,
        IDLE_COOLDOWN,
        IDLE_FREE
    }

    public static final class Snapshot {
        private final Map<Category, List<String>> categoryToDevices;
        private final int totalDevices;
        private final int runningCount;
        private final int usingCount;
        private final int idleCooldownCount;
        private final int idleFreeCount;

        public Snapshot(Map<Category, List<String>> categoryToDevices) {
            this.categoryToDevices = categoryToDevices;
            this.totalDevices = categoryToDevices.values().stream().mapToInt(List::size).sum();
            this.runningCount = categoryToDevices.getOrDefault(Category.RUNNING, List.of()).size();
            this.usingCount = categoryToDevices.getOrDefault(Category.USING, List.of()).size();
            this.idleCooldownCount = categoryToDevices.getOrDefault(Category.IDLE_COOLDOWN, List.of()).size();
            this.idleFreeCount = categoryToDevices.getOrDefault(Category.IDLE_FREE, List.of()).size();
        }

        public Map<Category, List<String>> getCategoryToDevices() { return categoryToDevices; }
        public int getTotalDevices() { return totalDevices; }
        public int getRunningCount() { return runningCount; }
        public int getUsingCount() { return usingCount; }
        public int getIdleCooldownCount() { return idleCooldownCount; }
        public int getIdleFreeCount() { return idleFreeCount; }
    }

    private final LinkTaskMapper linkTaskMapper;
    private final MemoryMachineCooldownService cooldownService;
    private final DeviceStatusTransitionMapper transitionMapper;
    private final DeviceAllocationService deviceAllocationService;
    private final LinkTaskStatusHistoryMapper statusHistoryMapper;
    private final SystemConfigService systemConfigService;

    // 记录上一次统计时每台设备的分类结果，用于检测状态变更
    private final Map<String, Category> lastStatusByDevice = new ConcurrentHashMap<>();

    public DeviceStats(LinkTaskMapper linkTaskMapper,
                       MemoryMachineCooldownService cooldownService,
                       DeviceStatusTransitionMapper transitionMapper,
                       DeviceAllocationService deviceAllocationService,
                       SystemConfigService systemConfigService,
                       LinkTaskStatusHistoryMapper statusHistoryMapper) {
        this.linkTaskMapper = linkTaskMapper;
        this.cooldownService = cooldownService;
        this.transitionMapper = transitionMapper;
        this.deviceAllocationService = deviceAllocationService;
        this.systemConfigService = systemConfigService;
        this.statusHistoryMapper = statusHistoryMapper;
    }

    /**
     * 根据外部传入的全量设备快照进行分组统计，并更新内存中的状态与变更记录。
     * 分类优先级（高→低）：
     * 1) RUNNING：存在 LOGGED_IN 任务 或 脚本返回“已运行”
     * 2) USING：存在 USING 任务 或 脚本返回“正在登录中/数字（进度/积分）”
     * 3) IDLE_COOLDOWN：脚本返回“已打完” 或 处于冷却服务中
     * 4) IDLE_FREE：其余所有（包括脚本返回“空的/空闲/空字符串/未知”）
     */
    public Snapshot updateWithSnapshot(DeviceStatusResponse snapshot) {
        Map<String, DeviceStatusResponse.DeviceInfo> devices = snapshot != null ? snapshot.getDevices() : Collections.emptyMap();
        if (devices == null) {
            devices = Collections.emptyMap();
        }
        log.info("接收设备快照：设备总数={}", devices.size());

        // 初始化分类容器
        Map<Category, List<String>> bucket = new EnumMap<>(Category.class);
        for (Category c : Category.values()) {
            bucket.put(c, new ArrayList<>());
        }

        for (String deviceId : devices.keySet()) {
            DeviceStatusResponse.DeviceInfo info = devices.get(deviceId);
            String val = info != null ? info.getVal() : null;
            String v = val != null ? val.trim() : null;

            // 预先计算判定条件，便于统一记录日志
            boolean loggedIn = hasLoggedInTask(deviceId);
            boolean usingTask = hasUsingTask(deviceId);
            boolean cooldown = cooldownService.isMachineInCooldown(deviceId);
            boolean numeric = isNumeric(v);
            String configuredIdle = systemConfigService.getDeviceIdleStatus();

//            log.debug("设备[{}] 原始脚本值='{}' | LOGGED_IN={} USING={} COOLDOWN={} NUMERIC={}",
//                    deviceId, v, loggedIn, usingTask, cooldown, numeric);

            // 当脚本值为配置的空闲标识时，若设备上存在 LOGGED_IN 且登录超过3分钟的任务，则自动完成这些任务
            if (v != null && configuredIdle != null && configuredIdle.equals(v)) {
                try {
                    int completed = autoCompleteLoggedInTasksIfIdleOver3m(deviceId);
                    if (completed > 0) {
                        // 任务状态可能已变化，重新评估 loggedIn
                        loggedIn = hasLoggedInTask(deviceId);
                    }
                } catch (Exception e) {
                    log.warn("autoCompleteLoggedInTasksIfIdleOver3m 执行异常 device={} err={}", deviceId, e.getMessage());
                }
            }

            // 分类与原因
            Category newCategory;
            String reason;
            if (loggedIn || "已运行".equals(v)) {
                newCategory = Category.RUNNING;
                reason = loggedIn ? "存在LOGGED_IN任务" : "脚本值=已运行";
            } else if (usingTask || "正在登录中".equals(v) || numeric) {
                newCategory = Category.USING;
                reason = usingTask ? "存在USING任务" : ("正在登录中".equals(v) ? "脚本值=正在登录中" : "脚本值为数字");
            } else if ("已打完".equals(v) || cooldown) {
                newCategory = Category.IDLE_COOLDOWN;
                reason = "已打完".equals(v) ? "脚本值=已打完" : "处于冷却服务中";
            } else if (v != null && configuredIdle != null && configuredIdle.equals(v)) {
                newCategory = Category.IDLE_FREE;
                reason = "脚本值=配置的空闲标识";
            } else {
                newCategory = Category.RUNNING;
                reason = "默认运行中/未知状态";
            }

            // 放入桶
            bucket.get(newCategory).add(deviceId);
//            log.debug("设备[{}] 归类为 {}，原因={}", deviceId, newCategory, reason);

            // 检测并记录状态变更
            Category prevCategory = lastStatusByDevice.get(deviceId);
            boolean changed = prevCategory != null && prevCategory != newCategory;
            if (changed) {
                LocalDateTime occurredAt = parseTimeOrNow(info != null ? info.getTime() : null);
                String series = info != null ? info.getSeries() : null;
                Integer indexNo = info != null ? info.getIndex() : null;

                // 审计日志（独立文件）
                auditLog.info("device={} prev={} next={} reason={} val='{}' at={}",
                        deviceId,
                        prevCategory != null ? prevCategory.name() : null,
                        newCategory.name(),
                        reason,
                        v,
                        occurredAt);

                // 入库（仅状态变化时）
                DeviceStatusTransition t = new DeviceStatusTransition(
                        deviceId,
                        prevCategory != null ? prevCategory.name() : null,
                        newCategory.name(),
                        reason,
                        v,
                        series,
                        indexNo,
                        occurredAt
                );
                try {
                    transitionMapper.insert(t);
                } catch (Exception e) {
                    log.error("写入设备状态变更失败 device={} prev={} next={} err={}",
                            deviceId,
                            prevCategory != null ? prevCategory.name() : null,
                            newCategory.name(),
                            e.getMessage(), e);
                }
            }

            // 无论是否变化，都更新内存快照为最新分类
            lastStatusByDevice.put(deviceId, newCategory);
        }

        Snapshot result = new Snapshot(bucket);
        log.info("设备分组统计完成：total={} running={} using={} idleCooldown={} idleFree={}",
                result.getTotalDevices(), result.getRunningCount(), result.getUsingCount(),
                result.getIdleCooldownCount(), result.getIdleFreeCount());
        this.lastComputedSnapshot = result;
        return result;
    }

    // 最近一次分组统计结果（供查询展示）
    private volatile Snapshot lastComputedSnapshot;

    public Snapshot getLastComputedSnapshot() {
        return lastComputedSnapshot;
    }

    /**
     * 基于传入的快照进行设备分配，并立即将该设备的分组状态切换为 USING（内存与审计）。
     * 注意：数据库中 LinkTask 的状态更新与原子占用仍由上层调用完成（reserveDeviceIfFree）。
     * 这里仅完成：
     * 1) 使用 DeviceAllocationService 基于快照可用列表进行原子分配（冷却+并发安全）
     * 2) 如分配成功，在本地分类快照中将该设备标记为 USING，并记录 DeviceStatusTransition 审计
     *
     * @param snapshot 全量设备快照
     * @param linkTaskId 链接任务ID
     * @param reason 分配原因（审计用途）
     * @return 分配成功的设备ID；若无可分配设备则返回 null
     */
    public String allocateFromSnapshot(DeviceStatusResponse snapshot, Long linkTaskId, String reason) {
        if (snapshot == null || snapshot.getAvailableDevices() == null || snapshot.getAvailableDevices().isEmpty()) {
            log.warn("allocateFromSnapshot: 无可用设备，跳过分配");
            return null;
        }

        // 通过分配服务进行原子分配（含冷却与并发控制）
        String selectedDeviceId = deviceAllocationService.allocateDevice(snapshot.getAvailableDevices(), linkTaskId, reason);
        if (selectedDeviceId == null) {
            log.warn("allocateFromSnapshot: 原子分配失败（可能在冷却或被占用），linkTaskId={}", linkTaskId);
            return null;
        }

        // 审计与内存分类：将该设备在本地快照中标记为 USING
        try {
            DeviceStatusResponse.DeviceInfo info = null;
            if (snapshot.getDevices() != null) {
                info = snapshot.getDevices().get(selectedDeviceId);
            }

            Category prevCategory = lastStatusByDevice.get(selectedDeviceId);
            Category newCategory = Category.USING;
            boolean changed = prevCategory == null || prevCategory != newCategory;

            if (changed) {
                LocalDateTime occurredAt = parseTimeOrNow(info != null ? info.getTime() : null);
                String series = info != null ? info.getSeries() : null;
                Integer indexNo = info != null ? info.getIndex() : null;
                String snapshotVal = info != null ? info.getVal() : null;

                // 写入设备状态变更历史
                DeviceStatusTransition t = new DeviceStatusTransition(
                        selectedDeviceId,
                        prevCategory != null ? prevCategory.name() : null,
                        newCategory.name(),
                        reason != null ? ("ALLOCATE:" + reason) : "ALLOCATE",
                        snapshotVal,
                        series,
                        indexNo,
                        occurredAt
                );
                try {
                    transitionMapper.insert(t);
                } catch (Exception e) {
                    log.error("写入设备状态变更(ALLOCATE)失败 device={} prev={} next={} err={}",
                            selectedDeviceId,
                            prevCategory != null ? prevCategory.name() : null,
                            newCategory.name(),
                            e.getMessage(), e);
                }
            }

            lastStatusByDevice.put(selectedDeviceId, Category.USING);
            log.info("allocateFromSnapshot: 设备分配并标记USING完成 device={} linkTaskId={}", selectedDeviceId, linkTaskId);
        } catch (Exception e) {
            log.warn("allocateFromSnapshot: 标记内存分类为USING时发生异常 device={} err={}", selectedDeviceId, e.getMessage());
        }

        return selectedDeviceId;
    }

    /**
     * 当脚本回报为配置的空闲标识时，如果设备上仍有 LOGGED_IN 任务且运行超过3分钟，则将其自动标记为 COMPLETED。
     * 返回成功完成的任务数量。
     */
    private int autoCompleteLoggedInTasksIfIdleOver3m(String deviceId) {
        List<LinkTask> loggedInTasks = linkTaskMapper.findByMachineIdAndStatus(deviceId, "LOGGED_IN");
        if (loggedInTasks == null || loggedInTasks.isEmpty()) {
            return 0;
        }
        LocalDateTime now = LocalDateTime.now();
        int completed = 0;
        for (LinkTask task : loggedInTasks) {
            LocalDateTime loginAt = task.getLoginAt();
            boolean over3m = loginAt != null && loginAt.isBefore(now.minusMinutes(3));
            if (!over3m) {
                continue;
            }
            String prev = task.getStatus();
            task.setStatus("COMPLETED");
            task.setReason("AUTO_COMPLETE_IDLE_3M");
            task.setUpdatedAt(now);
            try {
                int updated = linkTaskMapper.update(task);
                if (updated > 0) {
                    Integer sinceLoginSeconds = loginAt != null ? (int) Duration.between(loginAt, now).getSeconds() : null;
                    try {
                        statusHistoryMapper.insert(new LinkTaskStatusHistory(
                                task.getId(), task.getCodeNo(), deviceId,
                                prev, "COMPLETED", "DEVICE_STATS", "idleConfiguredAndRunningOver3m",
                                sinceLoginSeconds, null
                        ));
                    } catch (Exception ignore) {}
                    completed++;
                    log.info("自动完成任务：codeNo={} device={} loginAt={} 超过3分钟", task.getCodeNo(), deviceId, loginAt);
                }
            } catch (Exception e) {
                log.warn("自动完成任务失败：codeNo={} device={} err={}", task.getCodeNo(), deviceId, e.getMessage());
            }
        }
        return completed;
    }

    private boolean hasLoggedInTask(String deviceId) {
        List<LinkTask> loggedInTasks = linkTaskMapper.findByMachineIdAndStatus(deviceId, "LOGGED_IN");
        return loggedInTasks != null && !loggedInTasks.isEmpty();
    }

    private boolean hasUsingTask(String deviceId) {
        List<LinkTask> usingTasks = linkTaskMapper.findByMachineIdAndStatus(deviceId, "USING");
        return usingTasks != null && !usingTasks.isEmpty();
    }

    private static boolean isNumeric(String text) {
        if (text == null) return false;
        int len = text.length();
        if (len == 0) return false;
        for (int i = 0; i < len; i++) {
            char c = text.charAt(i);
            if (c < '0' || c > '9') return false;
        }
        return true;
    }

    private static LocalDateTime parseTimeOrNow(String timeStr) {
        if (timeStr == null || timeStr.isEmpty()) {
            return LocalDateTime.now();
        }
        // 常见格式：yyyy-MM-dd HH:mm:ss
        try {
            return LocalDateTime.parse(timeStr, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        } catch (DateTimeParseException e) {
            // 回退当前时间
            return LocalDateTime.now();
        }
    }
}
