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.StorageEvent;
import dev.lions.exceptions.StorageConfigurationException;
import dev.lions.utils.SecurityUtils;

import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.FileAttribute;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.PosixFilePermissions;
import java.time.Duration;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Service de gestion avancée des configurations de stockage.
 * Assure la gestion sécurisée et optimisée des paramètres de stockage fichier
 * de l'application, avec validation complète et monitoring.
 *
 * @author Lions Dev Team
 * @version 2.1
 */
@Slf4j
@ApplicationScoped
public class StorageConfigService {

  private static final String DEFAULT_DIRECTORY_PERMISSIONS = "rwxr-x---";
  private static final long MIN_FREE_SPACE_BYTES = 100 * 1024 * 1024; // 100 MB
  private static final int CLEANUP_BATCH_SIZE = 100;
  private static final Duration CLEANUP_INTERVAL = Duration.ofHours(24);

  private final Map<String, Path> pathCache;
  private final Map<String, Boolean> fileTypeCache;
  private final ApplicationConfig appConfig;
  private final SecurityUtils securityUtils;

  @Inject
  Event<StorageEvent> storageEvent;

  /**
   * Initialise le service avec la configuration de l'application.
   *
   * @param appConfig     Configuration de l'application
   * @param securityUtils Utilitaires de sécurité
   */
  @Inject
  public StorageConfigService(@NotNull ApplicationConfig appConfig,
                              @NotNull SecurityUtils securityUtils) {
    this.appConfig = appConfig;
    this.securityUtils = securityUtils;
    this.pathCache = new ConcurrentHashMap<>();
    this.fileTypeCache = new ConcurrentHashMap<>();

    initializeStorage();
  }

  /**
   * Initialise et valide la configuration du stockage.
   */
  private void initializeStorage() {
    log.info("Initialisation de la configuration du stockage");

    try {
      createMainDirectories();
      configureSecurity();
      scheduleMaintenanceTasks();
      validateStorageCapacity();
      notifyStorageInitialized();

      log.info("Configuration du stockage initialisée avec succès");

    } catch (Exception e) {
      String message = "Erreur lors de l'initialisation du stockage";
      log.error(message, e);
      throw new StorageConfigurationException(message, e);
    }
  }

  /**
   * Crée les répertoires principaux nécessaires au stockage.
   */
  private void createMainDirectories() {
    log.debug("Création des répertoires principaux de stockage");

    String basePath = appConfig.getStorageBasePath();
    Set<String> requiredDirs = Set.of("images", "documents", "temp", "backup");

    requiredDirs.forEach(dir -> {
      Path dirPath = Paths.get(basePath, dir);
      createSecureDirectory(dirPath);
    });

    log.info("Répertoires principaux créés avec succès.");
  }

  /**
   * Configure les paramètres de sécurité pour la production.
   */
  private void applyProductionSecurity() {
    log.info("Application des paramètres de sécurité spécifiques pour la production");

    try {
      Path basePath = Paths.get(appConfig.getStorageBasePath());

      // Applique des permissions POSIX sécurisées
      Set<PosixFilePermission> permissions =
          PosixFilePermissions.fromString(DEFAULT_DIRECTORY_PERMISSIONS);

      Files.setPosixFilePermissions(basePath, permissions);
      log.info("Permissions POSIX sécurisées appliquées : {}", permissions);

      // Vérifie l'accès sécurisé
      if (!Files.isWritable(basePath) || !Files.isReadable(basePath)) {
        throw new StorageConfigurationException(
            "Permissions insuffisantes sur le répertoire de stockage : " + basePath);
      }

      log.debug("Sécurité des répertoires en production validée");

    } catch (IOException e) {
      log.error("Erreur lors de l'application des permissions de sécurité", e);
      throw new StorageConfigurationException(
          "Impossible d'appliquer les paramètres de sécurité en production", e);
    }
  }

  /**
   * Configure les paramètres de sécurité pour le stockage.
   */
  private void configureSecurity() {
    log.info("Configuration des paramètres de sécurité du stockage");

    try {
      if (appConfig.isProduction()) {
        applyProductionSecurity();
      }
      securityUtils.initializeEncryption();
      log.info("Sécurité du stockage configurée avec succès");
    } catch (Exception e) {
      throw new StorageConfigurationException("Erreur lors de la configuration de la sécurité", e);
    }
  }

  /**
   * Planifie les tâches de maintenance pour le stockage.
   */
  private void scheduleMaintenanceTasks() {
    log.info("Planification des tâches de maintenance du stockage");

    try {
      scheduleStorageCleanup();
      scheduleCapacityCheck();
      log.info("Tâches de maintenance planifiées avec succès.");
    } catch (Exception e) {
      throw new StorageConfigurationException("Erreur lors de la planification des tâches de maintenance", e);
    }
  }

  /**
   * Crée un répertoire sécurisé avec les permissions appropriées.
   */
  private void createSecureDirectory(Path path) {
    try {
      if (!Files.exists(path)) {
        FileAttribute<Set<PosixFilePermission>> attr = PosixFilePermissions
            .asFileAttribute(PosixFilePermissions.fromString(DEFAULT_DIRECTORY_PERMISSIONS));
        Files.createDirectories(path, attr);
        log.debug("Répertoire créé avec succès : {}", path);
      }
      validateDirectoryAccess(path);
    } catch (IOException e) {
      throw new StorageConfigurationException(
          "Impossible de créer le répertoire sécurisé : " + path, e);
    }
  }

  /**
   * Planifie le nettoyage automatique du stockage.
   */
  private void scheduleStorageCleanup() {
    log.info("Planification de la tâche de nettoyage automatique du stockage");
    // Simulation d'une tâche de nettoyage. Remplacer par un vrai scheduler si nécessaire.
    log.debug("Nettoyage automatique exécuté toutes les {} heures, batch size: {}",
              CLEANUP_INTERVAL.toHours(), CLEANUP_BATCH_SIZE);
  }

  /**
   * Planifie la vérification périodique de la capacité de stockage.
   */
  private void scheduleCapacityCheck() {
    log.info("Planification de la vérification périodique de la capacité de stockage");
    // Simulation d'une vérification périodique. À remplacer par un scheduler réel.
    log.debug("Vérification de la capacité planifiée toutes les {} heures", CLEANUP_INTERVAL.toHours());
  }

  /**
   * Valide la capacité de stockage disponible.
   */
  private void validateStorageCapacity() {
    try {
      Path storagePath = Paths.get(appConfig.getStorageBasePath());
      long freeSpace = Files.getFileStore(storagePath).getUsableSpace();

      if (freeSpace < MIN_FREE_SPACE_BYTES) {
        throw new StorageConfigurationException(
            "Espace de stockage insuffisant. Minimum requis : " +
            formatSize(MIN_FREE_SPACE_BYTES));
      }

      log.debug("Espace de stockage validé : {} disponible", formatSize(freeSpace));

    } catch (IOException e) {
      throw new StorageConfigurationException(
          "Impossible de vérifier l'espace de stockage disponible", e);
    }
  }

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

  /**
   * Notifie les observateurs de l'initialisation du stockage.
   */
  private void notifyStorageInitialized() {
    StorageEvent event = new StorageEvent(
        "STORAGE_INITIALIZED",
        Map.of(
            "basePath", appConfig.getStorageBasePath(),
            "environment", appConfig.getEnvironment()
        )
    );
    storageEvent.fire(event);
  }

  /**
   * Valide l'accès au répertoire.
   */
  private void validateDirectoryAccess(Path path) {
    if (!Files.isDirectory(path)) {
      throw new StorageConfigurationException(
          "Le chemin n'est pas un répertoire : " + path);
    }
    if (!Files.isWritable(path)) {
      throw new StorageConfigurationException(
          "Le répertoire n'est pas accessible en écriture : " + path);
    }
  }
}
