package dev.lions.exceptions;

import java.util.Map;
import lombok.Builder;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

/**
 * Exception spécialisée pour la gestion des erreurs d'initialisation.
 * Cette classe traite les erreurs survenant lors de l'initialisation
 * des composants, services et ressources de l'application.
 *
 * @author Lions Dev Team
 * @version 1.0
 */
@Slf4j
public class InitializationException extends BusinessException {

  private static final long serialVersionUID = 1L;

  /**
   * Contexte détaillé de l'erreur d'initialisation
   */
  private final InitializationContext context;

  /**
   * Phase d'initialisation durant laquelle l'erreur est survenue
   */
  private final InitializationPhase phase;

  /**
   * Crée une nouvelle instance avec un message d'erreur simple.
   *
   * @param message Description de l'erreur
   */
  public InitializationException(String message) {
    this(message, null, null, null);
    log.error("Erreur d'initialisation : {}", message);
  }

  /**
   * Crée une nouvelle instance avec un message et une cause.
   *
   * @param message Description de l'erreur
   * @param cause Exception à l'origine de l'erreur
   */
  public InitializationException(String message, Throwable cause) {
    this(message, cause, null, null);
    log.error("Erreur d'initialisation : {}", message, cause);
  }

  /**
   * Crée une nouvelle instance avec tous les détails de l'erreur.
   *
   * @param message Description de l'erreur
   * @param cause Exception à l'origine de l'erreur
   * @param context Contexte de l'initialisation
   * @param phase Phase d'initialisation
   */
  public InitializationException(String message, Throwable cause,
                                 InitializationContext context, InitializationPhase phase) {
    super(message, cause);
    this.context = context;
    this.phase = phase;
    log.error("Erreur d'initialisation détaillée : {} - Phase : {} - Contexte : {}",
              message, phase, context);
  }

  /**
   * Récupère le contexte de l'erreur d'initialisation.
   *
   * @return Contexte de l'erreur ou null si non disponible
   */
  public InitializationContext getContext() {
    return context;
  }

  /**
   * Récupère la phase d'initialisation.
   *
   * @return Phase d'initialisation ou null si non disponible
   */
  public InitializationPhase getPhase() {
    return phase;
  }

  /**
   * Représente les différentes phases d'initialisation possibles.
   */
  public enum InitializationPhase {
    CONFIGURATION("Configuration"),
    RESOURCE_LOADING("Chargement des ressources"),
    DATABASE("Base de données"),
    DEPENDENCY_INJECTION("Injection de dépendances"),
    SECURITY("Sécurité"),
    CACHE("Cache"),
    SERVICE_STARTUP("Démarrage des services");

    private final String description;

    InitializationPhase(String description) {
      this.description = description;
    }

    public String getDescription() {
      return description;
    }
  }

  /**
   * Classe interne représentant le contexte d'une erreur d'initialisation.
   */
  @Getter
  @Builder
  public static class InitializationContext {
    private final String componentName;
    private final String resourceName;
    private final String configurationKey;
    private final String expectedState;
    private final String actualState;
    private final Map<String, String> additionalInfo;

    @Override
    public String toString() {
      StringBuilder sb = new StringBuilder()
          .append("InitializationContext[")
          .append("component=").append(componentName)
          .append(", resource=").append(resourceName)
          .append(", config=").append(configurationKey);

      if (expectedState != null) {
        sb.append(", expected=").append(expectedState);
      }
      if (actualState != null) {
        sb.append(", actual=").append(actualState);
      }
      if (additionalInfo != null && !additionalInfo.isEmpty()) {
        sb.append(", info=").append(additionalInfo);
      }

      return sb.append("]").toString();
    }
  }

  /**
   * Crée une exception pour une ressource manquante.
   *
   * @param resourceName Nom de la ressource
   * @param componentName Nom du composant
   * @return Instance de InitializationException
   */
  public static InitializationException resourceNotFound(String resourceName, String componentName) {
    String message = String.format(
        "Ressource requise '%s' non trouvée pour le composant '%s'",
        resourceName, componentName
    );

    InitializationContext context = InitializationContext.builder()
                                                         .componentName(componentName)
                                                         .resourceName(resourceName)
                                                         .build();

    return new InitializationException(message, null, context, InitializationPhase.RESOURCE_LOADING);
  }

  /**
   * Crée une exception pour une configuration invalide.
   *
   * @param key Clé de configuration
   * @param expectedValue Valeur attendue
   * @param actualValue Valeur actuelle
   * @return Instance de InitializationException
   */
  public static InitializationException invalidConfiguration(String key,
                                                             String expectedValue, String actualValue) {
    String message = String.format(
        "Configuration invalide pour '%s'. Attendu : %s, Actuel : %s",
        key, expectedValue, actualValue
    );

    InitializationContext context = InitializationContext.builder()
                                                         .configurationKey(key)
                                                         .expectedState(expectedValue)
                                                         .actualState(actualValue)
                                                         .build();

    return new InitializationException(message, null, context, InitializationPhase.CONFIGURATION);
  }

  /**
   * Crée une exception pour un échec de démarrage de service.
   *
   * @param serviceName Nom du service
   * @param reason Raison de l'échec
   * @return Instance de InitializationException
   */
  public static InitializationException serviceStartupFailure(String serviceName, String reason) {
    String message = String.format(
        "Échec du démarrage du service '%s' : %s",
        serviceName, reason
    );

    InitializationContext context = InitializationContext.builder()
                                                         .componentName(serviceName)
                                                         .additionalInfo(Map.of("reason", reason))
                                                         .build();

    return new InitializationException(message, null, context, InitializationPhase.SERVICE_STARTUP);
  }
}