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

import static org.assertj.core.api.Assertions.assertThat;

import dev.lions.unionflow.server.api.enums.paiement.StatutTraitement;
import dev.lions.unionflow.server.api.enums.paiement.TypeEvenement;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.UUID;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

/**
 * Tests unitaires complets pour WaveWebhookDTO.
 *
 * @author UnionFlow Team
 * @version 1.0
 * @since 2025-01-10
 */
@DisplayName("Tests WaveWebhookDTO")
class WaveWebhookDTOBasicTest {

  private WaveWebhookDTO webhook;

  @BeforeEach
  void setUp() {
    webhook = new WaveWebhookDTO();
  }

  @Nested
  @DisplayName("Tests de construction")
  class ConstructionTests {

    @Test
    @DisplayName("Constructeur par défaut - Initialisation correcte")
    void testConstructeurParDefaut() {
      WaveWebhookDTO newWebhook = new WaveWebhookDTO();

      assertThat(newWebhook.getId()).isNotNull();
      assertThat(newWebhook.getDateCreation()).isNotNull();
      assertThat(newWebhook.isActif()).isTrue();
      assertThat(newWebhook.getVersion()).isEqualTo(0L);
      assertThat(newWebhook.getStatutTraitement()).isEqualTo(StatutTraitement.RECU);
      assertThat(newWebhook.getDateReception()).isNotNull();
      assertThat(newWebhook.getNombreTentativesTraitement()).isEqualTo(0);
      assertThat(newWebhook.getTraitementAutomatique()).isTrue();
      assertThat(newWebhook.getInterventionManuelleRequise()).isFalse();
    }

    @Test
    @DisplayName("Constructeur avec paramètres")
    void testConstructeurAvecParametres() {
      String webhookId = "webhook_123456789";
      TypeEvenement typeEvenement = TypeEvenement.CHECKOUT_COMPLETE;
      String payloadJson = "{\"event\": \"checkout.completed\"}";

      WaveWebhookDTO newWebhook = new WaveWebhookDTO(webhookId, typeEvenement, payloadJson);

      assertThat(newWebhook.getWebhookId()).isEqualTo(webhookId);
      assertThat(newWebhook.getTypeEvenement()).isEqualTo(typeEvenement);
      assertThat(newWebhook.getCodeEvenement()).isEqualTo(typeEvenement.getCodeWave());
      assertThat(newWebhook.getPayloadJson()).isEqualTo(payloadJson);
      assertThat(newWebhook.getStatutTraitement()).isEqualTo(StatutTraitement.RECU);
    }
  }

  @Nested
  @DisplayName("Tests getters/setters")
  class GettersSettersTests {

    @Test
    @DisplayName("Test getters/setters - Partie 1")
    void testGettersSettersPart1() {
      // Données de test
      String webhookId = "webhook_123456789";
      TypeEvenement typeEvenement = TypeEvenement.CHECKOUT_COMPLETE;
      StatutTraitement statutTraitement = StatutTraitement.TRAITE;
      String payloadJson = "{\"event\": \"checkout.completed\"}";
      String headersHttp = "Content-Type: application/json";
      String signatureWave = "sha256=abcdef123456";
      LocalDateTime dateReception = LocalDateTime.now();
      LocalDateTime dateTraitement = LocalDateTime.now();

      // Test des setters
      webhook.setWebhookId(webhookId);
      webhook.setTypeEvenement(typeEvenement);
      webhook.setStatutTraitement(statutTraitement);
      webhook.setPayloadJson(payloadJson);
      webhook.setHeadersHttp(headersHttp);
      webhook.setSignatureWave(signatureWave);
      webhook.setDateReception(dateReception);
      webhook.setDateTraitement(dateTraitement);

      // Test des getters
      assertThat(webhook.getWebhookId()).isEqualTo(webhookId);
      assertThat(webhook.getTypeEvenement()).isEqualTo(typeEvenement);
      assertThat(webhook.getCodeEvenement()).isEqualTo(typeEvenement.getCodeWave());
      assertThat(webhook.getStatutTraitement()).isEqualTo(statutTraitement);
      assertThat(webhook.getPayloadJson()).isEqualTo(payloadJson);
      assertThat(webhook.getHeadersHttp()).isEqualTo(headersHttp);
      assertThat(webhook.getSignatureWave()).isEqualTo(signatureWave);
      assertThat(webhook.getDateReception()).isEqualTo(dateReception);
      assertThat(webhook.getDateTraitement()).isEqualTo(dateTraitement);
    }

    @Test
    @DisplayName("Test getters/setters - Partie 2")
    void testGettersSettersPart2() {
      // Données de test
      String sessionCheckoutId = "checkout_123456789";
      String transactionWaveId = "txn_987654321";
      BigDecimal montantTransaction = new BigDecimal("25000.00");
      String deviseTransaction = "XOF";
      String statutTransactionWave = "SUCCESS";
      UUID organisationId = UUID.randomUUID();
      UUID membreId = UUID.randomUUID();
      String referenceUnionFlow = "UF-REF-123";
      String typePaiementUnionFlow = "COTISATION";

      // Test des setters
      webhook.setSessionCheckoutId(sessionCheckoutId);
      webhook.setTransactionWaveId(transactionWaveId);
      webhook.setMontantTransaction(montantTransaction);
      webhook.setDeviseTransaction(deviseTransaction);
      webhook.setStatutTransactionWave(statutTransactionWave);
      webhook.setOrganisationId(organisationId);
      webhook.setMembreId(membreId);
      webhook.setReferenceUnionFlow(referenceUnionFlow);
      webhook.setTypePaiementUnionFlow(typePaiementUnionFlow);

      // Test des getters
      assertThat(webhook.getSessionCheckoutId()).isEqualTo(sessionCheckoutId);
      assertThat(webhook.getTransactionWaveId()).isEqualTo(transactionWaveId);
      assertThat(webhook.getMontantTransaction()).isEqualTo(montantTransaction);
      assertThat(webhook.getDeviseTransaction()).isEqualTo(deviseTransaction);
      assertThat(webhook.getStatutTransactionWave()).isEqualTo(statutTransactionWave);
      assertThat(webhook.getOrganisationId()).isEqualTo(organisationId);
      assertThat(webhook.getMembreId()).isEqualTo(membreId);
      assertThat(webhook.getReferenceUnionFlow()).isEqualTo(referenceUnionFlow);
      assertThat(webhook.getTypePaiementUnionFlow()).isEqualTo(typePaiementUnionFlow);
    }

    @Test
    @DisplayName("Test getters/setters - Partie 3")
    void testGettersSettersPart3() {
      // Données de test
      String adresseIpSource = "192.168.1.1";
      String userAgentSource = "Wave-Webhook/1.0";
      Integer nombreTentativesTraitement = 3;
      String messageErreurTraitement = "Erreur de traitement";
      String codeErreurTraitement = "ERR_001";
      String stackTraceErreur = "java.lang.Exception: Test error";
      Boolean traitementAutomatique = false;
      Boolean interventionManuelleRequise = true;
      String notesTraitementManuel = "Traitement manuel requis";
      String utilisateurTraitementManuel = "admin@unionflow.com";
      LocalDateTime dateTraitementManuel = LocalDateTime.now();

      // Test des setters
      webhook.setAdresseIpSource(adresseIpSource);
      webhook.setUserAgentSource(userAgentSource);
      webhook.setNombreTentativesTraitement(nombreTentativesTraitement);
      webhook.setMessageErreurTraitement(messageErreurTraitement);
      webhook.setCodeErreurTraitement(codeErreurTraitement);
      webhook.setStackTraceErreur(stackTraceErreur);
      webhook.setTraitementAutomatique(traitementAutomatique);
      webhook.setInterventionManuelleRequise(interventionManuelleRequise);
      webhook.setNotesTraitementManuel(notesTraitementManuel);
      webhook.setUtilisateurTraitementManuel(utilisateurTraitementManuel);
      webhook.setDateTraitementManuel(dateTraitementManuel);

      // Test des getters
      assertThat(webhook.getAdresseIpSource()).isEqualTo(adresseIpSource);
      assertThat(webhook.getUserAgentSource()).isEqualTo(userAgentSource);
      assertThat(webhook.getNombreTentativesTraitement()).isEqualTo(nombreTentativesTraitement);
      assertThat(webhook.getMessageErreurTraitement()).isEqualTo(messageErreurTraitement);
      assertThat(webhook.getCodeErreurTraitement()).isEqualTo(codeErreurTraitement);
      assertThat(webhook.getStackTraceErreur()).isEqualTo(stackTraceErreur);
      assertThat(webhook.getTraitementAutomatique()).isEqualTo(traitementAutomatique);
      assertThat(webhook.getInterventionManuelleRequise()).isEqualTo(interventionManuelleRequise);
      assertThat(webhook.getNotesTraitementManuel()).isEqualTo(notesTraitementManuel);
      assertThat(webhook.getUtilisateurTraitementManuel()).isEqualTo(utilisateurTraitementManuel);
      assertThat(webhook.getDateTraitementManuel()).isEqualTo(dateTraitementManuel);
    }
  }

  @Nested
  @DisplayName("Tests méthodes métier")
  class MethodesMetierTests {

    @Test
    @DisplayName("Test setTypeEvenement avec mise à jour du code")
    void testSetTypeEvenementAvecMiseAJourCode() {
      TypeEvenement typeEvenement = TypeEvenement.CHECKOUT_COMPLETE;

      webhook.setTypeEvenement(typeEvenement);

      assertThat(webhook.getTypeEvenement()).isEqualTo(typeEvenement);
      assertThat(webhook.getCodeEvenement()).isEqualTo(typeEvenement.getCodeWave());
    }

    @Test
    @DisplayName("Test setTypeEvenement avec null")
    void testSetTypeEvenementAvecNull() {
      // D'abord définir un type d'événement
      webhook.setTypeEvenement(TypeEvenement.CHECKOUT_COMPLETE);
      assertThat(webhook.getCodeEvenement()).isEqualTo("checkout.complete");

      // Maintenant mettre à null - le code ne devrait pas être modifié
      webhook.setTypeEvenement(null);

      assertThat(webhook.getTypeEvenement()).isNull();
      assertThat(webhook.getCodeEvenement()).isEqualTo("checkout.complete"); // Garde l'ancienne valeur
    }

    @Test
    @DisplayName("Test setCodeEvenement avec mise à jour du type")
    void testSetCodeEvenementAvecMiseAJourType() {
      String codeEvenement = "checkout.completed";
      
      webhook.setCodeEvenement(codeEvenement);
      
      assertThat(webhook.getCodeEvenement()).isEqualTo(codeEvenement);
      assertThat(webhook.getTypeEvenement()).isEqualTo(TypeEvenement.fromCode(codeEvenement));
    }

    @Test
    @DisplayName("Test isEvenementCheckout")
    void testIsEvenementCheckout() {
      webhook.setTypeEvenement(TypeEvenement.CHECKOUT_COMPLETE);
      assertThat(webhook.isEvenementCheckout()).isTrue();

      webhook.setTypeEvenement(TypeEvenement.CHECKOUT_CANCELLED);
      assertThat(webhook.isEvenementCheckout()).isTrue();

      webhook.setTypeEvenement(TypeEvenement.CHECKOUT_EXPIRED);
      assertThat(webhook.isEvenementCheckout()).isTrue();

      webhook.setTypeEvenement(TypeEvenement.PAYOUT_COMPLETE);
      assertThat(webhook.isEvenementCheckout()).isFalse();
    }

    @Test
    @DisplayName("Test isEvenementPayout")
    void testIsEvenementPayout() {
      webhook.setTypeEvenement(TypeEvenement.PAYOUT_COMPLETE);
      assertThat(webhook.isEvenementPayout()).isTrue();

      webhook.setTypeEvenement(TypeEvenement.PAYOUT_FAILED);
      assertThat(webhook.isEvenementPayout()).isTrue();

      webhook.setTypeEvenement(TypeEvenement.CHECKOUT_COMPLETE);
      assertThat(webhook.isEvenementPayout()).isFalse();
    }

    @Test
    @DisplayName("Test marquerCommeTraite")
    void testMarquerCommeTraite() {
      webhook.marquerCommeTraite();

      assertThat(webhook.getStatutTraitement()).isEqualTo(StatutTraitement.TRAITE);
      assertThat(webhook.getDateTraitement()).isNotNull();
      assertThat(webhook.getDateModification()).isNotNull();
      assertThat(webhook.getModifiePar()).isEqualTo("SYSTEM");
    }

    @Test
    @DisplayName("Test marquerCommeEchec")
    void testMarquerCommeEchec() {
      String messageErreur = "Erreur de test";
      String codeErreur = "TEST_ERROR";
      Integer tentativesInitiales = webhook.getNombreTentativesTraitement();

      webhook.marquerCommeEchec(messageErreur, codeErreur);

      assertThat(webhook.getStatutTraitement()).isEqualTo(StatutTraitement.ECHEC);
      assertThat(webhook.getMessageErreurTraitement()).isEqualTo(messageErreur);
      assertThat(webhook.getCodeErreurTraitement()).isEqualTo(codeErreur);
      assertThat(webhook.getNombreTentativesTraitement()).isEqualTo(tentativesInitiales + 1);
      assertThat(webhook.getDateTraitement()).isNotNull();
      assertThat(webhook.getDateModification()).isNotNull();
      assertThat(webhook.getModifiePar()).isEqualTo("SYSTEM");
    }

    @Test
    @DisplayName("Test demarrerTraitement")
    void testDemarrerTraitement() {
      Integer tentativesInitiales = webhook.getNombreTentativesTraitement();

      webhook.demarrerTraitement();

      assertThat(webhook.getStatutTraitement()).isEqualTo(StatutTraitement.EN_COURS);
      assertThat(webhook.getNombreTentativesTraitement()).isEqualTo(tentativesInitiales + 1);
      assertThat(webhook.getDateModification()).isNotNull();
      assertThat(webhook.getModifiePar()).isEqualTo("SYSTEM");
    }
  }

  @Test
  @DisplayName("Test toString")
  void testToString() {
    webhook.setWebhookId("webhook_123");
    webhook.setTypeEvenement(TypeEvenement.CHECKOUT_COMPLETE);
    webhook.setStatutTraitement(StatutTraitement.TRAITE);
    webhook.setSessionCheckoutId("checkout_456");
    webhook.setMontantTransaction(new BigDecimal("25000.00"));

    String result = webhook.toString();
    
    assertThat(result).isNotNull();
    assertThat(result).contains("WaveWebhookDTO");
    assertThat(result).contains("webhook_123");
    assertThat(result).contains("CHECKOUT_COMPLETE");
    assertThat(result).contains("TRAITE");
    assertThat(result).contains("checkout_456");
    assertThat(result).contains("25000.00");
  }
}
