package com.zioinfo.kintex.system.syshealth;

import com.zioinfo.kintex.module.m5.RenderProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;

import java.io.File;
import java.lang.management.ManagementFactory;
import java.time.OffsetDateTime;

/**
 * 시스템 상태 집계(actuator 미도입, 의존성 0 커스텀) — ADMIN 전용.
 * DB=경량 SELECT 1, Redis=ping, worker=렌더 큐 접근 가능 여부·적재량, 디스크/메모리=JVM 표준.
 * 스택트레이스·내부IP 미노출.
 */
@Service
public class SystemHealthService {

    private static final Logger log = LoggerFactory.getLogger(SystemHealthService.class);
    private static final String VERSION = "1.0.0";

    private final JdbcTemplate jdbcTemplate;
    private final StringRedisTemplate redis;
    private final RenderProperties renderProps;

    public SystemHealthService(JdbcTemplate jdbcTemplate, StringRedisTemplate redis,
                               RenderProperties renderProps) {
        this.jdbcTemplate = jdbcTemplate;
        this.redis = redis;
        this.renderProps = renderProps;
    }

    public SystemHealthDto health() {
        boolean redisUp = redisUp();
        long depth = redisUp ? queueDepth() : -1L;
        return new SystemHealthDto(
                dbStatus(),
                redisUp ? "UP" : "DOWN",
                workerStatus(redisUp, depth),
                depth,
                diskFreePct(),
                diskFreeMb(),
                memUsedPct(),
                uptimeSec(),
                VERSION,
                OffsetDateTime.now().toString());
    }

    private String dbStatus() {
        try {
            jdbcTemplate.queryForObject("SELECT 1", Integer.class);
            return "UP";
        } catch (Exception e) {
            log.warn("[HEALTH] DB DOWN: {}", e.getClass().getSimpleName());
            return "DOWN";
        }
    }

    private boolean redisUp() {
        org.springframework.data.redis.connection.RedisConnectionFactory factory = redis.getConnectionFactory();
        if (factory == null) {
            return false;
        }
        try (org.springframework.data.redis.connection.RedisConnection conn = factory.getConnection()) {
            String pong = conn.ping();
            return pong != null;
        } catch (Exception e) {
            log.warn("[HEALTH] Redis DOWN: {}", e.getClass().getSimpleName());
            return false;
        }
    }

    private long queueDepth() {
        try {
            Long size = redis.opsForList().size(renderProps.getQueueKey());
            return size == null ? 0L : size;
        } catch (Exception e) {
            return -1L;
        }
    }

    /** 워커 상태(추정): Redis 접근 불가=DOWN, 큐 비어있음=IDLE, 적재 있음=UP. */
    private String workerStatus(boolean redisUp, long depth) {
        if (!redisUp || depth < 0) {
            return "DOWN";
        }
        return depth == 0 ? "IDLE" : "UP";
    }

    private long diskFreePct() {
        File root = new File(".");
        long total = root.getTotalSpace();
        long usable = root.getUsableSpace();
        return total <= 0 ? -1 : Math.round(usable * 100.0 / total);
    }

    private long diskFreeMb() {
        return new File(".").getUsableSpace() / (1024 * 1024);
    }

    private long memUsedPct() {
        Runtime rt = Runtime.getRuntime();
        long used = rt.totalMemory() - rt.freeMemory();
        long max = rt.maxMemory();
        return max <= 0 ? -1 : Math.round(used * 100.0 / max);
    }

    private long uptimeSec() {
        return ManagementFactory.getRuntimeMXBean().getUptime() / 1000;
    }
}
