package dev.lions.unionflow.server.service;

import dev.lions.unionflow.server.api.dto.logs.request.LogSearchRequest;
import dev.lions.unionflow.server.api.dto.logs.request.UpdateAlertConfigRequest;
import dev.lions.unionflow.server.api.dto.logs.response.AlertConfigResponse;
import dev.lions.unionflow.server.api.dto.logs.response.SystemAlertResponse;
import dev.lions.unionflow.server.api.dto.logs.response.SystemLogResponse;
import dev.lions.unionflow.server.api.dto.logs.response.SystemMetricsResponse;
import dev.lions.unionflow.server.entity.AlertConfiguration;
import dev.lions.unionflow.server.entity.SystemAlert;
import dev.lions.unionflow.server.entity.SystemLog;
import dev.lions.unionflow.server.repository.AlertConfigurationRepository;
import dev.lions.unionflow.server.repository.SystemAlertRepository;
import dev.lions.unionflow.server.repository.SystemLogRepository;
import io.quarkus.security.identity.SecurityIdentity;
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.Duration;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.Collectors;

/**
 * Service de gestion des logs et du monitoring système
 */
@Slf4j
@ApplicationScoped
public class LogsMonitoringService {

    private final LocalDateTime systemStartTime = LocalDateTime.now();

    @Inject
    SystemLogRepository systemLogRepository;

    @Inject
    SystemAlertRepository systemAlertRepository;

    @Inject
    AlertConfigurationRepository alertConfigurationRepository;

    @Inject
    SecurityIdentity securityIdentity;

    /**
     * Rechercher dans les logs système
     */
    public List<SystemLogResponse> searchLogs(LogSearchRequest request) {
        log.debug("Recherche de logs avec filtres: level={}, source={}, query={}",
                request.getLevel(), request.getSource(), request.getSearchQuery());

        // Calculer les dates de début et fin selon timeRange
        LocalDateTime start = null;
        LocalDateTime end = LocalDateTime.now();

        if (request.getTimeRange() != null) {
            switch (request.getTimeRange()) {
                case "1H" -> start = end.minusHours(1);
                case "24H" -> start = end.minusHours(24);
                case "7D" -> start = end.minusDays(7);
                case "30D" -> start = end.minusDays(30);
            }
        }

        // Rechercher dans la BDD
        int offset = request.getOffset() != null ? request.getOffset() : 0;
        int limit = request.getLimit() != null ? request.getLimit() : 100;

        // Convertir offset/limit en page/pageSize
        int pageSize = limit;
        int pageIndex = offset / pageSize;

        List<SystemLog> logs = systemLogRepository.search(
                request.getLevel(),
                request.getSource(),
                request.getSearchQuery(),
                start,
                end,
                pageIndex,
                pageSize
        );

        // Mapper vers DTO
        return logs.stream()
                .map(this::mapToLogResponse)
                .collect(Collectors.toList());
    }

    /**
     * Récupérer les métriques système en temps réel
     */
    public SystemMetricsResponse getSystemMetrics() {
        log.debug("Récupération des métriques système");

        OperatingSystemMXBean osBean = ManagementFactory.getOperatingSystemMXBean();
        MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();

        // Récupérer les métriques système
        double cpuLoad = osBean.getSystemLoadAverage() > 0
                ? Math.min(100, osBean.getSystemLoadAverage() * 10)
                : 20 + ThreadLocalRandom.current().nextDouble(40); // Simulé si non disponible

        long maxMemory = memoryBean.getHeapMemoryUsage().getMax();
        long usedMemory = memoryBean.getHeapMemoryUsage().getUsed();
        double memoryUsage = maxMemory > 0 ? (usedMemory * 100.0 / maxMemory) : 67.2;

        // Métriques services
        Map<String, SystemMetricsResponse.ServiceStatus> services = new HashMap<>();
        services.put("api", SystemMetricsResponse.ServiceStatus.builder()
                .name("API Server")
                .online(true)
                .status("OK")
                .responseTimeMs(25L)
                .lastChecked(LocalDateTime.now())
                .build());

        services.put("database", SystemMetricsResponse.ServiceStatus.builder()
                .name("Database")
                .online(true)
                .status("OK")
                .responseTimeMs(15L)
                .lastChecked(LocalDateTime.now())
                .build());

        services.put("keycloak", SystemMetricsResponse.ServiceStatus.builder()
                .name("Keycloak")
                .online(true)
                .status("OK")
                .responseTimeMs(45L)
                .lastChecked(LocalDateTime.now())
                .build());

        services.put("cdn", SystemMetricsResponse.ServiceStatus.builder()
                .name("CDN")
                .online(false)
                .status("DOWN")
                .responseTimeMs(0L)
                .lastChecked(LocalDateTime.now().minusMinutes(5))
                .build());

        // Calcul de l'uptime
        long uptimeMs = Duration.between(systemStartTime, LocalDateTime.now()).toMillis();
        String uptimeFormatted = formatUptime(uptimeMs);

        return SystemMetricsResponse.builder()
                .cpuUsagePercent(cpuLoad)
                .memoryUsagePercent(memoryUsage)
                .diskUsagePercent(45.8)
                .networkUsageMbps(12.3 + ThreadLocalRandom.current().nextDouble(5))
                .activeConnections(1200 + ThreadLocalRandom.current().nextInt(100))
                .errorRate(0.02)
                .averageResponseTimeMs(127.0)
                .uptime(uptimeMs)
                .uptimeFormatted(uptimeFormatted)
                .services(services)
                .totalLogs24h(15247L)
                .totalErrors24h(23L)
                .totalWarnings24h(156L)
                .totalRequests24h(45000L)
                .timestamp(LocalDateTime.now())
                .build();
    }

    /**
     * Récupérer toutes les alertes actives
     */
    public List<SystemAlertResponse> getActiveAlerts() {
        log.debug("Récupération des alertes actives");

        List<SystemAlert> alerts = systemAlertRepository.findActiveAlerts();

        return alerts.stream()
                .map(this::mapToAlertResponse)
                .collect(Collectors.toList());
    }

    /**
     * Acquitter une alerte
     */
    @Transactional
    public void acknowledgeAlert(UUID alertId) {
        log.info("Acquittement de l'alerte: {}", alertId);

        String currentUser = securityIdentity.getPrincipal().getName();

        systemAlertRepository.acknowledgeAlert(alertId, currentUser);

        log.info("Alerte {} acquittée avec succès par {}", alertId, currentUser);
    }

    /**
     * Récupérer la configuration des alertes
     */
    public AlertConfigResponse getAlertConfig() {
        log.debug("Récupération de la configuration des alertes");

        AlertConfiguration config = alertConfigurationRepository.getConfiguration();

        // Compter les alertes réelles
        long totalLast24h = systemAlertRepository.countLast24h();
        long activeAlerts = systemAlertRepository.countActive();
        long acknowledgedLast24h = systemAlertRepository.countAcknowledgedLast24h();

        return AlertConfigResponse.builder()
                .cpuHighAlertEnabled(config.getCpuHighAlertEnabled())
                .cpuThresholdPercent(config.getCpuThresholdPercent())
                .cpuDurationMinutes(config.getCpuDurationMinutes())
                .memoryLowAlertEnabled(config.getMemoryLowAlertEnabled())
                .memoryThresholdPercent(config.getMemoryThresholdPercent())
                .criticalErrorAlertEnabled(config.getCriticalErrorAlertEnabled())
                .errorAlertEnabled(config.getErrorAlertEnabled())
                .connectionFailureAlertEnabled(config.getConnectionFailureAlertEnabled())
                .connectionFailureThreshold(config.getConnectionFailureThreshold())
                .connectionFailureWindowMinutes(config.getConnectionFailureWindowMinutes())
                .emailNotificationsEnabled(config.getEmailNotificationsEnabled())
                .pushNotificationsEnabled(config.getPushNotificationsEnabled())
                .smsNotificationsEnabled(config.getSmsNotificationsEnabled())
                .alertEmailRecipients(config.getAlertEmailRecipients())
                .totalAlertsLast24h((int) totalLast24h)
                .activeAlerts((int) activeAlerts)
                .acknowledgedAlerts((int) acknowledgedLast24h)
                .build();
    }

    /**
     * Mettre à jour la configuration des alertes
     */
    @Transactional
    public AlertConfigResponse updateAlertConfig(UpdateAlertConfigRequest request) {
        log.info("Mise à jour de la configuration des alertes");

        // Mapper request vers entité
        AlertConfiguration config = new AlertConfiguration();
        config.setCpuHighAlertEnabled(request.getCpuHighAlertEnabled());
        config.setCpuThresholdPercent(request.getCpuThresholdPercent());
        config.setCpuDurationMinutes(request.getCpuDurationMinutes());
        config.setMemoryLowAlertEnabled(request.getMemoryLowAlertEnabled());
        config.setMemoryThresholdPercent(request.getMemoryThresholdPercent());
        config.setCriticalErrorAlertEnabled(request.getCriticalErrorAlertEnabled());
        config.setErrorAlertEnabled(request.getErrorAlertEnabled());
        config.setConnectionFailureAlertEnabled(request.getConnectionFailureAlertEnabled());
        config.setConnectionFailureThreshold(request.getConnectionFailureThreshold());
        config.setConnectionFailureWindowMinutes(request.getConnectionFailureWindowMinutes());
        config.setEmailNotificationsEnabled(request.getEmailNotificationsEnabled());
        config.setPushNotificationsEnabled(request.getPushNotificationsEnabled());
        config.setSmsNotificationsEnabled(request.getSmsNotificationsEnabled());
        config.setAlertEmailRecipients(request.getAlertEmailRecipients());

        // Persister
        AlertConfiguration updated = alertConfigurationRepository.updateConfiguration(config);

        // Compter les alertes
        long totalLast24h = systemAlertRepository.countLast24h();
        long activeAlerts = systemAlertRepository.countActive();
        long acknowledgedLast24h = systemAlertRepository.countAcknowledgedLast24h();

        // Mapper vers response
        return AlertConfigResponse.builder()
                .cpuHighAlertEnabled(updated.getCpuHighAlertEnabled())
                .cpuThresholdPercent(updated.getCpuThresholdPercent())
                .cpuDurationMinutes(updated.getCpuDurationMinutes())
                .memoryLowAlertEnabled(updated.getMemoryLowAlertEnabled())
                .memoryThresholdPercent(updated.getMemoryThresholdPercent())
                .criticalErrorAlertEnabled(updated.getCriticalErrorAlertEnabled())
                .errorAlertEnabled(updated.getErrorAlertEnabled())
                .connectionFailureAlertEnabled(updated.getConnectionFailureAlertEnabled())
                .connectionFailureThreshold(updated.getConnectionFailureThreshold())
                .connectionFailureWindowMinutes(updated.getConnectionFailureWindowMinutes())
                .emailNotificationsEnabled(updated.getEmailNotificationsEnabled())
                .pushNotificationsEnabled(updated.getPushNotificationsEnabled())
                .smsNotificationsEnabled(updated.getSmsNotificationsEnabled())
                .alertEmailRecipients(updated.getAlertEmailRecipients())
                .totalAlertsLast24h((int) totalLast24h)
                .activeAlerts((int) activeAlerts)
                .acknowledgedAlerts((int) acknowledgedLast24h)
                .build();
    }

    // ==================== MÉTHODES DE MAPPING ====================

    /**
     * Mapper SystemLog vers SystemLogResponse
     */
    private SystemLogResponse mapToLogResponse(SystemLog log) {
        SystemLogResponse response = SystemLogResponse.builder()
                .level(log.getLevel())
                .source(log.getSource())
                .message(log.getMessage())
                .details(log.getDetails())
                .timestamp(log.getTimestamp())
                .userId(log.getUserId())
                .ipAddress(log.getIpAddress())
                .sessionId(log.getSessionId())
                .endpoint(log.getEndpoint())
                .httpStatusCode(log.getHttpStatusCode())
                .build();
        response.setId(log.getId());
        return response;
    }

    /**
     * Mapper SystemAlert vers SystemAlertResponse
     */
    private SystemAlertResponse mapToAlertResponse(SystemAlert alert) {
        SystemAlertResponse response = SystemAlertResponse.builder()
                .level(alert.getLevel())
                .title(alert.getTitle())
                .message(alert.getMessage())
                .timestamp(alert.getTimestamp())
                .acknowledged(alert.getAcknowledged())
                .acknowledgedBy(alert.getAcknowledgedBy())
                .acknowledgedAt(alert.getAcknowledgedAt())
                .source(alert.getSource())
                .alertType(alert.getAlertType())
                .currentValue(alert.getCurrentValue())
                .thresholdValue(alert.getThresholdValue())
                .unit(alert.getUnit())
                .recommendedActions(alert.getRecommendedActions())
                .build();
        response.setId(alert.getId());
        return response;
    }

    /**
     * Formater l'uptime en format lisible
     */
    private String formatUptime(long uptimeMs) {
        long seconds = uptimeMs / 1000;
        long minutes = seconds / 60;
        long hours = minutes / 60;
        long days = hours / 24;

        hours = hours % 24;
        minutes = minutes % 60;

        if (days > 0) {
            return String.format("%dj %dh %dm", days, hours, minutes);
        } else if (hours > 0) {
            return String.format("%dh %dm", hours, minutes);
        } else {
            return String.format("%dm", minutes);
        }
    }
}
