package cn.novalon.gym.manage.gateway.monitor;

import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.Gauge;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Timer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.lang.management.ManagementFactory;
import java.lang.management.ThreadMXBean;
import java.time.Duration;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 性能监控服务
 * 
 * 文件定义：监控网关性能指标
 * 涉及业务：性能统计、瓶颈识别、性能优化
 * 
 * 监控指标：
 * 1. 请求处理时间
 * 2. 内存使用情况
 * 3. 线程池状态
 * 4. 连接池状态
 * 
 * @author 张翔
 * @date 2026-03-26
 */
@Service
public class PerformanceMonitor {

    private static final Logger logger = LoggerFactory.getLogger(PerformanceMonitor.class);

    private final MeterRegistry meterRegistry;
    
    private final Counter slowRequestsCounter;
    private final Counter memoryWarningCounter;
    
    private final AtomicLong totalProcessingTime = new AtomicLong(0);
    private final AtomicLong requestCount = new AtomicLong(0);
    
    private final Map<String, PerformanceStats> pathStats = new ConcurrentHashMap<>();

    private long slowRequestThresholdMs = 2000;
    private double memoryWarningThreshold = 0.85;

    public PerformanceMonitor(MeterRegistry meterRegistry) {
        this.meterRegistry = meterRegistry;

        this.slowRequestsCounter = Counter.builder("gateway.performance.slow_requests")
                .description("Number of slow requests")
                .register(meterRegistry);

        this.memoryWarningCounter = Counter.builder("gateway.performance.memory_warnings")
                .description("Number of memory warnings")
                .register(meterRegistry);

        Gauge.builder("gateway.performance.avg_processing_time", 
                this, PerformanceMonitor::getAverageProcessingTime)
                .description("Average request processing time in ms")
                .register(meterRegistry);

        Gauge.builder("gateway.performance.memory_usage", 
                this, PerformanceMonitor::getMemoryUsage)
                .description("Current memory usage ratio")
                .register(meterRegistry);

        logger.info("Performance monitor initialized");
    }

    public void recordRequest(String path, long durationMs) {
        totalProcessingTime.addAndGet(durationMs);
        requestCount.incrementAndGet();

        pathStats.compute(path, (key, stats) -> {
            if (stats == null) {
                stats = new PerformanceStats();
            }
            stats.recordRequest(durationMs);
            return stats;
        });

        if (durationMs > slowRequestThresholdMs) {
            slowRequestsCounter.increment();
            logger.warn("Slow request detected - Path: {}, Duration: {}ms", path, durationMs);
        }

        Timer.builder("gateway.performance.request_duration")
                .description("Request processing duration")
                .tag("path", path)
                .register(meterRegistry)
                .record(Duration.ofMillis(durationMs));

        checkMemoryUsage();
    }

    private void checkMemoryUsage() {
        double memoryUsage = getMemoryUsage();
        
        if (memoryUsage > memoryWarningThreshold) {
            memoryWarningCounter.increment();
            logger.warn("High memory usage detected: {}%", String.format("%.2f", memoryUsage * 100));
        }
    }

    public double getAverageProcessingTime() {
        long count = requestCount.get();
        if (count == 0) {
            return 0.0;
        }
        return (double) totalProcessingTime.get() / count;
    }

    public double getMemoryUsage() {
        Runtime runtime = Runtime.getRuntime();
        long totalMemory = runtime.totalMemory();
        long freeMemory = runtime.freeMemory();
        long usedMemory = totalMemory - freeMemory;
        
        return (double) usedMemory / totalMemory;
    }

    public Map<String, Object> getMemoryStats() {
        Runtime runtime = Runtime.getRuntime();
        
        Map<String, Object> stats = new ConcurrentHashMap<>();
        stats.put("totalMemory", runtime.totalMemory());
        stats.put("freeMemory", runtime.freeMemory());
        stats.put("usedMemory", runtime.totalMemory() - runtime.freeMemory());
        stats.put("maxMemory", runtime.maxMemory());
        stats.put("memoryUsage", getMemoryUsage());
        
        return stats;
    }

    public Map<String, Object> getThreadStats() {
        ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
        
        Map<String, Object> stats = new ConcurrentHashMap<>();
        stats.put("threadCount", threadBean.getThreadCount());
        stats.put("peakThreadCount", threadBean.getPeakThreadCount());
        stats.put("daemonThreadCount", threadBean.getDaemonThreadCount());
        stats.put("totalStartedThreadCount", threadBean.getTotalStartedThreadCount());
        
        return stats;
    }

    public Map<String, PerformanceStats> getPathStats() {
        return new ConcurrentHashMap<>(pathStats);
    }

    public void clearStats() {
        totalProcessingTime.set(0);
        requestCount.set(0);
        pathStats.clear();
        logger.info("Performance stats cleared");
    }

    public void setSlowRequestThresholdMs(long threshold) {
        this.slowRequestThresholdMs = threshold;
        logger.info("Slow request threshold set to: {}ms", threshold);
    }

    public void setMemoryWarningThreshold(double threshold) {
        this.memoryWarningThreshold = threshold;
        logger.info("Memory warning threshold set to: {}", threshold);
    }

    public static class PerformanceStats {
        private final AtomicLong requestCount = new AtomicLong(0);
        private final AtomicLong totalTime = new AtomicLong(0);
        private final AtomicLong maxTime = new AtomicLong(0);
        private final AtomicLong minTime = new AtomicLong(Long.MAX_VALUE);

        public void recordRequest(long durationMs) {
            requestCount.incrementAndGet();
            totalTime.addAndGet(durationMs);
            
            long currentMax = maxTime.get();
            if (durationMs > currentMax) {
                maxTime.compareAndSet(currentMax, durationMs);
            }
            
            long currentMin = minTime.get();
            if (durationMs < currentMin) {
                minTime.compareAndSet(currentMin, durationMs);
            }
        }

        public long getRequestCount() {
            return requestCount.get();
        }

        public double getAverageTime() {
            long count = requestCount.get();
            return count == 0 ? 0.0 : (double) totalTime.get() / count;
        }

        public long getMaxTime() {
            return maxTime.get();
        }

        public long getMinTime() {
            long min = minTime.get();
            return min == Long.MAX_VALUE ? 0 : min;
        }
    }
}
