package dev.lions.unionflow.server.service;

import dev.lions.unionflow.server.entity.AlertConfiguration;
import dev.lions.unionflow.server.entity.SystemAlert;
import dev.lions.unionflow.server.repository.AlertConfigurationRepository;
import dev.lions.unionflow.server.repository.SystemAlertRepository;
import dev.lions.unionflow.server.repository.SystemLogRepository;
import io.quarkus.arc.Arc;
import io.quarkus.scheduler.Scheduled;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
import lombok.extern.slf4j.Slf4j;

import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.OperatingSystemMXBean;
import java.time.LocalDateTime;

/**
 * Service de monitoring automatique qui vérifie les métriques système
 * et génère des alertes automatiquement selon la configuration.
 *
 * @author UnionFlow Team
 * @version 1.0
 * @since 2026-03-15
 */
@Slf4j
@ApplicationScoped
public class AlertMonitoringService {

    @Inject
    AlertConfigurationRepository alertConfigurationRepository;

    @Inject
    SystemAlertRepository systemAlertRepository;

    @Inject
    SystemLogRepository systemLogRepository;

    // Stocker la dernière valeur CPU pour détecter les dépassements prolongés
    private Double lastCpuUsage = 0.0;
    private LocalDateTime lastCpuHighTime = null;

    /**
     * Scheduler qui s'exécute toutes les minutes pour vérifier les métriques
     */
    @Scheduled(cron = "0 * * * * ?") // Toutes les minutes à la seconde 0
    @Transactional
    public void monitorSystemMetrics() {
        // Guard contre l'exécution pendant le shutdown Quarkus (Arc.container() null → NPE)
        if (!Arc.container().isRunning()) {
            return;
        }
        try {
            log.debug("Running scheduled system metrics monitoring...");

            AlertConfiguration config = alertConfigurationRepository.getConfiguration();

            // Vérifier CPU
            if (config.getCpuHighAlertEnabled()) {
                checkCpuThreshold(config);
            }

            // Vérifier mémoire
            if (config.getMemoryLowAlertEnabled()) {
                checkMemoryThreshold(config);
            }

            // Vérifier erreurs critiques
            if (config.getCriticalErrorAlertEnabled()) {
                checkCriticalErrors();
            }

        } catch (Exception e) {
            log.error("Error in scheduled system monitoring", e);
        }
    }

    /**
     * Vérifier si le CPU dépasse le seuil configuré
     */
    private void checkCpuThreshold(AlertConfiguration config) {
        try {
            // getProcessCpuLoad() renvoie la charge CPU de CE process JVM (0.0-1.0),
            // ce qui est correct en conteneur K8s/Docker.
            // getSystemLoadAverage() renvoie la charge du NODE entier (hôte Linux),
            // divisée par availableProcessors() limité par le conteneur (ex: 1),
            // ce qui produit des faux positifs dès que le node est actif.
            com.sun.management.OperatingSystemMXBean osBean =
                (com.sun.management.OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean();
            double processCpuLoad = osBean.getProcessCpuLoad();
            double cpuUsage = processCpuLoad < 0 ? 0.0 : Math.min(100.0, processCpuLoad * 100.0);
            lastCpuUsage = cpuUsage;

            int threshold = config.getCpuThresholdPercent();
            int durationMinutes = config.getCpuDurationMinutes();

            // Vérifier si le seuil est dépassé
            if (cpuUsage > threshold) {
                if (lastCpuHighTime == null) {
                    lastCpuHighTime = LocalDateTime.now();
                } else {
                    // Vérifier si le dépassement dure depuis assez longtemps
                    LocalDateTime now = LocalDateTime.now();
                    long minutesSinceHigh = java.time.Duration.between(lastCpuHighTime, now).toMinutes();

                    if (minutesSinceHigh >= durationMinutes) {
                        // Créer une alerte seulement si pas déjà créée récemment
                        if (!hasRecentCpuAlert()) {
                            createCpuAlert(cpuUsage, threshold);
                            log.warn("CPU alert created: {}% (threshold: {}%)", cpuUsage, threshold);
                        }
                    }
                }
            } else {
                // Reset le compteur si CPU revient sous le seuil
                lastCpuHighTime = null;
            }

        } catch (Exception e) {
            log.error("Error checking CPU threshold", e);
        }
    }

    /**
     * Vérifier si la mémoire dépasse le seuil configuré
     */
    private void checkMemoryThreshold(AlertConfiguration config) {
        try {
            MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
            long maxMemory = memoryBean.getHeapMemoryUsage().getMax();
            long usedMemory = memoryBean.getHeapMemoryUsage().getUsed();

            double memoryUsage = maxMemory > 0 ? (usedMemory * 100.0 / maxMemory) : 0.0;
            int threshold = config.getMemoryThresholdPercent();

            if (memoryUsage > threshold) {
                // Créer une alerte seulement si pas déjà créée récemment
                if (!hasRecentMemoryAlert()) {
                    createMemoryAlert(memoryUsage, threshold);
                    log.warn("Memory alert created: {}% (threshold: {}%)", memoryUsage, threshold);
                }
            }

        } catch (Exception e) {
            log.error("Error checking memory threshold", e);
        }
    }

    /**
     * Vérifier le nombre d'erreurs critiques dans la dernière heure
     */
    private void checkCriticalErrors() {
        try {
            long criticalCount = systemLogRepository.countByLevelLast24h("CRITICAL");

            // Si plus de 5 erreurs critiques dans les dernières 24h, créer une alerte
            if (criticalCount > 5) {
                if (!hasRecentCriticalErrorsAlert()) {
                    createCriticalErrorsAlert(criticalCount);
                    log.warn("Critical errors alert created: {} errors in last 24h", criticalCount);
                }
            }

        } catch (Exception e) {
            log.error("Error checking critical errors", e);
        }
    }

    /**
     * Créer une alerte CPU
     */
    private void createCpuAlert(double currentValue, int threshold) {
        SystemAlert alert = new SystemAlert();
        alert.setLevel("WARNING");
        alert.setTitle("Utilisation CPU élevée");
        alert.setMessage(String.format("L'utilisation CPU dépasse le seuil configuré"));
        alert.setTimestamp(LocalDateTime.now());
        alert.setAcknowledged(false);
        alert.setSource("CPU");
        alert.setAlertType("THRESHOLD");
        alert.setCurrentValue(currentValue);
        alert.setThresholdValue((double) threshold);
        alert.setUnit("%");
        alert.setRecommendedActions("Vérifier les processus en cours d'exécution. Redémarrer les services si nécessaire.");

        systemAlertRepository.persist(alert);
    }

    /**
     * Créer une alerte mémoire
     */
    private void createMemoryAlert(double currentValue, int threshold) {
        SystemAlert alert = new SystemAlert();
        alert.setLevel("WARNING");
        alert.setTitle("Utilisation mémoire élevée");
        alert.setMessage(String.format("L'utilisation mémoire dépasse le seuil configuré"));
        alert.setTimestamp(LocalDateTime.now());
        alert.setAcknowledged(false);
        alert.setSource("MEMORY");
        alert.setAlertType("THRESHOLD");
        alert.setCurrentValue(currentValue);
        alert.setThresholdValue((double) threshold);
        alert.setUnit("%");
        alert.setRecommendedActions("Vérifier les applications consommant de la mémoire. Considérer l'augmentation des ressources.");

        systemAlertRepository.persist(alert);
    }

    /**
     * Créer une alerte pour erreurs critiques
     */
    private void createCriticalErrorsAlert(long errorCount) {
        SystemAlert alert = new SystemAlert();
        alert.setLevel("CRITICAL");
        alert.setTitle("Erreurs critiques détectées");
        alert.setMessage(String.format("%d erreurs critiques détectées dans les dernières 24h", errorCount));
        alert.setTimestamp(LocalDateTime.now());
        alert.setAcknowledged(false);
        alert.setSource("System");
        alert.setAlertType("ERROR");
        alert.setCurrentValue((double) errorCount);
        alert.setThresholdValue(5.0);
        alert.setUnit("erreurs");
        alert.setRecommendedActions("Consulter les logs système pour identifier la cause. Corriger les erreurs critiques.");

        systemAlertRepository.persist(alert);
    }

    /**
     * Vérifier si une alerte CPU a été créée récemment (dans les 30 dernières minutes)
     */
    private boolean hasRecentCpuAlert() {
        LocalDateTime thirtyMinutesAgo = LocalDateTime.now().minusMinutes(30);
        return systemAlertRepository.findByTimestampBetween(thirtyMinutesAgo, LocalDateTime.now()).stream()
                .anyMatch(alert -> "CPU".equals(alert.getSource()) && !alert.getAcknowledged());
    }

    /**
     * Vérifier si une alerte mémoire a été créée récemment (dans les 30 dernières minutes)
     */
    private boolean hasRecentMemoryAlert() {
        LocalDateTime thirtyMinutesAgo = LocalDateTime.now().minusMinutes(30);
        return systemAlertRepository.findByTimestampBetween(thirtyMinutesAgo, LocalDateTime.now()).stream()
                .anyMatch(alert -> "MEMORY".equals(alert.getSource()) && !alert.getAcknowledged());
    }

    /**
     * Vérifier si une alerte erreurs critiques a été créée récemment (dans la dernière heure)
     */
    private boolean hasRecentCriticalErrorsAlert() {
        LocalDateTime oneHourAgo = LocalDateTime.now().minusHours(1);
        return systemAlertRepository.findByTimestampBetween(oneHourAgo, LocalDateTime.now()).stream()
                .anyMatch(alert -> "System".equals(alert.getSource()) && "ERROR".equals(alert.getAlertType()) && !alert.getAcknowledged());
    }
}
