package com.utic.center.utic.traf.server.health;

import com.utic.center.common.utils.SystemHealth;
import com.utic.center.utic.traf.server.config.ApplicationConfig;
import com.utic.center.utic.traf.server.repository.ApplicationRepository;
import com.zaxxer.hikari.HikariDataSource;
import com.zaxxer.hikari.HikariPoolMXBean;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import javax.sql.DataSource;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.text.DecimalFormat;
import java.util.EnumMap;
import java.util.Map;

@Slf4j
@Getter
@Service
public class SystemHealthService {

    private final ApplicationConfig config;
    private final DataSource dataSource;
    private final Map<Thread.State, Integer> stateCountMap = new EnumMap<>(Thread.State.class);
    private final SystemHealth systemHealth = new SystemHealth();
    private final DecimalFormat df = new DecimalFormat("#.##");

    public SystemHealthService(ApplicationConfig config,
                               @Qualifier("dataSource") DataSource dataSource
                               ) {
        this.config = config;
        this.dataSource = dataSource;
    }

    public boolean checkSystemHealth() {
        try {
            this.systemHealth.checkHealth(false);
            loggingHealthCheck();

            double cpuUsage = this.systemHealth.getCpuUsage();
            if (cpuUsage > this.config.getCpuLimits()) {
                ApplicationRepository.processState.setErrDesc("CPU 사용율이 너무 높음: " + String.format("%.2f", cpuUsage));
                log.warn("[SKIP] SystemHealthService.checkSystemHealth: High CPU Usage, Limit({} %), Current({} %), Schedule Job SKIP...",
                        this.config.getCpuLimits(), String.format("%.2f", cpuUsage));
                loggingThreads();
                return false;
            }
        } catch (Exception e) {
            log.error("SystemHealthService.checkSystemHealth: Exception {}", e.getMessage());
        }
        return true;
    }

    private void loggingHealthCheck() {
        log.info("----------------------------------------------------------------------------------------------------------");
        loggingSystemHealth();
        logSessionStatus();
        loggingThreadState();
        log.info("----------------------------------------------------------------------------------------------------------");
    }

    private void loggingSystemHealth() {
        double loadAvg = this.systemHealth.getLoadAverage();
        String loadAvgStr = (loadAvg < 0) ? "N/A" : this.df.format(loadAvg);
        log.info("SYSTEM HEALTH: CPU {} Cores[{} %, LoadAvg({})], Memory[{} %, Used({} MB), Max({} MB)], Threads[{}, Peak({})], GC(T/R)[{}/{} ms]",
                this.systemHealth.getCpuCores(),
                this.df.format(this.systemHealth.getCpuUsage()),
                loadAvgStr,
                this.df.format(this.systemHealth.getMemUsage()),
                this.systemHealth.getUsedMemory(),
                this.systemHealth.getMaxMemory(),
                this.systemHealth.getThreadCount(),
                this.systemHealth.getPeakThreadCount(),
                this.systemHealth.getGcTotalAvgTime(),
                this.systemHealth.getGcRecentAvgTime());
    }

    private void logSessionStatus() {
        HikariDataSource hikariDataSource = (HikariDataSource) this.dataSource;
        HikariPoolMXBean poolStats = hikariDataSource.getHikariPoolMXBean();
        int totalConnections = poolStats.getTotalConnections();
        int activeConnections = poolStats.getActiveConnections();
        int idleConnections = poolStats.getIdleConnections();
        int threadsAwaiting = poolStats.getThreadsAwaitingConnection();
        log.info("   DB SESSION: UTIS, Total: {}, Active: {}, Idle: {}, Waiting: {}",
                totalConnections, activeConnections, idleConnections, threadsAwaiting);
    }

    private void loggingThreadState() {
        ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
        ThreadInfo[] infos = threadBean.dumpAllThreads(false, false);
        this.stateCountMap.clear();
        for (ThreadInfo info : infos) {
            Thread.State state = info.getThreadState();
            this.stateCountMap.put(state, this.stateCountMap.getOrDefault(state, 0) + 1);
        }
        StringBuilder sb = new StringBuilder();
        for (Map.Entry<Thread.State, Integer> entry : stateCountMap.entrySet()) {
            sb.append(String.format("%s(%d), ", entry.getKey().name(), entry.getValue()));
        }
        // 마지막 쉼표 제거
        if (sb.length() > 0) {
            sb.setLength(sb.length() - 2);
        }
        log.info(" THREAD STATE: {}", sb.toString());
    }

    private void loggingThreads() {
        ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
        ThreadInfo[] infos = threadBean.dumpAllThreads(false, false);
        this.stateCountMap.clear();
        for (ThreadInfo info : infos) {
            Thread.State state = info.getThreadState();
            this.stateCountMap.put(state, this.stateCountMap.getOrDefault(state, 0) + 1);
            log.info("         [" + info.getThreadId() + "] " + info.getThreadName() + " - " + info.getThreadState());
        }
    }

}
