package dev.lions.unionflow.server.service.mutuelle;

import dev.lions.unionflow.server.api.enums.mutuelle.epargne.StatutCompteEpargne;
import dev.lions.unionflow.server.api.enums.mutuelle.epargne.TypeTransactionEpargne;
import dev.lions.unionflow.server.entity.Organisation;
import dev.lions.unionflow.server.entity.mutuelle.ParametresFinanciersMutuelle;
import dev.lions.unionflow.server.entity.mutuelle.epargne.CompteEpargne;
import dev.lions.unionflow.server.entity.mutuelle.epargne.TransactionEpargne;
import dev.lions.unionflow.server.entity.mutuelle.parts.ComptePartsSociales;
import dev.lions.unionflow.server.entity.mutuelle.parts.TransactionPartsSociales;
import dev.lions.unionflow.server.repository.mutuelle.ParametresFinanciersMutuellRepository;
import dev.lions.unionflow.server.repository.mutuelle.epargne.CompteEpargneRepository;
import dev.lions.unionflow.server.repository.mutuelle.epargne.TransactionEpargneRepository;
import dev.lions.unionflow.server.repository.mutuelle.parts.ComptePartsSocialesRepository;
import dev.lions.unionflow.server.repository.mutuelle.parts.TransactionPartsSocialesRepository;
import io.quarkus.test.InjectMock;
import io.quarkus.test.junit.QuarkusTest;
import jakarta.inject.Inject;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;

import static org.assertj.core.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.*;

@QuarkusTest
@DisplayName("InteretsEpargneService")
class InteretsEpargneServiceTest {

    @Inject InteretsEpargneService service;

    @InjectMock ParametresFinanciersMutuellRepository parametresRepo;
    @InjectMock CompteEpargneRepository compteEpargneRepository;
    @InjectMock TransactionEpargneRepository transactionEpargneRepository;
    @InjectMock ComptePartsSocialesRepository comptePartsSocialesRepository;
    @InjectMock TransactionPartsSocialesRepository transactionPartsSocialesRepository;

    private UUID orgId;
    private Organisation org;
    private ParametresFinanciersMutuelle params;

    @BeforeEach
    void setUp() {
        orgId = UUID.randomUUID();
        org = new Organisation();
        org.setId(orgId);
        org.setNom("Mutuelle Test");

        params = ParametresFinanciersMutuelle.builder()
                .organisation(org)
                .tauxInteretAnnuelEpargne(new BigDecimal("0.03"))
                .tauxDividendePartsAnnuel(new BigDecimal("0.05"))
                .periodiciteCalcul("MENSUEL")
                .seuilMinEpargneInterets(BigDecimal.ZERO)
                .dernierNbComptesTraites(0)
                .build();
    }

    @Test
    @DisplayName("calculerManuellement — lève IllegalArgumentException si aucun paramètre configuré")
    void calculerManuellement_sansParametres_leveException() {
        when(parametresRepo.findByOrganisation(orgId)).thenReturn(Optional.empty());
        assertThatThrownBy(() -> service.calculerManuellement(orgId))
                .isInstanceOf(IllegalArgumentException.class)
                .hasMessageContaining("paramètre financier");
    }

    @Test
    @DisplayName("calculerInteretsPourOrg — crédite les intérêts mensuels sur les comptes épargne")
    @SuppressWarnings("unchecked")
    void calculerInterets_comptesActifs_crediteInterets() {
        CompteEpargne compte = CompteEpargne.builder()
                .soldeActuel(new BigDecimal("100000"))
                .soldeBloque(BigDecimal.ZERO)
                .statut(StatutCompteEpargne.ACTIF)
                .organisation(org)
                .build();

        io.quarkus.hibernate.orm.panache.PanacheQuery<CompteEpargne> query = mock(
                io.quarkus.hibernate.orm.panache.PanacheQuery.class);
        // Use typed matchers to resolve find(String, Object...) unambiguously
        when(compteEpargneRepository.find(anyString(), any(UUID.class), any(StatutCompteEpargne.class)))
                .thenReturn(query);
        when(query.list()).thenReturn(List.of(compte));
        when(comptePartsSocialesRepository.findByOrganisation(orgId)).thenReturn(List.of());
        // Use any(TransactionEpargne.class) to resolve persist(Entity) unambiguously
        doNothing().when(transactionEpargneRepository).persist(any(TransactionEpargne.class));

        Map<String, Object> result = service.calculerInteretsPourOrg(params);

        assertThat(result).containsKey("comptesEpargneTraites");
        assertThat((int) result.get("comptesEpargneTraites")).isEqualTo(1);

        // Taux mensuel = 3%/12 = 0.25% → 100000 × 0.0025 = 250 XOF crédités
        BigDecimal attendu = new BigDecimal("250");
        assertThat(compte.getSoldeActuel()).isEqualByComparingTo(new BigDecimal("100000").add(attendu));

        verify(transactionEpargneRepository).persist(argThat((TransactionEpargne tx) ->
                tx.getType() == TypeTransactionEpargne.PAIEMENT_INTERETS
                && tx.getMontant().compareTo(attendu) == 0));
    }

    @Test
    @DisplayName("calculerInterets — ignore les comptes en dessous du seuil minimum")
    @SuppressWarnings("unchecked")
    void calculerInterets_sousLeSeuil_pasDeCredit() {
        params.setSeuilMinEpargneInterets(new BigDecimal("50000"));
        CompteEpargne compte = CompteEpargne.builder()
                .soldeActuel(new BigDecimal("10000"))
                .soldeBloque(BigDecimal.ZERO)
                .statut(StatutCompteEpargne.ACTIF)
                .organisation(org)
                .build();

        io.quarkus.hibernate.orm.panache.PanacheQuery<CompteEpargne> query = mock(
                io.quarkus.hibernate.orm.panache.PanacheQuery.class);
        when(compteEpargneRepository.find(anyString(), any(UUID.class), any(StatutCompteEpargne.class)))
                .thenReturn(query);
        when(query.list()).thenReturn(List.of(compte));
        when(comptePartsSocialesRepository.findByOrganisation(orgId)).thenReturn(List.of());

        service.calculerInteretsPourOrg(params);

        verify(transactionEpargneRepository, never()).persist(any(TransactionEpargne.class));
        assertThat(compte.getSoldeActuel()).isEqualByComparingTo(new BigDecimal("10000"));
    }

    @Test
    @DisplayName("calculerInterets — taux zéro → aucune transaction créée")
    void calculerInterets_tauxZero_aucuneTransaction() {
        params.setTauxInteretAnnuelEpargne(BigDecimal.ZERO);
        params.setTauxDividendePartsAnnuel(BigDecimal.ZERO);

        when(comptePartsSocialesRepository.findByOrganisation(orgId)).thenReturn(List.of());

        Map<String, Object> result = service.calculerInteretsPourOrg(params);

        verify(transactionEpargneRepository, never()).persist(any(TransactionEpargne.class));
        assertThat((int) result.get("comptesEpargneTraites")).isZero();
    }
}
