package dev.lions.unionflow.server.service;

import static org.assertj.core.api.Assertions.assertThatCode;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.when;

import dev.lions.unionflow.server.client.AdminRoleServiceClient;
import dev.lions.unionflow.server.client.AdminUserServiceClient;
import dev.lions.unionflow.server.client.RoleServiceClient;
import dev.lions.unionflow.server.client.UserServiceClient;
import dev.lions.unionflow.server.entity.Membre;
import dev.lions.unionflow.server.repository.MembreRepository;
import dev.lions.user.manager.dto.user.PasswordResetRequestDTO;
import io.quarkus.test.InjectMock;
import io.quarkus.test.junit.QuarkusTest;
import jakarta.inject.Inject;
import jakarta.ws.rs.NotFoundException;

import java.util.Optional;
import java.util.UUID;

import org.eclipse.microprofile.rest.client.inject.RestClient;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

/**
 * Tests pour {@link MembreKeycloakSyncService#changerMotDePassePremierLogin(UUID, String)}.
 * Cette méthode n'était pas couverte (15 lignes manquantes).
 */
@QuarkusTest
@DisplayName("MembreKeycloakSyncService — changerMotDePassePremierLogin")
class MembreKeycloakSyncServiceMissingTest {

    @Inject
    MembreKeycloakSyncService syncService;

    @InjectMock
    MembreRepository membreRepository;

    @InjectMock
    @RestClient
    UserServiceClient userServiceClient;

    @InjectMock
    @RestClient
    RoleServiceClient roleServiceClient;

    @InjectMock
    @RestClient
    AdminUserServiceClient adminUserServiceClient;

    @InjectMock
    @RestClient
    AdminRoleServiceClient adminRoleServiceClient;

    // =========================================================================
    // changerMotDePassePremierLogin — L486-509
    // =========================================================================

    @Test
    @DisplayName("changerMotDePassePremierLogin — membre introuvable → NotFoundException (lambda L490)")
    void changerMotDePassePremierLogin_membreIntrouvable_leveNotFoundException() {
        UUID membreId = UUID.randomUUID();
        when(membreRepository.findByIdOptional(membreId)).thenReturn(Optional.empty());

        assertThatThrownBy(() -> syncService.changerMotDePassePremierLogin(membreId, "newPassword123!"))
                .isInstanceOf(NotFoundException.class)
                .hasMessageContaining(membreId.toString());
    }

    @Test
    @DisplayName("changerMotDePassePremierLogin — membre sans keycloakId → IllegalStateException (L492-493)")
    void changerMotDePassePremierLogin_sansKeycloakId_leveIllegalStateException() {
        UUID membreId = UUID.randomUUID();

        Membre membre = new Membre();
        membre.setId(membreId);
        membre.setEmail("sans.keycloak@test.dev");
        membre.setNom("Sans");
        membre.setPrenom("Keycloak");
        membre.setKeycloakId(null); // pas de keycloakId

        when(membreRepository.findByIdOptional(membreId)).thenReturn(Optional.of(membre));

        assertThatThrownBy(() -> syncService.changerMotDePassePremierLogin(membreId, "newPassword123!"))
                .isInstanceOf(IllegalStateException.class)
                .hasMessageContaining("Keycloak");
    }

    @Test
    @DisplayName("changerMotDePassePremierLogin — happy path → resetPassword appelé + premiereConnexion=false")
    void changerMotDePassePremierLogin_happyPath_resetsPasswordAndFlagsFalse() {
        UUID membreId = UUID.randomUUID();
        UUID keycloakId = UUID.randomUUID();

        Membre membre = new Membre();
        membre.setId(membreId);
        membre.setEmail("avec.keycloak@test.dev");
        membre.setNom("Avec");
        membre.setPrenom("Keycloak");
        membre.setKeycloakId(keycloakId);
        membre.setPremiereConnexion(true);

        when(membreRepository.findByIdOptional(membreId)).thenReturn(Optional.of(membre));
        // The service field "userServiceClient" is of type AdminUserServiceClient (injected as AdminUserServiceClient)
        doNothing().when(adminUserServiceClient).resetPassword(anyString(), anyString(), any(PasswordResetRequestDTO.class));
        doNothing().when(membreRepository).persist(any(Membre.class));

        assertThatCode(() -> syncService.changerMotDePassePremierLogin(membreId, "NewPass123!"))
                .doesNotThrowAnyException();

        // premiereConnexion doit être false après l'appel (L505)
        assertThat(membre.getPremiereConnexion()).isFalse();
    }

    private static org.assertj.core.api.AbstractBooleanAssert<?> assertThat(Boolean value) {
        return org.assertj.core.api.Assertions.assertThat(value);
    }
}
