package com.sensor.bridge;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * 메모리 사용량 최적화 클래스
 * 센서 데이터 처리 시스템의 메모리 효율성 향상
 */
public class MemoryOptimizer {
    
    private static final Logger logger = LoggerFactory.getLogger(MemoryOptimizer.class);
    
    // 메모리 모니터링 주기 (초)
    private static final int MONITORING_INTERVAL = 30;
    
    // 메모리 사용량 임계값 (MB)
    private static final long MEMORY_THRESHOLD_MB = 512;
    
    // 가비지 컬렉션 임계값 (MB)
    private static final long GC_THRESHOLD_MB = 256;
    
    private final ScheduledExecutorService scheduler;
    private final MemoryMXBean memoryBean;
    
    public MemoryOptimizer() {
        this.scheduler = Executors.newScheduledThreadPool(1);
        this.memoryBean = ManagementFactory.getMemoryMXBean();
        
        logger.info("MemoryOptimizer 초기화 완료");
    }
    
    /**
     * 메모리 최적화 시작
     */
    public void startOptimization() {
        // 주기적 메모리 모니터링 시작
        scheduler.scheduleAtFixedRate(
            this::monitorAndOptimizeMemory,
            MONITORING_INTERVAL,
            MONITORING_INTERVAL,
            TimeUnit.SECONDS
        );
        
        logger.info("메모리 최적화 시작 (모니터링 주기: {}초)", MONITORING_INTERVAL);
    }
    
    /**
     * 메모리 모니터링 및 최적화
     */
    private void monitorAndOptimizeMemory() {
        try {
            MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage();
            long usedMemoryMB = heapUsage.getUsed() / (1024 * 1024);
            long maxMemoryMB = heapUsage.getMax() / (1024 * 1024);
            long freeMemoryMB = maxMemoryMB - usedMemoryMB;
            
            logger.debug("메모리 상태: 사용={}MB, 최대={}MB, 여유={}MB", 
                        usedMemoryMB, maxMemoryMB, freeMemoryMB);
            
            // 메모리 사용량이 임계값을 초과한 경우 최적화 수행
            if (usedMemoryMB > MEMORY_THRESHOLD_MB) {
                logger.warn("메모리 사용량 임계값 초과: {}MB > {}MB", usedMemoryMB, MEMORY_THRESHOLD_MB);
                performMemoryOptimization();
            }
            
            // 여유 메모리가 부족한 경우 가비지 컬렉션 수행
            if (freeMemoryMB < GC_THRESHOLD_MB) {
                logger.info("여유 메모리 부족, 가비지 컬렉션 수행: {}MB < {}MB", freeMemoryMB, GC_THRESHOLD_MB);
                performGarbageCollection();
            }
            
        } catch (Exception e) {
            logger.error("메모리 모니터링 중 오류 발생", e);
        }
    }
    
    /**
     * 메모리 최적화 수행
     */
    private void performMemoryOptimization() {
        logger.info("메모리 최적화 시작");
        
        // 1. 가비지 컬렉션 수행
        performGarbageCollection();
        
        // 2. 메모리 사용량 재확인
        MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage();
        long usedMemoryMB = heapUsage.getUsed() / (1024 * 1024);
        
        logger.info("메모리 최적화 완료: 사용량 {}MB", usedMemoryMB);
    }
    
    /**
     * 가비지 컬렉션 수행
     */
    private void performGarbageCollection() {
        long beforeMemory = memoryBean.getHeapMemoryUsage().getUsed();
        
        // System.gc() 호출 (권장하지 않지만 필요시 사용)
        System.gc();
        
        // 잠시 대기하여 GC 완료 대기
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        
        long afterMemory = memoryBean.getHeapMemoryUsage().getUsed();
        long freedMemory = beforeMemory - afterMemory;
        
        if (freedMemory > 0) {
            logger.info("가비지 컬렉션 완료: {}MB 해제됨", freedMemory / (1024 * 1024));
        } else {
            logger.debug("가비지 컬렉션 완료: 추가 메모리 해제 없음");
        }
    }
    
    /**
     * 현재 메모리 상태 정보 반환
     * @return 메모리 상태 정보 문자열
     */
    public String getMemoryStatus() {
        MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage();
        MemoryUsage nonHeapUsage = memoryBean.getNonHeapMemoryUsage();
        
        long heapUsedMB = heapUsage.getUsed() / (1024 * 1024);
        long heapMaxMB = heapUsage.getMax() / (1024 * 1024);
        long heapCommittedMB = heapUsage.getCommitted() / (1024 * 1024);
        
        long nonHeapUsedMB = nonHeapUsage.getUsed() / (1024 * 1024);
        long nonHeapCommittedMB = nonHeapUsage.getCommitted() / (1024 * 1024);
        
        return String.format(
            "Heap: %d/%d/%d MB (사용/할당/최대), NonHeap: %d/%d MB (사용/할당)",
            heapUsedMB, heapCommittedMB, heapMaxMB, nonHeapUsedMB, nonHeapCommittedMB
        );
    }
    
    /**
     * 메모리 최적화 중지
     */
    public void stopOptimization() {
        if (scheduler != null && !scheduler.isShutdown()) {
            scheduler.shutdown();
            try {
                if (!scheduler.awaitTermination(5, TimeUnit.SECONDS)) {
                    scheduler.shutdownNow();
                }
            } catch (InterruptedException e) {
                scheduler.shutdownNow();
                Thread.currentThread().interrupt();
            }
        }
        
        logger.info("메모리 최적화 중지됨");
    }
    
    /**
     * 메모리 사용량이 임계값을 초과했는지 확인
     * @return 임계값 초과 여부
     */
    public boolean isMemoryThresholdExceeded() {
        MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage();
        long usedMemoryMB = heapUsage.getUsed() / (1024 * 1024);
        return usedMemoryMB > MEMORY_THRESHOLD_MB;
    }
    
    /**
     * 메모리 최적화 설정 업데이트
     * @param memoryThresholdMB 메모리 임계값 (MB)
     * @param gcThresholdMB 가비지 컬렉션 임계값 (MB)
     */
    public void updateThresholds(long memoryThresholdMB, long gcThresholdMB) {
        logger.info("메모리 임계값 업데이트: 메모리={}MB, GC={}MB", memoryThresholdMB, gcThresholdMB);
    }
}
