package dev.lions.unionflow.server.service;

import dev.lions.unionflow.server.api.dto.system.request.UpdateSystemConfigRequest;
import dev.lions.unionflow.server.api.dto.system.response.CacheStatsResponse;
import dev.lions.unionflow.server.api.dto.system.response.SystemConfigResponse;
import dev.lions.unionflow.server.api.dto.system.response.SystemTestResultResponse;
import io.quarkus.cache.CacheManager;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.microprofile.config.inject.ConfigProperty;

import javax.sql.DataSource;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.OperatingSystemMXBean;
import java.sql.Connection;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

/**
 * Service de gestion de la configuration système
 */
@Slf4j
@ApplicationScoped
public class SystemConfigService {

    @Inject
    CacheManager cacheManager;

    @Inject
    DataSource dataSource;

    @ConfigProperty(name = "quarkus.application.name", defaultValue = "UnionFlow")
    String applicationName;

    @ConfigProperty(name = "quarkus.application.version", defaultValue = "1.0.0")
    String applicationVersion;

    private final LocalDateTime startTime = LocalDateTime.now();
    private final AtomicReference<UpdateSystemConfigRequest> configOverrides = new AtomicReference<>(null);

    /**
     * Récupérer la configuration système complète
     */
    public SystemConfigResponse getSystemConfig() {
        log.debug("Récupération de la configuration système");

        UpdateSystemConfigRequest overrides = configOverrides.get();
        long uptimeMs = java.time.Duration.between(startTime, LocalDateTime.now()).toMillis();

        return SystemConfigResponse.builder()
                // Configuration générale
                .applicationName(overrides != null && overrides.getApplicationName() != null
                        ? overrides.getApplicationName() : applicationName)
                .version(applicationVersion)
                .timezone(overrides != null && overrides.getTimezone() != null
                        ? overrides.getTimezone() : "UTC")
                .defaultLanguage(overrides != null && overrides.getDefaultLanguage() != null
                        ? overrides.getDefaultLanguage() : "fr")
                .maintenanceMode(overrides != null && overrides.getMaintenanceMode() != null
                        ? overrides.getMaintenanceMode() : false)
                .lastUpdated(LocalDateTime.now())

                // Configuration réseau
                .networkTimeout(overrides != null && overrides.getNetworkTimeout() != null
                        ? overrides.getNetworkTimeout() : 30)
                .maxRetries(overrides != null && overrides.getMaxRetries() != null
                        ? overrides.getMaxRetries() : 3)
                .connectionPoolSize(overrides != null && overrides.getConnectionPoolSize() != null
                        ? overrides.getConnectionPoolSize() : 10)

                // Configuration sécurité
                .twoFactorAuthEnabled(overrides != null && overrides.getTwoFactorAuthEnabled() != null
                        ? overrides.getTwoFactorAuthEnabled() : false)
                .sessionTimeoutMinutes(overrides != null && overrides.getSessionTimeoutMinutes() != null
                        ? overrides.getSessionTimeoutMinutes() : 30)
                .auditLoggingEnabled(overrides != null && overrides.getAuditLoggingEnabled() != null
                        ? overrides.getAuditLoggingEnabled() : true)

                // Configuration performance
                .metricsCollectionEnabled(overrides != null && overrides.getMetricsCollectionEnabled() != null
                        ? overrides.getMetricsCollectionEnabled() : true)
                .metricsIntervalSeconds(overrides != null && overrides.getMetricsIntervalSeconds() != null
                        ? overrides.getMetricsIntervalSeconds() : 5)
                .performanceOptimizationEnabled(overrides != null && overrides.getPerformanceOptimizationEnabled() != null
                        ? overrides.getPerformanceOptimizationEnabled() : true)

                // Configuration backup
                .autoBackupEnabled(overrides != null && overrides.getAutoBackupEnabled() != null
                        ? overrides.getAutoBackupEnabled() : true)
                .backupFrequency(overrides != null && overrides.getBackupFrequency() != null
                        ? overrides.getBackupFrequency() : "DAILY")
                .backupRetentionDays(overrides != null && overrides.getBackupRetentionDays() != null
                        ? overrides.getBackupRetentionDays() : 30)
                .lastBackup(LocalDateTime.now().minusHours(2))

                // Configuration logs
                .logLevel(overrides != null && overrides.getLogLevel() != null
                        ? overrides.getLogLevel() : "INFO")
                .logRetentionDays(overrides != null && overrides.getLogRetentionDays() != null
                        ? overrides.getLogRetentionDays() : 30)
                .detailedLoggingEnabled(overrides != null && overrides.getDetailedLoggingEnabled() != null
                        ? overrides.getDetailedLoggingEnabled() : true)
                .logCompressionEnabled(overrides != null && overrides.getLogCompressionEnabled() != null
                        ? overrides.getLogCompressionEnabled() : true)

                // Configuration monitoring
                .realTimeMonitoringEnabled(overrides != null && overrides.getRealTimeMonitoringEnabled() != null
                        ? overrides.getRealTimeMonitoringEnabled() : true)
                .monitoringIntervalSeconds(overrides != null && overrides.getMonitoringIntervalSeconds() != null
                        ? overrides.getMonitoringIntervalSeconds() : 5)
                .emailAlertsEnabled(overrides != null && overrides.getEmailAlertsEnabled() != null
                        ? overrides.getEmailAlertsEnabled() : true)
                .pushAlertsEnabled(overrides != null && overrides.getPushAlertsEnabled() != null
                        ? overrides.getPushAlertsEnabled() : false)

                // Configuration alertes
                .cpuHighAlertEnabled(overrides != null && overrides.getCpuHighAlertEnabled() != null
                        ? overrides.getCpuHighAlertEnabled() : true)
                .cpuThresholdPercent(overrides != null && overrides.getCpuThresholdPercent() != null
                        ? overrides.getCpuThresholdPercent() : 80)
                .memoryLowAlertEnabled(overrides != null && overrides.getMemoryLowAlertEnabled() != null
                        ? overrides.getMemoryLowAlertEnabled() : true)
                .memoryThresholdPercent(overrides != null && overrides.getMemoryThresholdPercent() != null
                        ? overrides.getMemoryThresholdPercent() : 85)
                .criticalErrorAlertEnabled(overrides != null && overrides.getCriticalErrorAlertEnabled() != null
                        ? overrides.getCriticalErrorAlertEnabled() : true)
                .connectionFailureAlertEnabled(overrides != null && overrides.getConnectionFailureAlertEnabled() != null
                        ? overrides.getConnectionFailureAlertEnabled() : true)
                .connectionFailureThreshold(overrides != null && overrides.getConnectionFailureThreshold() != null
                        ? overrides.getConnectionFailureThreshold() : 100)

                // Statut système
                .systemStatus("OPERATIONAL")
                .uptime(uptimeMs)
                .build();
    }

    /**
     * Mettre à jour la configuration système
     */
    public SystemConfigResponse updateSystemConfig(UpdateSystemConfigRequest request) {
        log.info("Mise à jour de la configuration système");
        configOverrides.set(request);
        log.info("Configuration système mise à jour en mémoire");
        return getSystemConfig();
    }

    /**
     * Récupérer les statistiques du cache
     */
    public CacheStatsResponse getCacheStats() {
        log.debug("Récupération des statistiques du cache");

        MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
        long heapUsed = memoryBean.getHeapMemoryUsage().getUsed();

        Map<String, CacheStatsResponse.CacheEntry> caches = new HashMap<>();

        // Exemple de cache entries (simulé)
        caches.put("user-sessions", CacheStatsResponse.CacheEntry.builder()
                .name("user-sessions")
                .sizeBytes(1024L * 1024 * 50) // 50 MB
                .entries(1247)
                .hitRate(85.5)
                .hits(12450L)
                .misses(2100L)
                .lastAccessed(LocalDateTime.now().minusMinutes(5))
                .build()
        );

        caches.put("dashboard-data", CacheStatsResponse.CacheEntry.builder()
                .name("dashboard-data")
                .sizeBytes(1024L * 1024 * 30) // 30 MB
                .entries(500)
                .hitRate(92.3)
                .hits(8500L)
                .misses(720L)
                .lastAccessed(LocalDateTime.now().minusMinutes(2))
                .build()
        );

        return CacheStatsResponse.builder()
                .totalSizeBytes(heapUsed)
                .totalSizeFormatted(formatBytes(heapUsed))
                .totalEntries(1747)
                .hitRate(88.2)
                .hits(20950L)
                .misses(2820L)
                .lastCleared(LocalDateTime.now().minusHours(24))
                .caches(caches)
                .build();
    }

    /**
     * Vider le cache système
     */
    public void clearCache() {
        log.info("Nettoyage du cache système");

        try {
            cacheManager.getCacheNames().forEach(cacheName -> {
                log.debug("Invalidation du cache: {}", cacheName);
                cacheManager.getCache(cacheName).ifPresent(cache -> cache.invalidateAll().await().indefinitely());
            });
            log.info("Cache système vidé avec succès");
        } catch (Exception e) {
            log.error("Erreur lors du nettoyage du cache", e);
            throw new RuntimeException("Erreur lors du nettoyage du cache: " + e.getMessage());
        }
    }

    /**
     * Tester la connexion à la base de données
     */
    public SystemTestResultResponse testDatabaseConnection() {
        log.info("Test de la connexion à la base de données");

        long startTime = System.currentTimeMillis();
        try {
            try (Connection connection = dataSource.getConnection()) {
                boolean isValid = connection.isValid(5); // Timeout 5 secondes
                long responseTime = System.currentTimeMillis() - startTime;

                return SystemTestResultResponse.builder()
                        .testType("DATABASE")
                        .success(isValid)
                        .message(isValid ? "Connexion à la base de données réussie" : "Échec de la connexion")
                        .responseTimeMs(responseTime)
                        .testedAt(LocalDateTime.now())
                        .details("Connection pool actif")
                        .build();
            }
        } catch (Exception e) {
            long responseTime = System.currentTimeMillis() - startTime;
            log.error("Erreur lors du test de connexion DB", e);

            return SystemTestResultResponse.builder()
                    .testType("DATABASE")
                    .success(false)
                    .message("Erreur de connexion: " + e.getMessage())
                    .responseTimeMs(responseTime)
                    .testedAt(LocalDateTime.now())
                    .details(e.getClass().getSimpleName())
                    .build();
        }
    }

    /**
     * Tester la configuration email (simulé)
     */
    public SystemTestResultResponse testEmailConfiguration() {
        log.info("Test de la configuration email");

        long startTime = System.currentTimeMillis();

        // Dans une vraie implémentation, on enverrait un email de test
        // Pour l'instant, on simule le succès
        try {
            Thread.sleep(500); // Simule le temps d'envoi
            long responseTime = System.currentTimeMillis() - startTime;

            return SystemTestResultResponse.builder()
                    .testType("EMAIL")
                    .success(true)
                    .message("Configuration email valide (test simulé)")
                    .responseTimeMs(responseTime)
                    .testedAt(LocalDateTime.now())
                    .details("SMTP configuré")
                    .build();
        } catch (Exception e) {
            long responseTime = System.currentTimeMillis() - startTime;

            return SystemTestResultResponse.builder()
                    .testType("EMAIL")
                    .success(false)
                    .message("Erreur: " + e.getMessage())
                    .responseTimeMs(responseTime)
                    .testedAt(LocalDateTime.now())
                    .details(e.getClass().getSimpleName())
                    .build();
        }
    }

    /**
     * Formater les bytes en format lisible
     */
    private String formatBytes(long bytes) {
        if (bytes < 1024) return bytes + " B";
        int exp = (int) (Math.log(bytes) / Math.log(1024));
        char pre = "KMGTPE".charAt(exp - 1);
        return String.format("%.1f %sB", bytes / Math.pow(1024, exp), pre);
    }
}
