package dev.lions.config;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.event.Event;
import jakarta.inject.Inject;
import jakarta.validation.constraints.NotNull;

import lombok.extern.slf4j.Slf4j;

import dev.lions.events.ConfigurationEvent;
import dev.lions.exceptions.ConfigurationException;
import dev.lions.utils.EncryptionUtils;

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

/**
 * Service de gestion avancée de la configuration de l'application.
 * Fournit une interface enrichie pour accéder et gérer les paramètres de configuration
 * de manière thread-safe, sécurisée et optimisée.
 *
 * @author Lions Dev Team
 * @version 2.1
 */
@Slf4j
@ApplicationScoped
public class ApplicationConfigService {

  private static final long CACHE_DURATION_MS = 300_000; // 5 minutes
  private static final long MIN_DISK_SPACE_BYTES = 100 * 1024 * 1024; // 100 MB
  private static final String[] MANDATORY_DIRECTORIES = {"logs", "data", "temp"};

  private final ApplicationConfig applicationConfig;
  private final Map<String, CachedValue<Object>> configCache;
  private final AtomicLong lastHealthCheck;

  @Inject
  private Event<ConfigurationEvent> configurationEvent;

  @Inject
  private EncryptionUtils encryptionUtils;

  @Inject
  public ApplicationConfigService(@NotNull ApplicationConfig applicationConfig) {
    this.applicationConfig = applicationConfig;
    this.configCache = new ConcurrentHashMap<>();
    this.lastHealthCheck = new AtomicLong(0);

    initializeService();
  }

  /**
   * Valide la configuration actuelle de l'application.
   * Vérifie les chemins de stockage, les répertoires obligatoires et les paramètres critiques.
   */
  private void validateConfiguration() {
    log.info("Validation de la configuration de l'application...");

    try {
      validateStorageBasePath();
      validateMandatoryDirectories();
      validateSecuritySettings();

      log.info("Configuration de l'application validée avec succès.");
    } catch (ConfigurationException e) {
      log.error("Validation échouée : {}", e.getMessage());
      throw e; // Relancer l'exception après log
    } catch (Exception e) {
      log.error("Erreur inattendue lors de la validation de la configuration", e);
      throw new ConfigurationException("Erreur inattendue lors de la validation", e);
    }
  }

  /**
   * Vérifie l'existence et l'accessibilité du chemin de stockage.
   */
  private void validateStorageBasePath() {
    Path storagePath = Paths.get(applicationConfig.getStorageBasePath());
    if (!Files.exists(storagePath) || !Files.isDirectory(storagePath)) {
      throw new ConfigurationException("Le chemin de stockage est invalide : " + storagePath);
    }
    log.debug("Chemin de stockage validé : {}", storagePath);
  }

  /**
   * Vérifie l'existence des répertoires obligatoires.
   */
  private void validateMandatoryDirectories() {
    String basePath = applicationConfig.getStorageBasePath();
    for (String directory : MANDATORY_DIRECTORIES) {
      Path directoryPath = Paths.get(basePath, directory);
      if (!Files.exists(directoryPath) || !Files.isWritable(directoryPath)) {
        throw new ConfigurationException("Répertoire obligatoire manquant ou inaccessible : " + directoryPath);
      }
      log.debug("Répertoire valide : {}", directoryPath);
    }
  }

  /**
   * Valide les paramètres de sécurité essentiels.
   */
  private void validateSecuritySettings() {
    if (applicationConfig.isProduction()) {
      if (!applicationConfig.isSmtpConfigured()) {
        throw new ConfigurationException("La configuration SMTP est obligatoire en production");
      }
      log.debug("Paramètres SMTP validés pour l'environnement production");
    }
    log.debug("Paramètres de sécurité validés");
  }

  private void initializeService() {
    try {
      log.info("Initialisation du service de configuration");

      createMandatoryDirectories();
      validateConfiguration();
      initializeCache();

      notifyServiceInitialized();

      log.info("Service de configuration initialisé avec succès");

    } catch (Exception e) {
      log.error("Erreur lors de l'initialisation du service de configuration", e);
      throw new ConfigurationException("Échec de l'initialisation du service", e);
    }
  }

  private void createMandatoryDirectories() {
    String basePath = applicationConfig.getStorageBasePath();

    for (String directory : MANDATORY_DIRECTORIES) {
      Path directoryPath = Paths.get(basePath, directory);
      if (!Files.exists(directoryPath)) {
        try {
          Files.createDirectories(directoryPath);
          log.debug("Répertoire créé : {}", directoryPath);
        } catch (Exception e) {
          throw new ConfigurationException("Impossible de créer le répertoire : " + directoryPath, e);
        }
      }
    }
  }

  private void initializeCache() {
    log.info("Initialisation du cache de configuration...");
    configCache.clear();
  }

  private void notifyServiceInitialized() {
    ConfigurationEvent event = new ConfigurationEvent(
        "SERVICE_INITIALIZED",
        Map.of("timestamp", System.currentTimeMillis(),
               "environment", applicationConfig.getEnvironment())
    );
    configurationEvent.fire(event);
  }

  /**
   * Classe interne représentant une valeur mise en cache.
   */
  private static class CachedValue<T> {
    private final T value;
    private final long timestamp;

    CachedValue(T value) {
      this.value = value;
      this.timestamp = System.currentTimeMillis();
    }

    T getValue() {
      return value;
    }
  }
}
