package dev.lions.config;

import jakarta.annotation.PostConstruct;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;

import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import org.eclipse.microprofile.config.inject.ConfigProperty;
import dev.lions.exceptions.ConfigurationException;

import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Configuration centrale de l'application Lions Dev.
 * Cette classe gère l'ensemble des paramètres de configuration de manière thread-safe
 * et fournit une interface unifiée pour accéder aux différentes configurations.
 *
 * @author Lions Dev Team
 * @version 2.0
 */
@Slf4j
@ApplicationScoped
@Getter
public class ApplicationConfig {

  /**
   * Énumération des environnements d'exécution supportés.
   */
  public enum Environment {
    DEVELOPMENT("development"),
    STAGING("staging"),
    PRODUCTION("production");

    private final String value;

    Environment(String value) {
      this.value = value;
    }

    public String getValue() {
      return value;
    }

    public static Environment fromString(String value) {
      return Arrays.stream(values())
                   .filter(env -> env.getValue().equalsIgnoreCase(value))
                   .findFirst()
                   .orElse(DEVELOPMENT);
    }
  }

  // Constantes de configuration
  private static final String DEFAULT_ENVIRONMENT = "development";
  private static final long DEFAULT_MAX_FILE_SIZE = 10_485_760L; // 10MB
  private static final int DEFAULT_CACHE_SIZE = 1000;
  private static final int MIN_PORT = 1;
  private static final int MAX_PORT = 65535;

  // Configuration de base de l'application
  @Inject
  @ConfigProperty(name = "app.name", defaultValue = "Lions Dev")
  private String applicationName;

  @Inject
  @ConfigProperty(name = "app.environment", defaultValue = DEFAULT_ENVIRONMENT)
  private String environment;

  @NotBlank
  @Inject
  @ConfigProperty(name = "app.base-url")
  private String baseUrl;

  // Configuration du stockage
  @NotBlank
  @Inject
  @ConfigProperty(name = "app.storage.base-path")
  private String storageBasePath;

  @NotBlank
  @Inject
  @ConfigProperty(name = "app.storage.images.path", defaultValue = "images")
  private String imageStoragePath;

  @Inject
  @ConfigProperty(name = "app.storage.allowed-types", defaultValue = "jpg,jpeg,png,gif")
  private String allowedFileTypes;

  @Min(1_048_576L) // 1MB minimum
  @Max(104_857_600L) // 100MB maximum
  @Inject
  @ConfigProperty(name = "app.storage.max-size", defaultValue = "10485760")
  private Long maxFileSize;

  // Configuration des emails
  @NotBlank
  @Inject
  @ConfigProperty(name = "app.email.from")
  private String emailFrom;

  @NotBlank
  @Inject
  @ConfigProperty(name = "app.email.support")
  private String emailSupport;

  @Inject
  @ConfigProperty(name = "app.email.template-path", defaultValue = "templates/email")
  private String emailTemplatePath;

  // Configuration SMTP
  @NotBlank
  @Inject
  @ConfigProperty(name = "app.smtp.host")
  private String smtpHost;

  @Min(MIN_PORT)
  @Max(MAX_PORT)
  @Inject
  @ConfigProperty(name = "app.smtp.port")
  private Integer smtpPort;

  @Inject
  @ConfigProperty(name = "app.smtp.username")
  private Optional<String> smtpUsername;

  @Inject
  @ConfigProperty(name = "app.smtp.password")
  private Optional<String> smtpPassword;

  @NotBlank
  @Inject
  @ConfigProperty(name = "app.admin.email")
  private String adminEmailAddress;

  // Collections thread-safe pour les configurations dynamiques
  private final Map<String, String> applicationUrls = new ConcurrentHashMap<>();
  private final Map<Environment, String> environmentConfigs = new EnumMap<>(Environment.class);
  private List<String> allowedFileTypesList;

  /**
   * Initialise la configuration après l'injection des propriétés.
   * Valide et prépare l'ensemble des paramètres de configuration.
   *
   * @throws ConfigurationException si la configuration est invalide
   */
  @PostConstruct
  void initialize() {
    try {
      log.info("Initialisation de la configuration de l'application: {}", applicationName);
      validateConfiguration();
      initializeApplicationUrls();
      initializeAllowedFileTypes();
      initializeEnvironmentConfigs();
      log.info("Configuration initialisée avec succès en environnement: {}", environment);
    } catch (Exception e) {
      String errorMessage = "Erreur lors de l'initialisation de la configuration";
      log.error(errorMessage, e);
      throw new ConfigurationException(errorMessage, e);
    }
  }

  /**
   * Valide l'ensemble de la configuration.
   *
   * @throws ConfigurationException si la validation échoue
   */
  private void validateConfiguration() {
    log.debug("Validation de la configuration");
    validateEnvironment();
    validateStoragePaths();
    validateSmtpConfiguration();
    validateFileSize();
  }

  /**
   * Valide l'environnement d'exécution.
   */
  private void validateEnvironment() {
    if (!isValidEnvironment(environment)) {
      throw new ConfigurationException("Environnement non reconnu: " + environment);
    }
  }

  /**
   * Valide les chemins de stockage.
   */
  private void validateStoragePaths() {
    Path basePath = Paths.get(storageBasePath);
    validatePath(basePath, "stockage principal");

    Path imagesPath = basePath.resolve(imageStoragePath);
    validatePath(imagesPath, "stockage des images");
  }

  /**
   * Valide un chemin spécifique.
   */
  private void validatePath(Path path, String description) {
    if (!path.toFile().exists() && !path.toFile().mkdirs()) {
      throw new ConfigurationException(
          "Impossible de créer le répertoire de " + description + ": " + path);
    }
  }

  /**
   * Valide la configuration SMTP.
   */
  private void validateSmtpConfiguration() {
    if (isSmtpConfigured() && (smtpPort < MIN_PORT || smtpPort > MAX_PORT)) {
      throw new ConfigurationException("Port SMTP invalide: " + smtpPort);
    }
  }

  /**
   * Valide la taille maximale des fichiers.
   */
  private void validateFileSize() {
    if (maxFileSize <= 0) {
      throw new ConfigurationException(
          "Taille maximale de fichier invalide: " + maxFileSize);
    }
  }

  /**
   * Récupère l'adresse email système (expéditeur par défaut).
   *
   * @return Adresse email système
   */
  public String getSystemEmailAddress() {
    return emailFrom;
  }

  /**
   * Vérifie si le SSL est activé pour le serveur SMTP.
   *
   * @return true si SSL est activé, sinon false
   */
  public boolean isSmtpSslEnabled() {
    return smtpPort == 465; // Port 465 est commun pour SMTP avec SSL
  }

  /**
   * Initialise les URLs de l'application.
   */
  private void initializeApplicationUrls() {
    applicationUrls.clear();
    applicationUrls.put("home", "/");
    applicationUrls.put("services", "/services");
    applicationUrls.put("contact", "/contact");
    applicationUrls.put("admin", "/admin");
    applicationUrls.put("projects", "/projects");
    applicationUrls.put("portfolio", "/portfolio");
  }

  /**
   * Initialise la liste des types de fichiers autorisés.
   */
  private void initializeAllowedFileTypes() {
    allowedFileTypesList = Collections.unmodifiableList(
        Arrays.asList(allowedFileTypes.toLowerCase().split(","))
    );
  }

  /**
   * Initialise les configurations spécifiques aux environnements.
   */
  private void initializeEnvironmentConfigs() {
    environmentConfigs.put(Environment.DEVELOPMENT, "dev");
    environmentConfigs.put(Environment.STAGING, "stage");
    environmentConfigs.put(Environment.PRODUCTION, "prod");
  }

  // Méthodes publiques utilitaires

  /**
   * Récupère le chemin complet pour le stockage des images.
   */
  public String getImageStoragePath() {
    return Paths.get(storageBasePath, imageStoragePath).toString();
  }

  /**
   * Vérifie si un type de fichier est autorisé.
   */
  public boolean isFileTypeAllowed(String fileType) {
    return fileType != null && allowedFileTypesList.contains(fileType.toLowerCase().trim());
  }

  /**
   * Récupère l'URL d'une section de l'application.
   */
  public String getUrl(String key) {
    return applicationUrls.getOrDefault(key, "/");
  }

  /**
   * Vérifie si l'environnement est en développement.
   */
  public boolean isDevelopment() {
    return Environment.DEVELOPMENT.getValue().equals(environment);
  }

  /**
   * Vérifie si l'environnement est en production.
   */
  public boolean isProduction() {
    return Environment.PRODUCTION.getValue().equals(environment);
  }

  /**
   * Vérifie si la configuration SMTP est complète.
   */
  public boolean isSmtpConfigured() {
    return smtpUsername.isPresent() && smtpPassword.isPresent() &&
           smtpHost != null && !smtpHost.equals("localhost");
  }

  /**
   * Vérifie si un environnement est valide.
   */
  private boolean isValidEnvironment(String env) {
    return Arrays.stream(Environment.values())
                 .anyMatch(e -> e.getValue().equals(env));
  }
}