package dev.lions.exceptions;

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

/**
 * Exception spécialisée pour la gestion des erreurs de filtrage.
 * Cette classe encapsule les différentes erreurs pouvant survenir lors
 * de l'application ou la manipulation des filtres de données.
 *
 * @author Lions Dev Team
 * @version 1.0
 */
@Slf4j
public class FilterException extends BusinessException {

  private static final long serialVersionUID = 1L;

  /**
   * Contexte détaillé de l'erreur de filtrage
   */
  private final FilterContext filterContext;

  /**
   * Crée une nouvelle instance avec un message d'erreur simple.
   *
   * @param message Description de l'erreur
   */
  public FilterException(String message) {
    this(message, null, null);
    log.error("Erreur de filtrage : {}", 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 FilterException(String message, Throwable cause) {
    this(message, cause, null);
    log.error("Erreur de filtrage : {}", 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 du filtrage au moment de l'erreur
   */
  public FilterException(String message, Throwable cause, FilterContext context) {
    super(message, cause);
    this.filterContext = context;
    log.error("Erreur de filtrage détaillée : {} - Contexte : {}", message, context);
  }

  /**
   * Récupère le contexte de l'erreur de filtrage.
   *
   * @return Contexte de l'erreur ou null si non disponible
   */
  public FilterContext getFilterContext() {
    return filterContext;
  }

  /**
   * Classe interne représentant le contexte d'une erreur de filtrage.
   */
  @Getter
  @Builder
  public static class FilterContext {
    private final String field;
    private final String operator;
    private final String value;
    private final String expectedType;
    private final String actualType;
    private final String validationError;

    @Override
    public String toString() {
      return String.format(
          "FilterContext[field=%s, operator=%s, value=%s, expectedType=%s, actualType=%s, error=%s]",
          field, operator, value, expectedType, actualType, validationError
      );
    }
  }

  /**
   * Crée une exception pour un champ de filtrage invalide.
   *
   * @param fieldName Nom du champ
   * @param value Valeur invalide
   * @param expectedType Type attendu
   * @return Instance de FilterException
   */
  public static FilterException invalidFieldValue(String fieldName, String value, String expectedType) {
    String message = String.format(
        "Valeur invalide '%s' pour le champ '%s'. Type attendu : %s",
        value, fieldName, expectedType
    );

    FilterContext context = FilterContext.builder()
                                         .field(fieldName)
                                         .value(value)
                                         .expectedType(expectedType)
                                         .validationError("INVALID_FIELD_VALUE")
                                         .build();

    return new FilterException(message, null, context);
  }

  /**
   * Crée une exception pour un opérateur de filtre incompatible.
   *
   * @param operator Opérateur utilisé
   * @param fieldName Nom du champ
   * @param fieldType Type du champ
   * @return Instance de FilterException
   */
  public static FilterException incompatibleOperator(String operator, String fieldName, String fieldType) {
    String message = String.format(
        "L'opérateur '%s' n'est pas compatible avec le champ '%s' de type %s",
        operator, fieldName, fieldType
    );

    FilterContext context = FilterContext.builder()
                                         .field(fieldName)
                                         .operator(operator)
                                         .expectedType(fieldType)
                                         .validationError("INCOMPATIBLE_OPERATOR")
                                         .build();

    return new FilterException(message, null, context);
  }

  /**
   * Crée une exception pour une expression de filtre invalide.
   *
   * @param expression Expression de filtre
   * @param reason Raison de l'invalidité
   * @return Instance de FilterException
   */
  public static FilterException invalidFilterExpression(String expression, String reason) {
    String message = String.format(
        "Expression de filtre invalide '%s' : %s",
        expression, reason
    );

    FilterContext context = FilterContext.builder()
                                         .value(expression)
                                         .validationError("INVALID_FILTER_EXPRESSION")
                                         .build();

    return new FilterException(message, null, context);
  }
}