package com.ruoyi.blockchain.service;

import java.math.BigInteger;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.ruoyi.blockchain.config.BlockchainConfig;

/**
 * 区块链网络监控服务
 * 
 * @author ruoyi
 */
@Service
public class NetworkMonitorService {
    
    private static final Logger log = LoggerFactory.getLogger(NetworkMonitorService.class);
    
    @Autowired
    private BlockchainIntegrator blockchainIntegrator;
    
    @Autowired
    private BlockchainConfig blockchainConfig;
    
    private ScheduledExecutorService scheduler;
    private volatile boolean isMonitoring = false;
    
    // 网络状态缓存
    private volatile Map<String, Object> networkStatus = new HashMap<>();
    private volatile BigInteger lastBlockNumber = BigInteger.ZERO;
    private volatile long lastUpdateTime = 0;
    
    @PostConstruct
    public void startMonitoring() {
        scheduler = Executors.newScheduledThreadPool(2);
        isMonitoring = true;
        
        // 每30秒检查一次网络状态
        scheduler.scheduleAtFixedRate(this::checkNetworkStatus, 0, 30, TimeUnit.SECONDS);
        
        // 每10秒检查一次区块高度
        scheduler.scheduleAtFixedRate(this::checkBlockHeight, 5, 10, TimeUnit.SECONDS);
        
        log.info("区块链网络监控服务已启动");
    }
    
    @PreDestroy
    public void stopMonitoring() {
        isMonitoring = false;
        if (scheduler != null && !scheduler.isShutdown()) {
            scheduler.shutdown();
            try {
                if (!scheduler.awaitTermination(10, TimeUnit.SECONDS)) {
                    scheduler.shutdownNow();
                }
            } catch (InterruptedException e) {
                scheduler.shutdownNow();
                Thread.currentThread().interrupt();
            }
        }
        log.info("区块链网络监控服务已停止");
    }
    
    /**
     * 检查网络状态
     */
    private void checkNetworkStatus() {
        if (!isMonitoring) {
            return;
        }
        
        try {
            Map<String, Object> status = blockchainIntegrator.getNetworkStatus();
            networkStatus = status;
            lastUpdateTime = System.currentTimeMillis();
            
            boolean connected = (Boolean) status.getOrDefault("connected", false);
            if (!connected) {
                log.warn("区块链网络连接异常: {}", status.get("error"));
                handleNetworkDisconnection();
            } else {
                log.debug("区块链网络状态正常: chainId={}, blockNumber={}", 
                    status.get("chainId"), status.get("currentBlockNumber"));
            }
            
        } catch (Exception e) {
            log.error("检查网络状态失败", e);
            handleNetworkError(e);
        }
    }
    
    /**
     * 检查区块高度
     */
    private void checkBlockHeight() {
        if (!isMonitoring) {
            return;
        }
        
        try {
            BigInteger currentBlock = blockchainIntegrator.getCurrentBlockNumber();
            
            if (currentBlock.compareTo(lastBlockNumber) > 0) {
                log.debug("检测到新区块: {} -> {}", lastBlockNumber, currentBlock);
                lastBlockNumber = currentBlock;
                
                // 检查是否有长时间未出块的情况
                checkBlockProduction();
            }
            
        } catch (Exception e) {
            log.error("检查区块高度失败", e);
        }
    }
    
    /**
     * 检查区块生产情况
     */
    private void checkBlockProduction() {
        long currentTime = System.currentTimeMillis();
        long timeSinceLastUpdate = currentTime - lastUpdateTime;
        
        // 如果超过5分钟没有新区块，发出警告
        if (timeSinceLastUpdate > 5 * 60 * 1000) {
            log.warn("区块链网络可能存在异常，{}分钟内未检测到新区块", timeSinceLastUpdate / 60000);
        }
    }
    
    /**
     * 处理网络断开连接
     */
    private void handleNetworkDisconnection() {
        log.warn("区块链网络连接断开，尝试重新连接...");
        
        // 实现重连逻辑
        int retryCount = blockchainConfig.getNetwork().getRetryCount();
        for (int i = 0; i < retryCount; i++) {
            try {
                Thread.sleep(5000); // 等待5秒后重试
                
                if (blockchainIntegrator.isConnected()) {
                    log.info("区块链网络重连成功");
                    return;
                }
                
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                break;
            }
        }
        
        log.error("区块链网络重连失败，已达到最大重试次数: {}", retryCount);
    }
    
    /**
     * 处理网络错误
     */
    private void handleNetworkError(Exception e) {
        log.error("区块链网络监控出现错误", e);
        
        // 更新网络状态
        Map<String, Object> errorStatus = new HashMap<>();
        errorStatus.put("connected", false);
        errorStatus.put("error", e.getMessage());
        errorStatus.put("lastError", System.currentTimeMillis());
        networkStatus = errorStatus;
    }
    
    /**
     * 获取当前网络状态
     */
    public Map<String, Object> getCurrentNetworkStatus() {
        Map<String, Object> status = new HashMap<>(networkStatus);
        status.put("lastUpdateTime", lastUpdateTime);
        status.put("lastBlockNumber", lastBlockNumber);
        status.put("monitoringActive", isMonitoring);
        return status;
    }
    
    /**
     * 获取网络健康状态
     */
    public boolean isNetworkHealthy() {
        if (networkStatus.isEmpty()) {
            return false;
        }
        
        boolean connected = (Boolean) networkStatus.getOrDefault("connected", false);
        long timeSinceLastUpdate = System.currentTimeMillis() - lastUpdateTime;
        
        // 如果连接正常且最近有更新，认为网络健康
        return connected && timeSinceLastUpdate < 2 * 60 * 1000; // 2分钟内有更新
    }
    
    /**
     * 强制刷新网络状态
     */
    public void refreshNetworkStatus() {
        checkNetworkStatus();
        checkBlockHeight();
    }
}