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

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

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

/**
 * Tests unitaires pour WaveWebhookDTO.
 * Couvre toutes les branches : setTypeEvenement/setCodeEvenement, isEvenementCheckout,
 * isEvenementPayout, marquerCommeTraite, marquerCommeEchec, demarrerTraitement.
 *
 * @author UnionFlow Team
 * @version 1.0
 * @since 2026-02-21
 */
@DisplayName("Tests WaveWebhookDTO")
class WaveWebhookDTOTest {

  @Nested
  @DisplayName("Constructeurs")
  class Constructeurs {

    @Test
    @DisplayName("constructeur par défaut initialise statut et date")
    void testConstructeurVide() {
      WaveWebhookDTO dto = new WaveWebhookDTO();
      assertThat(dto.getStatutTraitement()).isEqualTo(StatutTraitement.RECU);
      assertThat(dto.getDateReception()).isNotNull();
      assertThat(dto.getNombreTentativesTraitement()).isEqualTo(0);
      assertThat(dto.getTraitementAutomatique()).isTrue();
    }

    @Test
    @DisplayName("constructeur avec paramètres remplit type et code")
    void testConstructeurAvecParams() {
      WaveWebhookDTO dto = new WaveWebhookDTO("wh-1", TypeEvenement.CHECKOUT_COMPLETE, "{}");
      assertThat(dto.getWebhookId()).isEqualTo("wh-1");
      assertThat(dto.getTypeEvenement()).isEqualTo(TypeEvenement.CHECKOUT_COMPLETE);
      assertThat(dto.getCodeEvenement()).isEqualTo("checkout.complete");
      assertThat(dto.getPayloadJson()).isEqualTo("{}");
    }
  }

  @Nested
  @DisplayName("setTypeEvenement")
  class SetTypeEvenement {

    @Test
    @DisplayName("met à jour codeEvenement quand type non null")
    void testTypeNonNull() {
      WaveWebhookDTO dto = new WaveWebhookDTO();
      dto.setTypeEvenement(TypeEvenement.PAYOUT_COMPLETE);
      assertThat(dto.getTypeEvenement()).isEqualTo(TypeEvenement.PAYOUT_COMPLETE);
      assertThat(dto.getCodeEvenement()).isEqualTo("payout.complete");
    }

    @Test
    @DisplayName("ne modifie pas codeEvenement quand type null")
    void testTypeNull() {
      WaveWebhookDTO dto = new WaveWebhookDTO("wh-1", TypeEvenement.CHECKOUT_COMPLETE, "{}");
      dto.setCodeEvenement("custom.code");
      dto.setTypeEvenement(null);
      assertThat(dto.getCodeEvenement()).isEqualTo("custom.code");
    }
  }

  @Nested
  @DisplayName("setCodeEvenement")
  class SetCodeEvenement {

    @Test
    @DisplayName("définit typeEvenement via fromCode")
    void testFromCode() {
      WaveWebhookDTO dto = new WaveWebhookDTO();
      dto.setCodeEvenement("checkout.cancelled");
      assertThat(dto.getTypeEvenement()).isEqualTo(TypeEvenement.CHECKOUT_CANCELLED);
      assertThat(dto.getCodeEvenement()).isEqualTo("checkout.cancelled");
    }

    @Test
    @DisplayName("code inconnu met typeEvenement à null")
    void testCodeInconnu() {
      WaveWebhookDTO dto = new WaveWebhookDTO("wh-1", TypeEvenement.CHECKOUT_COMPLETE, "{}");
      dto.setCodeEvenement("unknown.event");
      assertThat(dto.getTypeEvenement()).isNull();
    }
  }

  @Nested
  @DisplayName("isEvenementCheckout")
  class IsEvenementCheckout {

    @Test
    @DisplayName("true pour CHECKOUT_COMPLETE")
    void testCheckoutComplete() {
      WaveWebhookDTO dto = new WaveWebhookDTO("wh-1", TypeEvenement.CHECKOUT_COMPLETE, "{}");
      assertThat(dto.isEvenementCheckout()).isTrue();
    }

    @Test
    @DisplayName("true pour CHECKOUT_CANCELLED")
    void testCheckoutCancelled() {
      WaveWebhookDTO dto = new WaveWebhookDTO("wh-1", TypeEvenement.CHECKOUT_CANCELLED, "{}");
      assertThat(dto.isEvenementCheckout()).isTrue();
    }

    @Test
    @DisplayName("true pour CHECKOUT_EXPIRED")
    void testCheckoutExpired() {
      WaveWebhookDTO dto = new WaveWebhookDTO("wh-1", TypeEvenement.CHECKOUT_EXPIRED, "{}");
      assertThat(dto.isEvenementCheckout()).isTrue();
    }

    @Test
    @DisplayName("false pour PAYOUT_COMPLETE")
    void testPayout() {
      WaveWebhookDTO dto = new WaveWebhookDTO("wh-1", TypeEvenement.PAYOUT_COMPLETE, "{}");
      assertThat(dto.isEvenementCheckout()).isFalse();
    }
  }

  @Nested
  @DisplayName("isEvenementPayout")
  class IsEvenementPayout {

    @Test
    @DisplayName("true pour PAYOUT_COMPLETE")
    void testPayoutComplete() {
      WaveWebhookDTO dto = new WaveWebhookDTO("wh-1", TypeEvenement.PAYOUT_COMPLETE, "{}");
      assertThat(dto.isEvenementPayout()).isTrue();
    }

    @Test
    @DisplayName("true pour PAYOUT_FAILED")
    void testPayoutFailed() {
      WaveWebhookDTO dto = new WaveWebhookDTO("wh-1", TypeEvenement.PAYOUT_FAILED, "{}");
      assertThat(dto.isEvenementPayout()).isTrue();
    }

    @Test
    @DisplayName("false pour CHECKOUT_COMPLETE")
    void testCheckout() {
      WaveWebhookDTO dto = new WaveWebhookDTO("wh-1", TypeEvenement.CHECKOUT_COMPLETE, "{}");
      assertThat(dto.isEvenementPayout()).isFalse();
    }
  }

  @Nested
  @DisplayName("marquerCommeTraite")
  class MarquerCommeTraite {

    @Test
    @DisplayName("passe statut à TRAITE et remplit dateTraitement")
    void testMarquerCommeTraite() {
      WaveWebhookDTO dto = new WaveWebhookDTO("wh-1", TypeEvenement.CHECKOUT_COMPLETE, "{}");
      dto.marquerCommeTraite();
      assertThat(dto.getStatutTraitement()).isEqualTo(StatutTraitement.TRAITE);
      assertThat(dto.getDateTraitement()).isNotNull();
    }
  }

  @Nested
  @DisplayName("marquerCommeEchec")
  class MarquerCommeEchec {

    @Test
    @DisplayName("passe statut à ECHEC et incrémente tentatives")
    void testMarquerCommeEchec() {
      WaveWebhookDTO dto = new WaveWebhookDTO("wh-1", TypeEvenement.CHECKOUT_COMPLETE, "{}");
      dto.marquerCommeEchec("Erreur", "ERR_01");
      assertThat(dto.getStatutTraitement()).isEqualTo(StatutTraitement.ECHEC);
      assertThat(dto.getMessageErreurTraitement()).isEqualTo("Erreur");
      assertThat(dto.getCodeErreurTraitement()).isEqualTo("ERR_01");
      assertThat(dto.getNombreTentativesTraitement()).isEqualTo(1);
    }
  }

  @Nested
  @DisplayName("demarrerTraitement")
  class DemarrerTraitement {

    @Test
    @DisplayName("passe statut à EN_COURS et incrémente tentatives")
    void testDemarrerTraitement() {
      WaveWebhookDTO dto = new WaveWebhookDTO("wh-1", TypeEvenement.CHECKOUT_COMPLETE, "{}");
      dto.demarrerTraitement();
      assertThat(dto.getStatutTraitement()).isEqualTo(StatutTraitement.EN_COURS);
      assertThat(dto.getNombreTentativesTraitement()).isEqualTo(1);
    }
  }

  @Nested
  @DisplayName("toString")
  class ToString {

    @Test
    @DisplayName("contient webhookId et statut")
    void testToString() {
      WaveWebhookDTO dto = new WaveWebhookDTO("wh-1", TypeEvenement.CHECKOUT_COMPLETE, "{}");
      dto.setSessionCheckoutId("sess-1");
      dto.setMontantTransaction(BigDecimal.TEN);
      String s = dto.toString();
      assertThat(s).contains("wh-1");
      assertThat(s).contains("RECU");
      assertThat(s).contains("sess-1");
    }
  }
}
