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

import dev.lions.unionflow.server.api.dto.mutuelle.financier.ParametresFinanciersMutuellRequest;
import dev.lions.unionflow.server.api.dto.mutuelle.financier.ParametresFinanciersMutuellResponse;
import dev.lions.unionflow.server.service.mutuelle.InteretsEpargneService;
import dev.lions.unionflow.server.service.mutuelle.ParametresFinanciersService;
import jakarta.ws.rs.core.Response;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import java.util.Map;
import java.util.UUID;

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

@ExtendWith(MockitoExtension.class)
@DisplayName("Tests ParametresFinanciersResource")
class ParametresFinanciersResourceTest {

    @Mock
    ParametresFinanciersService parametresService;

    @Mock
    InteretsEpargneService interetsService;

    @InjectMocks
    ParametresFinanciersResource resource;

    private ParametresFinanciersMutuellResponse buildResponse() {
        return mock(ParametresFinanciersMutuellResponse.class);
    }

    private ParametresFinanciersMutuellRequest buildRequest() {
        return mock(ParametresFinanciersMutuellRequest.class);
    }

    // ── GET /{orgId} ──────────────────────────────────────────────────────────

    @Nested
    @DisplayName("GET /{orgId}")
    class GetByOrganisation {

        @Test
        @DisplayName("retourne 200 avec les paramètres quand organisation trouvée")
        void getByOrganisation_success_returns200() {
            UUID orgId = UUID.randomUUID();
            ParametresFinanciersMutuellResponse resp = buildResponse();
            when(parametresService.getByOrganisation(orgId)).thenReturn(resp);

            Response r = resource.getByOrganisation(orgId);

            assertThat(r.getStatus()).isEqualTo(200);
            assertThat(r.getEntity()).isEqualTo(resp);
            verify(parametresService).getByOrganisation(orgId);
        }

        @Test
        @DisplayName("retourne 200 avec entité null quand service retourne null (aucun paramètre)")
        void getByOrganisation_aucunParametre_returns200WithNull() {
            UUID orgId = UUID.randomUUID();
            when(parametresService.getByOrganisation(orgId)).thenReturn(null);

            Response r = resource.getByOrganisation(orgId);

            assertThat(r.getStatus()).isEqualTo(200);
            assertThat(r.getEntity()).isNull();
        }

        @Test
        @DisplayName("propage RuntimeException levée par le service")
        void getByOrganisation_serviceException_propagates() {
            UUID orgId = UUID.randomUUID();
            when(parametresService.getByOrganisation(orgId))
                    .thenThrow(new RuntimeException("Erreur base de données"));

            org.junit.jupiter.api.Assertions.assertThrows(RuntimeException.class,
                    () -> resource.getByOrganisation(orgId));
        }

        @Test
        @DisplayName("appelle le service avec l'UUID exact fourni")
        void getByOrganisation_passesCorrectUUID() {
            UUID orgId = UUID.fromString("11111111-2222-3333-4444-555555555555");
            when(parametresService.getByOrganisation(orgId)).thenReturn(buildResponse());

            resource.getByOrganisation(orgId);

            verify(parametresService).getByOrganisation(eq(orgId));
            verifyNoInteractions(interetsService);
        }
    }

    // ── POST / ────────────────────────────────────────────────────────────────

    @Nested
    @DisplayName("POST /")
    class CreerOuMettreAJour {

        @Test
        @DisplayName("retourne 200 après création ou mise à jour réussie")
        void creerOuMettrAJour_success_returns200() {
            ParametresFinanciersMutuellRequest req = buildRequest();
            ParametresFinanciersMutuellResponse resp = buildResponse();
            when(parametresService.creerOuMettrAJour(req)).thenReturn(resp);

            Response r = resource.creerOuMettrAJour(req);

            assertThat(r.getStatus()).isEqualTo(200);
            assertThat(r.getEntity()).isEqualTo(resp);
        }

        @Test
        @DisplayName("retourne 200 avec la réponse retournée par le service")
        void creerOuMettrAJour_retourneReponseService() {
            ParametresFinanciersMutuellRequest req = buildRequest();
            ParametresFinanciersMutuellResponse resp = buildResponse();
            when(parametresService.creerOuMettrAJour(req)).thenReturn(resp);

            Response r = resource.creerOuMettrAJour(req);

            assertThat(r.getEntity()).isSameAs(resp);
            verify(parametresService, times(1)).creerOuMettrAJour(req);
        }

        @Test
        @DisplayName("propage IllegalArgumentException levée par le service (validation métier)")
        void creerOuMettrAJour_tauxInvalide_propagates() {
            ParametresFinanciersMutuellRequest req = buildRequest();
            when(parametresService.creerOuMettrAJour(req))
                    .thenThrow(new IllegalArgumentException("Le taux d'intérêt doit être positif"));

            org.junit.jupiter.api.Assertions.assertThrows(IllegalArgumentException.class,
                    () -> resource.creerOuMettrAJour(req));
        }

        @Test
        @DisplayName("n'appelle pas interetsService lors de la création/mise à jour")
        void creerOuMettrAJour_neContactePasInteretsService() {
            ParametresFinanciersMutuellRequest req = buildRequest();
            when(parametresService.creerOuMettrAJour(any())).thenReturn(buildResponse());

            resource.creerOuMettrAJour(req);

            verifyNoInteractions(interetsService);
        }
    }

    // ── POST /{orgId}/calculer-interets ───────────────────────────────────────

    @Nested
    @DisplayName("POST /{orgId}/calculer-interets")
    class CalculerInterets {

        @Test
        @DisplayName("retourne 200 avec le résultat du calcul")
        void calculerInterets_success_returns200() {
            UUID orgId = UUID.randomUUID();
            Map<String, Object> resultat = Map.of(
                    "nombreComptes", 42,
                    "interetsCalcules", "250000",
                    "statut", "SUCCESS"
            );
            when(interetsService.calculerManuellement(orgId)).thenReturn(resultat);

            Response r = resource.calculerInterets(orgId);

            assertThat(r.getStatus()).isEqualTo(200);
            assertThat(r.getEntity()).isEqualTo(resultat);
        }

        @Test
        @DisplayName("retourne 200 avec résultat vide si aucun compte")
        void calculerInterets_aucunCompte_returns200() {
            UUID orgId = UUID.randomUUID();
            when(interetsService.calculerManuellement(orgId)).thenReturn(Map.of());

            Response r = resource.calculerInterets(orgId);

            assertThat(r.getStatus()).isEqualTo(200);
            @SuppressWarnings("unchecked")
            Map<String, Object> body = (Map<String, Object>) r.getEntity();
            assertThat(body).isEmpty();
        }

        @Test
        @DisplayName("appelle interetsService avec l'UUID exact fourni")
        void calculerInterets_passesCorrectUUID() {
            UUID orgId = UUID.fromString("aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee");
            when(interetsService.calculerManuellement(orgId)).thenReturn(Map.of("done", true));

            resource.calculerInterets(orgId);

            verify(interetsService).calculerManuellement(eq(orgId));
            verifyNoInteractions(parametresService);
        }

        @Test
        @DisplayName("propage RuntimeException levée par interetsService")
        void calculerInterets_serviceException_propagates() {
            UUID orgId = UUID.randomUUID();
            when(interetsService.calculerManuellement(orgId))
                    .thenThrow(new RuntimeException("Paramètres financiers non configurés pour cette organisation"));

            org.junit.jupiter.api.Assertions.assertThrows(RuntimeException.class,
                    () -> resource.calculerInterets(orgId));
        }

        @Test
        @DisplayName("retourne 200 avec résultat contenant statistiques détaillées")
        void calculerInterets_retourneStatistiquesDetaillees() {
            UUID orgId = UUID.randomUUID();
            Map<String, Object> resultat = Map.of(
                    "nombreComptes", 10,
                    "totalEpargne", "5000000",
                    "tauxApplique", "0.05",
                    "periodeDebut", "2026-01-01",
                    "periodeFin", "2026-03-31"
            );
            when(interetsService.calculerManuellement(orgId)).thenReturn(resultat);

            Response r = resource.calculerInterets(orgId);

            assertThat(r.getStatus()).isEqualTo(200);
            @SuppressWarnings("unchecked")
            Map<String, Object> body = (Map<String, Object>) r.getEntity();
            assertThat(body).containsKey("nombreComptes");
            assertThat(body).containsKey("tauxApplique");
        }
    }
}
