package dev.lions.unionflow.server.api.dto.paiement;

import com.fasterxml.jackson.annotation.JsonFormat;
import dev.lions.unionflow.server.api.dto.base.BaseDTO;
import dev.lions.unionflow.server.api.enums.paiement.StatutTraitement;
import dev.lions.unionflow.server.api.enums.paiement.TypeEvenement;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.UUID;
import lombok.Getter;
import lombok.Setter;

/**
 * DTO pour les webhooks Wave Money Représente les notifications reçues de Wave lors d'événements
 *
 * <p>Basé sur l'API officielle Wave : https://docs.wave.com/business#webhooks
 *
 * @author UnionFlow Team
 * @version 1.0
 * @since 2025-01-10
 */
@Getter
@Setter
public class WaveWebhookDTO extends BaseDTO {

  private static final long serialVersionUID = 1L;

  /** ID unique du webhook Wave */
  @NotBlank(message = "L'ID du webhook est obligatoire")
  private String webhookId;

  /** Type d'événement */
  @NotNull(message = "Le type d'événement est obligatoire")
  private TypeEvenement typeEvenement;

  /** Code de l'événement tel que reçu de Wave */
  @NotBlank(message = "Le code événement est obligatoire")
  private String codeEvenement;

  /** Statut de traitement du webhook */
  @NotNull(message = "Le statut de traitement est obligatoire")
  private StatutTraitement statutTraitement;

  /** Payload JSON complet reçu de Wave */
  @NotBlank(message = "Le payload est obligatoire")
  @Size(max = 5000, message = "Le payload ne peut pas dépasser 5000 caractères")
  private String payloadJson;

  /** Headers HTTP reçus */
  @Size(max = 2000, message = "Les headers ne peuvent pas dépasser 2000 caractères")
  private String headersHttp;

  /** Signature Wave pour vérification */
  private String signatureWave;

  /** Date de réception du webhook */
  @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
  private LocalDateTime dateReception;

  /** Date de traitement du webhook */
  @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
  private LocalDateTime dateTraitement;

  /** ID de la session checkout concernée (si applicable) */
  private String sessionCheckoutId;

  /** ID de la transaction Wave concernée */
  private String transactionWaveId;

  /** Montant de la transaction (si applicable) */
  private BigDecimal montantTransaction;

  /** Devise de la transaction */
  @Pattern(regexp = "^[A-Z]{3}$", message = "La devise doit être un code ISO à 3 lettres")
  private String deviseTransaction;

  /** Statut de la transaction Wave */
  private String statutTransactionWave;

  /** ID de l'organisation UnionFlow concernée */
  private UUID organisationId;

  /** ID du membre UnionFlow concerné */
  private UUID membreId;

  /** Référence UnionFlow liée */
  private String referenceUnionFlow;

  /** Type de paiement UnionFlow */
  @Pattern(
      regexp = "^(COTISATION|ABONNEMENT|DON|EVENEMENT|FORMATION|AUTRE)$",
      message = "Type de paiement invalide")
  private String typePaiementUnionFlow;

  /** Adresse IP source du webhook */
  private String adresseIpSource;

  /** User Agent du webhook */
  @Size(max = 500, message = "Le User Agent ne peut pas dépasser 500 caractères")
  private String userAgentSource;

  /** Nombre de tentatives de traitement */
  private Integer nombreTentativesTraitement;

  /** Message d'erreur de traitement (si échec) */
  @Size(max = 1000, message = "Le message d'erreur ne peut pas dépasser 1000 caractères")
  private String messageErreurTraitement;

  /** Code d'erreur de traitement (si échec) */
  private String codeErreurTraitement;

  /** Stack trace de l'erreur (si échec) */
  @Size(max = 3000, message = "La stack trace ne peut pas dépasser 3000 caractères")
  private String stackTraceErreur;

  /** Webhook traité automatiquement */
  private Boolean traitementAutomatique;

  /** Webhook nécessitant une intervention manuelle */
  private Boolean interventionManuelleRequise;

  /** Notes de traitement manuel */
  @Size(max = 1000, message = "Les notes ne peuvent pas dépasser 1000 caractères")
  private String notesTraitementManuel;

  /** Utilisateur ayant traité manuellement */
  private String utilisateurTraitementManuel;

  /** Date du traitement manuel */
  @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
  private LocalDateTime dateTraitementManuel;

  // Constructeurs
  public WaveWebhookDTO() {
    super();
    this.statutTraitement = StatutTraitement.RECU;
    this.dateReception = LocalDateTime.now();
    this.nombreTentativesTraitement = 0;
    this.traitementAutomatique = true;
    this.interventionManuelleRequise = false;
  }

  public WaveWebhookDTO(String webhookId, TypeEvenement typeEvenement, String payloadJson) {
    this();
    this.webhookId = webhookId;
    this.typeEvenement = typeEvenement;
    this.codeEvenement = typeEvenement.getCodeWave();
    this.payloadJson = payloadJson;
  }

  // Getters et Setters
  public String getWebhookId() {
    return webhookId;
  }

  public void setWebhookId(String webhookId) {
    this.webhookId = webhookId;
  }

  public TypeEvenement getTypeEvenement() {
    return typeEvenement;
  }

  public void setTypeEvenement(TypeEvenement typeEvenement) {
    this.typeEvenement = typeEvenement;
    if (typeEvenement != null) {
      this.codeEvenement = typeEvenement.getCodeWave();
    }
  }

  public String getCodeEvenement() {
    return codeEvenement;
  }

  public void setCodeEvenement(String codeEvenement) {
    this.codeEvenement = codeEvenement;
    this.typeEvenement = TypeEvenement.fromCode(codeEvenement);
  }

  public StatutTraitement getStatutTraitement() {
    return statutTraitement;
  }

  public void setStatutTraitement(StatutTraitement statutTraitement) {
    this.statutTraitement = statutTraitement;
  }

  public String getPayloadJson() {
    return payloadJson;
  }

  public void setPayloadJson(String payloadJson) {
    this.payloadJson = payloadJson;
  }

  public String getHeadersHttp() {
    return headersHttp;
  }

  public void setHeadersHttp(String headersHttp) {
    this.headersHttp = headersHttp;
  }

  public String getSignatureWave() {
    return signatureWave;
  }

  public void setSignatureWave(String signatureWave) {
    this.signatureWave = signatureWave;
  }

  public LocalDateTime getDateReception() {
    return dateReception;
  }

  public void setDateReception(LocalDateTime dateReception) {
    this.dateReception = dateReception;
  }

  public LocalDateTime getDateTraitement() {
    return dateTraitement;
  }

  public void setDateTraitement(LocalDateTime dateTraitement) {
    this.dateTraitement = dateTraitement;
  }

  public String getSessionCheckoutId() {
    return sessionCheckoutId;
  }

  public void setSessionCheckoutId(String sessionCheckoutId) {
    this.sessionCheckoutId = sessionCheckoutId;
  }

  public String getTransactionWaveId() {
    return transactionWaveId;
  }

  public void setTransactionWaveId(String transactionWaveId) {
    this.transactionWaveId = transactionWaveId;
  }

  public BigDecimal getMontantTransaction() {
    return montantTransaction;
  }

  public void setMontantTransaction(BigDecimal montantTransaction) {
    this.montantTransaction = montantTransaction;
  }

  public String getDeviseTransaction() {
    return deviseTransaction;
  }

  public void setDeviseTransaction(String deviseTransaction) {
    this.deviseTransaction = deviseTransaction;
  }

  public String getStatutTransactionWave() {
    return statutTransactionWave;
  }

  public void setStatutTransactionWave(String statutTransactionWave) {
    this.statutTransactionWave = statutTransactionWave;
  }

  public UUID getOrganisationId() {
    return organisationId;
  }

  public void setOrganisationId(UUID organisationId) {
    this.organisationId = organisationId;
  }

  public UUID getMembreId() {
    return membreId;
  }

  public void setMembreId(UUID membreId) {
    this.membreId = membreId;
  }

  public String getReferenceUnionFlow() {
    return referenceUnionFlow;
  }

  public void setReferenceUnionFlow(String referenceUnionFlow) {
    this.referenceUnionFlow = referenceUnionFlow;
  }

  public String getTypePaiementUnionFlow() {
    return typePaiementUnionFlow;
  }

  public void setTypePaiementUnionFlow(String typePaiementUnionFlow) {
    this.typePaiementUnionFlow = typePaiementUnionFlow;
  }

  public String getAdresseIpSource() {
    return adresseIpSource;
  }

  public void setAdresseIpSource(String adresseIpSource) {
    this.adresseIpSource = adresseIpSource;
  }

  public String getUserAgentSource() {
    return userAgentSource;
  }

  public void setUserAgentSource(String userAgentSource) {
    this.userAgentSource = userAgentSource;
  }

  public Integer getNombreTentativesTraitement() {
    return nombreTentativesTraitement;
  }

  public void setNombreTentativesTraitement(Integer nombreTentativesTraitement) {
    this.nombreTentativesTraitement = nombreTentativesTraitement;
  }

  public String getMessageErreurTraitement() {
    return messageErreurTraitement;
  }

  public void setMessageErreurTraitement(String messageErreurTraitement) {
    this.messageErreurTraitement = messageErreurTraitement;
  }

  public String getCodeErreurTraitement() {
    return codeErreurTraitement;
  }

  public void setCodeErreurTraitement(String codeErreurTraitement) {
    this.codeErreurTraitement = codeErreurTraitement;
  }

  public String getStackTraceErreur() {
    return stackTraceErreur;
  }

  public void setStackTraceErreur(String stackTraceErreur) {
    this.stackTraceErreur = stackTraceErreur;
  }

  public Boolean getTraitementAutomatique() {
    return traitementAutomatique;
  }

  public void setTraitementAutomatique(Boolean traitementAutomatique) {
    this.traitementAutomatique = traitementAutomatique;
  }

  public Boolean getInterventionManuelleRequise() {
    return interventionManuelleRequise;
  }

  public void setInterventionManuelleRequise(Boolean interventionManuelleRequise) {
    this.interventionManuelleRequise = interventionManuelleRequise;
  }

  public String getNotesTraitementManuel() {
    return notesTraitementManuel;
  }

  public void setNotesTraitementManuel(String notesTraitementManuel) {
    this.notesTraitementManuel = notesTraitementManuel;
  }

  public String getUtilisateurTraitementManuel() {
    return utilisateurTraitementManuel;
  }

  public void setUtilisateurTraitementManuel(String utilisateurTraitementManuel) {
    this.utilisateurTraitementManuel = utilisateurTraitementManuel;
  }

  public LocalDateTime getDateTraitementManuel() {
    return dateTraitementManuel;
  }

  public void setDateTraitementManuel(LocalDateTime dateTraitementManuel) {
    this.dateTraitementManuel = dateTraitementManuel;
  }

  // Méthodes utilitaires

  /**
   * Vérifie si le webhook concerne un checkout
   *
   * @return true si c'est un événement de checkout
   */
  public boolean isEvenementCheckout() {
    return typeEvenement == TypeEvenement.CHECKOUT_COMPLETE
        || typeEvenement == TypeEvenement.CHECKOUT_CANCELLED
        || typeEvenement == TypeEvenement.CHECKOUT_EXPIRED;
  }

  /**
   * Vérifie si le webhook concerne un payout
   *
   * @return true si c'est un événement de payout
   */
  public boolean isEvenementPayout() {
    return typeEvenement == TypeEvenement.PAYOUT_COMPLETE
        || typeEvenement == TypeEvenement.PAYOUT_FAILED;
  }

  /** Marque le webhook comme traité avec succès */
  public void marquerCommeTraite() {
    this.statutTraitement = StatutTraitement.TRAITE;
    this.dateTraitement = LocalDateTime.now();
    marquerCommeModifie("SYSTEM");
  }

  /**
   * Marque le webhook comme échoué
   *
   * @param messageErreur Le message d'erreur
   * @param codeErreur Le code d'erreur
   */
  public void marquerCommeEchec(String messageErreur, String codeErreur) {
    this.statutTraitement = StatutTraitement.ECHEC;
    this.messageErreurTraitement = messageErreur;
    this.codeErreurTraitement = codeErreur;
    this.nombreTentativesTraitement++;
    this.dateTraitement = LocalDateTime.now();
    marquerCommeModifie("SYSTEM");
  }

  /** Démarre le traitement du webhook */
  public void demarrerTraitement() {
    this.statutTraitement = StatutTraitement.EN_COURS;
    this.nombreTentativesTraitement++;
    marquerCommeModifie("SYSTEM");
  }

  @Override
  public String toString() {
    return "WaveWebhookDTO{"
        + "webhookId='"
        + webhookId
        + '\''
        + ", typeEvenement="
        + typeEvenement
        + ", statutTraitement="
        + statutTraitement
        + ", dateReception="
        + dateReception
        + ", sessionCheckoutId='"
        + sessionCheckoutId
        + '\''
        + ", montantTransaction="
        + montantTransaction
        + "} "
        + super.toString();
  }
}
