package dev.lions.unionflow.server.mapper.mutuelle.epargne;

import dev.lions.unionflow.server.api.dto.mutuelle.epargne.TransactionEpargneRequest;
import dev.lions.unionflow.server.api.dto.mutuelle.epargne.TransactionEpargneResponse;
import dev.lions.unionflow.server.api.enums.mutuelle.epargne.TypeTransactionEpargne;
import dev.lions.unionflow.server.api.enums.wave.StatutTransactionWave;
import dev.lions.unionflow.server.entity.mutuelle.epargne.CompteEpargne;
import dev.lions.unionflow.server.entity.mutuelle.epargne.TransactionEpargne;
import io.quarkus.test.junit.QuarkusTest;
import jakarta.inject.Inject;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.UUID;

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

@QuarkusTest
class TransactionEpargneMapperTest {

    @Inject
    TransactionEpargneMapper mapper;

    @Test
    @DisplayName("toDto avec null retourne null")
    void toDto_null_returnsNull() {
        assertThat(mapper.toDto(null)).isNull();
    }

    @Test
    @DisplayName("toEntity avec null retourne null")
    void toEntity_null_returnsNull() {
        assertThat(mapper.toEntity(null)).isNull();
    }

    @Test
    @DisplayName("toDto mappe entity vers DTO")
    void toDto_mapsEntityToDto() {
        UUID compteId = UUID.randomUUID();
        CompteEpargne compte = new CompteEpargne();
        compte.setId(compteId);
        TransactionEpargne entity = TransactionEpargne.builder()
                .compte(compte)
                .type(TypeTransactionEpargne.DEPOT)
                .montant(new BigDecimal("50000"))
                .soldeAvant(new BigDecimal("100000"))
                .soldeApres(new BigDecimal("150000"))
                .motif("Dépôt mensuel")
                .dateTransaction(LocalDateTime.now())
                .statutExecution(StatutTransactionWave.REUSSIE)
                .build();
        entity.setId(UUID.randomUUID());

        TransactionEpargneResponse dto = mapper.toDto(entity);

        assertThat(dto).isNotNull();
        assertThat(dto.getCompteId()).isEqualTo(compteId.toString());
        assertThat(dto.getType()).isEqualTo(TypeTransactionEpargne.DEPOT);
        assertThat(dto.getMontant()).isEqualByComparingTo("50000");
        assertThat(dto.getMotif()).isEqualTo("Dépôt mensuel");
        assertThat(dto.getStatutExecution()).isEqualTo(StatutTransactionWave.REUSSIE);
    }

    @Test
    @DisplayName("toEntity mappe Request vers entity (typeTransaction -> type)")
    void toEntity_mapsRequestToEntity() {
        TransactionEpargneRequest request = TransactionEpargneRequest.builder()
                .compteId(UUID.randomUUID().toString())
                .typeTransaction(TypeTransactionEpargne.RETRAIT)
                .montant(new BigDecimal("25000"))
                .motif("Retrait")
                .build();

        TransactionEpargne entity = mapper.toEntity(request);

        assertThat(entity).isNotNull();
        assertThat(entity.getType()).isEqualTo(TypeTransactionEpargne.RETRAIT);
        assertThat(entity.getMontant()).isEqualByComparingTo("25000");
        assertThat(entity.getMotif()).isEqualTo("Retrait");
        assertThat(entity.getCompte()).isNull();
    }

    @Test
    @DisplayName("updateEntityFromDto met à jour l'entité cible")
    void updateEntityFromDto_updatesTarget() {
        TransactionEpargne entity = TransactionEpargne.builder()
                .type(TypeTransactionEpargne.DEPOT)
                .montant(BigDecimal.ONE)
                .build();
        TransactionEpargneRequest request = TransactionEpargneRequest.builder()
                .compteId(UUID.randomUUID().toString())
                .typeTransaction(TypeTransactionEpargne.TRANSFERT_SORTANT)
                .montant(new BigDecimal("75000"))
                .motif("Virement")
                .build();

        mapper.updateEntityFromDto(request, entity);

        assertThat(entity.getType()).isEqualTo(TypeTransactionEpargne.TRANSFERT_SORTANT);
        assertThat(entity.getMontant()).isEqualByComparingTo("75000");
        assertThat(entity.getMotif()).isEqualTo("Virement");
    }

    @Test
    @DisplayName("updateEntityFromDto avec request null ne modifie pas l'entité")
    void updateEntityFromDto_nullRequest_noOp() {
        TransactionEpargne entity = TransactionEpargne.builder()
                .type(TypeTransactionEpargne.DEPOT)
                .montant(new BigDecimal("10000"))
                .motif("Original")
                .build();

        // doit retourner immédiatement sans modifier entity
        mapper.updateEntityFromDto(null, entity);

        assertThat(entity.getType()).isEqualTo(TypeTransactionEpargne.DEPOT);
        assertThat(entity.getMontant()).isEqualByComparingTo("10000");
        assertThat(entity.getMotif()).isEqualTo("Original");
    }

    @Test
    @DisplayName("toDto avec entity sans compte (compte null) ne lève pas d'exception")
    void toDto_entityWithNullCompte_setsNullCompteId() {
        TransactionEpargne entity = TransactionEpargne.builder()
                .type(TypeTransactionEpargne.DEPOT)
                .montant(new BigDecimal("5000"))
                .motif("Sans compte")
                .build();
        entity.setId(UUID.randomUUID());
        // compte est null → entityCompteId retourne null → compteId non setté

        TransactionEpargneResponse dto = mapper.toDto(entity);

        assertThat(dto).isNotNull();
        assertThat(dto.getCompteId()).isNull();
        assertThat(dto.getMontant()).isEqualByComparingTo("5000");
    }

    @Test
    @DisplayName("toDto avec pieceJustificativeId non null produit pieceJustificativeId en String (branche expression Java — pieceJustificativeId non null)")
    void toDto_pieceJustificativeIdNonNull_convertsToString() {
        UUID compteId = UUID.randomUUID();
        CompteEpargne compte = new CompteEpargne();
        compte.setId(compteId);
        UUID pieceId = UUID.randomUUID();
        TransactionEpargne entity = TransactionEpargne.builder()
                .compte(compte)
                .type(TypeTransactionEpargne.DEPOT)
                .montant(new BigDecimal("100000"))
                .motif("Avec pièce justificative")
                .pieceJustificativeId(pieceId)
                .build();
        entity.setId(UUID.randomUUID());

        TransactionEpargneResponse dto = mapper.toDto(entity);

        assertThat(dto).isNotNull();
        assertThat(dto.getPieceJustificativeId()).isEqualTo(pieceId.toString());
        assertThat(dto.getCompteId()).isEqualTo(compteId.toString());
    }

    @Test
    @DisplayName("toDto avec pieceJustificativeId null produit pieceJustificativeId null (branche expression Java — pieceJustificativeId null)")
    void toDto_pieceJustificativeIdNull_setsNull() {
        UUID compteId = UUID.randomUUID();
        CompteEpargne compte = new CompteEpargne();
        compte.setId(compteId);
        TransactionEpargne entity = TransactionEpargne.builder()
                .compte(compte)
                .type(TypeTransactionEpargne.RETRAIT)
                .montant(new BigDecimal("30000"))
                .motif("Sans pièce")
                .pieceJustificativeId(null)
                .build();
        entity.setId(UUID.randomUUID());

        TransactionEpargneResponse dto = mapper.toDto(entity);

        assertThat(dto).isNotNull();
        assertThat(dto.getPieceJustificativeId()).isNull();
        assertThat(dto.getMontant()).isEqualByComparingTo("30000");
    }
}
