package com.qqchen.deploy.backend.deploy.scheduler;

import com.qqchen.deploy.backend.deploy.dto.ServerInfoDTO;
import com.qqchen.deploy.backend.deploy.dto.ServerMonitorDataDTO;
import com.qqchen.deploy.backend.deploy.dto.ServerMonitorNotificationConfig;
import com.qqchen.deploy.backend.deploy.entity.Server;
import com.qqchen.deploy.backend.deploy.entity.ServerAlertRule;
import com.qqchen.deploy.backend.deploy.entity.ServerMonitor;
import com.qqchen.deploy.backend.deploy.enums.ServerStatusEnum;
import com.qqchen.deploy.backend.deploy.repository.IServerAlertRuleRepository;
import com.qqchen.deploy.backend.deploy.repository.IServerRepository;
import com.qqchen.deploy.backend.deploy.service.IServerAlertService;
import com.qqchen.deploy.backend.deploy.service.IServerMonitorService;
import com.qqchen.deploy.backend.deploy.service.IServerService;
import com.qqchen.deploy.backend.framework.dto.MonitorData;
import com.qqchen.deploy.backend.framework.ssh.ISSHCommandService;
import com.qqchen.deploy.backend.framework.ssh.SSHCommandServiceFactory;
import com.qqchen.deploy.backend.notification.dto.SendNotificationRequest;
import com.qqchen.deploy.backend.notification.service.INotificationService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import net.schmizz.sshj.SSHClient;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;

/**
 * 服务器监控定时任务
 * 由定时任务管理系统调用，不使用@Scheduled注解
 */
@Slf4j
@Component
public class ServerMonitorScheduler {

    @Resource
    private IServerRepository serverRepository;

    @Resource
    private SSHCommandServiceFactory sshCommandServiceFactory;

    @Resource
    private IServerMonitorService monitorService;

    @Resource
    private IServerAlertService alertService;

    @Resource
    private IServerAlertRuleRepository alertRuleRepository;

    @Resource
    private INotificationService notificationService;

    @Resource
    private IServerService serverService;

    @Resource
    @Qualifier("serverMonitorExecutor")
    private Executor serverMonitorExecutor;

    /**
     * 采集所有在线服务器的监控数据
     * 此方法由定时任务管理系统调用
     *
     * @param notificationChannelId   通知渠道ID（可选，为null则不发送通知）
     * @param resourceAlertTemplateId 资源告警通知模板ID（可选，用于所有监控告警）
     */
    public void collectServerMetrics(Long notificationChannelId,
                                     Long resourceAlertTemplateId) {
        // 构建通知配置对象
        ServerMonitorNotificationConfig config = null;
        if (notificationChannelId != null) {
            config = new ServerMonitorNotificationConfig();
            config.setNotificationChannelId(notificationChannelId);
            config.setResourceAlertTemplateId(resourceAlertTemplateId);

            log.debug("开始采集服务器监控数据: channelId={}, alertTemplateId={}",
                notificationChannelId, resourceAlertTemplateId);
        } else {
            log.debug("开始采集服务器监控数据（不发送通知）");
        }
        long startTime = System.currentTimeMillis();

        try {
            // 1. 查询所有服务器（不管当前状态），准备检测在线状态
            List<Server> allServers = serverRepository.findAll();

            if (allServers.isEmpty()) {
                log.debug("没有需要监控的服务器，跳过监控采集");
                return;
            }

            log.debug("发现 {} 台服务器，开始检测在线状态并采集监控数据", allServers.size());

            // 2. 并发检测所有服务器的连接状态并采集监控数据
            //    - 连接失败 → 发送离线通知
            //    - 连接成功 → 采集数据，检查阈值告警
            final ServerMonitorNotificationConfig finalConfig = config;
            List<CompletableFuture<ServerMonitorDataDTO>> futures = allServers.stream()
                .map(server -> CompletableFuture.supplyAsync(() ->
                    collectSingleServerWithStatusCheck(server, finalConfig),
                    serverMonitorExecutor))  // ✅ 使用专用虚拟线程池
                .collect(Collectors.toList());

            // 3. 等待所有任务完成
            CompletableFuture<Void> allFutures = CompletableFuture.allOf(
                futures.toArray(new CompletableFuture[0])
            );
            allFutures.join();

            // 4. 收集结果
            List<ServerMonitorDataDTO> monitorDataList = futures.stream()
                .map(CompletableFuture::join)
                .filter(data -> data != null)
                .collect(Collectors.toList());

            long duration = System.currentTimeMillis() - startTime;
            log.info("========== 监控数据采集完成: 在线={}/{}, 耗时={}ms ==========",
                monitorDataList.size(), allServers.size(), duration);

            // 5. 批量保存监控数据到数据库
            if (!monitorDataList.isEmpty()) {
                monitorService.batchSaveMonitorData(monitorDataList);
                log.debug("监控数据已保存到数据库: count={}", monitorDataList.size());
            }

            // 6. 检查告警规则（优化：只查询一次规则）
            if (!monitorDataList.isEmpty()) {
                // 一次性查询所有规则，避免 N 次数据库查询
                List<ServerAlertRule> allRules = alertRuleRepository.findAll();
                log.debug("开始检查告警规则: 服务器数={}, 规则数={}",
                    monitorDataList.size(), allRules.size());

                for (ServerMonitorDataDTO data : monitorDataList) {
                    try {
                        alertService.checkAlertRules(data.getServerId(), data, allRules, config);
                    } catch (Exception e) {
                        log.error("检查告警规则失败: serverId={}", data.getServerId(), e);
                    }
                }
            }

        } catch (Exception e) {
            log.error("服务器监控数据采集失败", e);
        } finally {
            // 7. 自动清理历史监控数据
            try {
                cleanOldMonitorData();
            } catch (Exception e) {
                log.error("清理历史监控数据失败", e);
            }
        }
    }

    /**
     * 检测服务器连接状态并采集监控数据（一次SSH连接完成所有任务）
     * 
     * 优化点：
     * 1. 只创建一次SSH连接（避免之前的双重连接问题）
     * 2. 不采集硬件信息（hostname、CPU核心数等），减少不必要的开销
     * 3. 只更新状态和最后连接时间
     */
    private ServerMonitorDataDTO collectSingleServerWithStatusCheck(Server server, ServerMonitorNotificationConfig config) {
        SSHClient sshClient = null;
        ISSHCommandService sshService = null;
        
        try {
            // 1. 获取对应OS的SSH服务
            sshService = sshCommandServiceFactory.getService(server.getOsType());
            
            // 2. 创建SSH连接
            String password = null;
            String privateKey = null;
            String passphrase = null;
            
            switch (server.getAuthType()) {
                case PASSWORD:
                    password = server.getSshPassword();
                    break;
                case KEY:
                    privateKey = server.getSshPrivateKey();
                    passphrase = server.getSshPassphrase();
                    break;
            }
            
            sshClient = sshService.createConnection(
                server.getHostIp(),
                server.getSshPort(),
                server.getSshUser(),
                password,
                privateKey,
                passphrase
            );
            
            // 3. 解除服务器状态告警（如果存在）
            try {
                alertService.resolveServerStatusAlert(server.getId());
            } catch (Exception e) {
                log.warn("解除服务器状态告警失败: serverId={}", server.getId(), e);
            }
            
            // 4. 更新服务器状态为ONLINE
            if (server.getStatus() == ServerStatusEnum.OFFLINE) {
                server.setStatus(ServerStatusEnum.ONLINE);
                server.setLastConnectTime(LocalDateTime.now());
                serverRepository.save(server);
                log.info("✓ 服务器已恢复在线: {} ({})", server.getServerName(), server.getHostIp());
            } else {
                server.setStatus(ServerStatusEnum.ONLINE);
                server.setLastConnectTime(LocalDateTime.now());
                serverRepository.save(server);
                log.info("✓ 服务器连接成功: {} ({})", server.getServerName(), server.getHostIp());
            }
            
            // 6. 采集监控数据（复用同一个SSH连接）
            return collectServerMonitorData(server, sshClient, sshService);
            
        } catch (Exception e) {
            // 连接失败，插入失败记录
            ServerMonitor failureRecord = ServerMonitor.builder()
                .serverId(server.getId())
                .status(com.qqchen.deploy.backend.framework.enums.StatusEnum.FAILURE)
                .collectTime(LocalDateTime.now())
                .build();
            
            try {
                monitorService.saveMonitorRecord(failureRecord);
            } catch (Exception saveError) {
                log.error("保存失败记录异常: serverId={}", server.getId(), saveError);
            }
            
            // 统计连续失败次数
            int failureCount = 0;
            try {
                failureCount = monitorService.countConsecutiveFailures(server.getId(), 10);
                log.error("✗ 服务器连接失败 [连续{}次]: {} ({}) - {}", 
                    failureCount, server.getServerName(), server.getHostIp(), e.getMessage());
            } catch (Exception countError) {
                log.error("统计连续失败次数异常: serverId={}", server.getId(), countError);
                log.error("✗ 服务器连接失败: {} ({}) - {}", 
                    server.getServerName(), server.getHostIp(), e.getMessage());
            }
            
            // 检查服务器状态告警（传入所有规则，避免重复查询）
            if (failureCount > 0 && config != null) {
                try {
                    // 查询所有规则（在外层已查询，这里重新查询以确保最新）
                    List<ServerAlertRule> allRules = alertRuleRepository.findAll();
                    alertService.checkServerStatusAlert(server.getId(), failureCount, allRules, config);
                    
                    // 如果达到严重阈值，标记服务器为OFFLINE
                    ServerAlertRule statusRule = allRules.stream()
                        .filter(rule -> rule.getAlertType() == com.qqchen.deploy.backend.framework.enums.MonitorMetricEnum.SERVER_STATUS)
                        .filter(rule -> Boolean.TRUE.equals(rule.getEnabled()))
                        .filter(rule -> rule.getServerId() == null || server.getId().equals(rule.getServerId()))
                        .findFirst()
                        .orElse(null);
                    
                    if (statusRule != null && failureCount >= statusRule.getCriticalThreshold().intValue()) {
                        if (server.getStatus() != ServerStatusEnum.OFFLINE) {
                            server.setStatus(ServerStatusEnum.OFFLINE);
                            serverRepository.save(server);
                            log.warn("🔴 服务器已标记为离线: {} (连续失败{}次)", 
                                server.getServerName(), failureCount);
                        }
                    }
                } catch (Exception alertError) {
                    log.error("检查服务器状态告警失败: serverId={}", server.getId(), alertError);
                }
            }
            
            return null;
            
        } finally {
            // 5. 关闭SSH连接
            if (sshService != null && sshClient != null) {
                sshService.closeConnection(sshClient);
            }
        }
    }

    /**
     * 采集服务器监控数据（CPU、内存、磁盘、网络使用率）
     * 
     * 性能优化：
     * 1. 每个OS实现类内部使用 executeCommands 批量执行命令（1次网络往返代替5次）
     * 2. 自动适配不同操作系统（Linux用bash，Windows用PowerShell）
     * 
     * @param server 服务器实体
     * @param sshClient 已建立的SSH连接（由调用方管理生命周期）
     * @param sshService SSH命令服务
     * @return 监控数据
     */
    private ServerMonitorDataDTO collectServerMonitorData(Server server, SSHClient sshClient, ISSHCommandService sshService) {
        try {
            // 1. 调用框架层的统一采集方法（内部已批量执行命令 ✅）
            MonitorData monitorData = sshService.collectMonitorData(sshClient);
            
            if (monitorData == null) {
                log.warn("监控数据采集失败: serverId={}, 返回null", server.getId());
                return null;
            }
            
            // 2. 计算已用内存（基于内存使用率和总内存）
            Integer memoryUsed = null;
            if (monitorData.getMemoryUsage() != null && server.getMemorySize() != null) {
                memoryUsed = monitorData.getMemoryUsage().multiply(new BigDecimal(server.getMemorySize()))
                    .divide(new BigDecimal(100), 0, BigDecimal.ROUND_HALF_UP)
                    .intValue();
            }

            // 3. 计算网络流量速率（用于告警检查）
            Long networkRxSpeed = 0L;
            Long networkTxSpeed = 0L;
            Long finalNetworkRx = monitorData.getNetworkRx();  // 最终存储的累计值
            Long finalNetworkTx = monitorData.getNetworkTx();
            
            if (monitorData.getNetworkRx() != null && monitorData.getNetworkTx() != null) {
                // 查询上次监控数据（数据库中存储的是累计值）
                ServerMonitor lastMonitor = monitorService.getLastMonitorData(server.getId());
                
                if (lastMonitor != null && lastMonitor.getNetworkRx() != null && lastMonitor.getNetworkTx() != null) {
                    // 计算时间差（秒）
                    long timeDiffSeconds = java.time.Duration.between(
                        lastMonitor.getCollectTime(), 
                        LocalDateTime.now()
                    ).getSeconds();
                    
                    if (timeDiffSeconds > 0) {
                        // 计算速率 = (当前累计值 - 上次累计值) / 时间差
                        long rxDiff = monitorData.getNetworkRx() - lastMonitor.getNetworkRx();
                        long txDiff = monitorData.getNetworkTx() - lastMonitor.getNetworkTx();
                        
                        // 防止负数（服务器重启或网卡变化导致计数器异常）
                        if (rxDiff >= 0 && txDiff >= 0) {
                            networkRxSpeed = rxDiff / timeDiffSeconds;  // KB/s
                            networkTxSpeed = txDiff / timeDiffSeconds;  // KB/s
                            
                            log.debug("网络速率计算: serverId={}, timeDiff={}s, rxSpeed={}KB/s, txSpeed={}KB/s",
                                server.getId(), timeDiffSeconds, networkRxSpeed, networkTxSpeed);
                        } else {
                            // 检测到异常：使用上次的累计值，避免存入错误数据
                            finalNetworkRx = lastMonitor.getNetworkRx();
                            finalNetworkTx = lastMonitor.getNetworkTx();
                            log.warn("检测到网络计数器异常(负差值)，使用上次累计值: serverId={}, rxDiff={}, txDiff={}, 上次rx={}, 上次tx={}", 
                                server.getId(), rxDiff, txDiff, lastMonitor.getNetworkRx(), lastMonitor.getNetworkTx());
                        }
                    } else {
                        log.warn("时间差异无效: serverId={}, timeDiff={}s", server.getId(), timeDiffSeconds);
                    }
                } else {
                    log.debug("首次采集或无历史数据: serverId={}, 速率设为0", server.getId());
                }
            }

            // 4. 转换为业务层DTO
            ServerMonitorDataDTO data = ServerMonitorDataDTO.builder()
                .serverId(server.getId())
                .cpuUsage(monitorData.getCpuUsage())
                .memoryUsage(monitorData.getMemoryUsage())
                .memoryUsed(memoryUsed)
                .diskUsage(monitorData.getDiskUsage())
                .networkRx(finalNetworkRx)                       // ✅ 累计值（存数据库，已过滤异常数据）
                .networkTx(finalNetworkTx)                       // ✅ 累计值（存数据库，已过滤异常数据）
                .networkRxSpeed(networkRxSpeed)                  // ✅ 速率（告警检查）
                .networkTxSpeed(networkTxSpeed)                  // ✅ 速率（告警检查）
                .collectTime(LocalDateTime.now())
                .build();

            log.debug("服务器监控数据采集成功: serverId={}, cpu={}%, mem={}%, diskCount={}, netRxTotal={}KB, netTxTotal={}KB, netRxSpeed={}KB/s, netTxSpeed={}KB/s",
                server.getId(), 
                monitorData.getCpuUsage(), 
                monitorData.getMemoryUsage(),
                monitorData.getDiskUsage() != null ? monitorData.getDiskUsage().size() : 0, 
                finalNetworkRx, 
                finalNetworkTx,
                networkRxSpeed,
                networkTxSpeed);

            return data;

        } catch (Exception e) {
            log.error("采集服务器监控数据失败: serverId={}, serverName={}, error={}",
                server.getId(), server.getServerName(), e.getMessage());
            return null; // 返回null，由调用方处理
        }
    }

    /**
     * 清理历史监控数据
     * 此方法由定时任务管理系统调用，建议每天凌晨执行
     */
    public void cleanOldMonitorData() {
        log.debug("开始清理历史监控数据");

        try {
            // 删除30天前的数据
            LocalDateTime thirtyDaysAgo = LocalDateTime.now().minusDays(30);
            int deletedCount = monitorService.deleteOldData(thirtyDaysAgo);

            log.info("========== 历史监控数据清理完成: count={} ==========", deletedCount);
        } catch (Exception e) {
            log.error("清理历史监控数据失败", e);
        }
    }
}
