package dev.lions.unionflow.server.api.dto.auth.response;

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

import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

/**
 * Tests unitaires pour LoginResponse.
 * Couvre builder, getters/setters, isExpired(), UserInfo, EntiteInfo.
 *
 * @author UnionFlow Team
 * @version 1.0
 * @since 2026-03-01
 */
@DisplayName("Tests LoginResponse")
class LoginResponseTest {

    @Nested
    @DisplayName("Builder et construction")
    class Builder {

        @Test
        @DisplayName("builder avec tous les champs")
        void testBuilderComplet() {
            LocalDateTime expiration = LocalDateTime.now().plusHours(1);
            LoginResponse.UserInfo user = new LoginResponse.UserInfo();

            LoginResponse response = LoginResponse.builder()
                .accessToken("access_token_123")
                .refreshToken("refresh_token_456")
                .tokenType("Bearer")
                .expiresIn(3600L)
                .expirationDate(expiration)
                .user(user)
                .build();

            assertThat(response.getAccessToken()).isEqualTo("access_token_123");
            assertThat(response.getRefreshToken()).isEqualTo("refresh_token_456");
            assertThat(response.getTokenType()).isEqualTo("Bearer");
            assertThat(response.getExpiresIn()).isEqualTo(3600L);
            assertThat(response.getExpirationDate()).isEqualTo(expiration);
            assertThat(response.getUser()).isEqualTo(user);
        }

        @Test
        @DisplayName("builder avec tokenType par défaut")
        void testBuilderTokenTypeParDefaut() {
            LoginResponse response = LoginResponse.builder()
                .accessToken("token")
                .refreshToken("refresh")
                .expiresIn(3600L)
                .build();

            assertThat(response.getTokenType()).isEqualTo("Bearer");
        }

        @Test
        @DisplayName("constructeur NoArgs")
        void testConstructeurNoArgs() {
            LoginResponse response = new LoginResponse();
            assertThat(response).isNotNull();
            assertThat(response.getAccessToken()).isNull();
        }

        @Test
        @DisplayName("constructeur AllArgs")
        void testConstructeurAllArgs() {
            LocalDateTime expiration = LocalDateTime.now().plusHours(1);
            LoginResponse.UserInfo user = new LoginResponse.UserInfo();

            LoginResponse response = new LoginResponse(
                "access",
                "refresh",
                "Bearer",
                3600L,
                expiration,
                user
            );

            assertThat(response.getAccessToken()).isEqualTo("access");
            assertThat(response.getRefreshToken()).isEqualTo("refresh");
            assertThat(response.getTokenType()).isEqualTo("Bearer");
            assertThat(response.getExpiresIn()).isEqualTo(3600L);
            assertThat(response.getExpirationDate()).isEqualTo(expiration);
            assertThat(response.getUser()).isEqualTo(user);
        }
    }

    @Nested
    @DisplayName("Getters et Setters")
    class GettersSetters {

        @Test
        @DisplayName("setters et getters fonctionnent")
        void testSettersGetters() {
            LoginResponse response = new LoginResponse();
            LocalDateTime expiration = LocalDateTime.now().plusHours(2);
            LoginResponse.UserInfo user = new LoginResponse.UserInfo();

            response.setAccessToken("new_access");
            response.setRefreshToken("new_refresh");
            response.setTokenType("Custom");
            response.setExpiresIn(7200L);
            response.setExpirationDate(expiration);
            response.setUser(user);

            assertThat(response.getAccessToken()).isEqualTo("new_access");
            assertThat(response.getRefreshToken()).isEqualTo("new_refresh");
            assertThat(response.getTokenType()).isEqualTo("Custom");
            assertThat(response.getExpiresIn()).isEqualTo(7200L);
            assertThat(response.getExpirationDate()).isEqualTo(expiration);
            assertThat(response.getUser()).isEqualTo(user);
        }
    }

    @Nested
    @DisplayName("isExpired")
    class IsExpired {

        @Test
        @DisplayName("retourne false quand expirationDate dans le futur")
        void testNonExpire() {
            LoginResponse response = LoginResponse.builder()
                .accessToken("token")
                .refreshToken("refresh")
                .expirationDate(LocalDateTime.now().plusHours(1))
                .build();

            assertThat(response.isExpired()).isFalse();
        }

        @Test
        @DisplayName("retourne true quand expirationDate dans le passé")
        void testExpire() {
            LoginResponse response = LoginResponse.builder()
                .accessToken("token")
                .refreshToken("refresh")
                .expirationDate(LocalDateTime.now().minusHours(1))
                .build();

            assertThat(response.isExpired()).isTrue();
        }

        @Test
        @DisplayName("retourne false quand expirationDate null")
        void testExpirationNull() {
            LoginResponse response = LoginResponse.builder()
                .accessToken("token")
                .refreshToken("refresh")
                .expirationDate(null)
                .build();

            assertThat(response.isExpired()).isFalse();
        }
    }

    @Nested
    @DisplayName("UserInfo - Builder et construction")
    class UserInfoBuilder {

        @Test
        @DisplayName("builder UserInfo avec tous les champs")
        void testBuilderComplet() {
            UUID userId = UUID.randomUUID();
            LoginResponse.EntiteInfo entite = new LoginResponse.EntiteInfo();
            List<String> roles = Arrays.asList("ADMIN", "MEMBRE");
            List<String> permissions = Arrays.asList("READ", "WRITE");

            LoginResponse.UserInfo user = LoginResponse.UserInfo.builder()
                .id(userId)
                .nom("Diallo")
                .prenom("Amadou")
                .email("amadou@test.com")
                .username("adiallo")
                .typeCompte("ADMIN")
                .roles(roles)
                .permissions(permissions)
                .entite(entite)
                .build();

            assertThat(user.getId()).isEqualTo(userId);
            assertThat(user.getNom()).isEqualTo("Diallo");
            assertThat(user.getPrenom()).isEqualTo("Amadou");
            assertThat(user.getEmail()).isEqualTo("amadou@test.com");
            assertThat(user.getUsername()).isEqualTo("adiallo");
            assertThat(user.getTypeCompte()).isEqualTo("ADMIN");
            assertThat(user.getRoles()).isEqualTo(roles);
            assertThat(user.getPermissions()).isEqualTo(permissions);
            assertThat(user.getEntite()).isEqualTo(entite);
        }

        @Test
        @DisplayName("constructeur NoArgs UserInfo")
        void testConstructeurNoArgs() {
            LoginResponse.UserInfo user = new LoginResponse.UserInfo();
            assertThat(user).isNotNull();
            assertThat(user.getId()).isNull();
        }

        @Test
        @DisplayName("constructeur AllArgs UserInfo")
        void testConstructeurAllArgs() {
            UUID userId = UUID.randomUUID();
            LoginResponse.EntiteInfo entite = new LoginResponse.EntiteInfo();
            List<String> roles = Collections.singletonList("MEMBRE");
            List<String> permissions = Collections.singletonList("READ");

            LoginResponse.UserInfo user = new LoginResponse.UserInfo(
                userId,
                "Traoré",
                "Fatou",
                "fatou@test.com",
                "ftraore",
                "MEMBRE",
                roles,
                permissions,
                entite
            );

            assertThat(user.getId()).isEqualTo(userId);
            assertThat(user.getNom()).isEqualTo("Traoré");
            assertThat(user.getPrenom()).isEqualTo("Fatou");
            assertThat(user.getEmail()).isEqualTo("fatou@test.com");
            assertThat(user.getUsername()).isEqualTo("ftraore");
            assertThat(user.getTypeCompte()).isEqualTo("MEMBRE");
            assertThat(user.getRoles()).isEqualTo(roles);
            assertThat(user.getPermissions()).isEqualTo(permissions);
            assertThat(user.getEntite()).isEqualTo(entite);
        }
    }

    @Nested
    @DisplayName("UserInfo - Getters et Setters")
    class UserInfoGettersSetters {

        @Test
        @DisplayName("setters et getters fonctionnent")
        void testSettersGetters() {
            LoginResponse.UserInfo user = new LoginResponse.UserInfo();
            UUID userId = UUID.randomUUID();
            LoginResponse.EntiteInfo entite = new LoginResponse.EntiteInfo();
            List<String> roles = Arrays.asList("ROLE1", "ROLE2");
            List<String> permissions = Arrays.asList("PERM1", "PERM2");

            user.setId(userId);
            user.setNom("Ndiaye");
            user.setPrenom("Moussa");
            user.setEmail("moussa@test.com");
            user.setUsername("mndiaye");
            user.setTypeCompte("SUPER_ADMIN");
            user.setRoles(roles);
            user.setPermissions(permissions);
            user.setEntite(entite);

            assertThat(user.getId()).isEqualTo(userId);
            assertThat(user.getNom()).isEqualTo("Ndiaye");
            assertThat(user.getPrenom()).isEqualTo("Moussa");
            assertThat(user.getEmail()).isEqualTo("moussa@test.com");
            assertThat(user.getUsername()).isEqualTo("mndiaye");
            assertThat(user.getTypeCompte()).isEqualTo("SUPER_ADMIN");
            assertThat(user.getRoles()).isEqualTo(roles);
            assertThat(user.getPermissions()).isEqualTo(permissions);
            assertThat(user.getEntite()).isEqualTo(entite);
        }
    }

    @Nested
    @DisplayName("UserInfo - getNomComplet")
    class UserInfoGetNomComplet {

        @Test
        @DisplayName("retourne prenom + nom quand les deux sont présents")
        void testPrenomEtNom() {
            LoginResponse.UserInfo user = new LoginResponse.UserInfo();
            user.setPrenom("Amadou");
            user.setNom("Diallo");
            user.setUsername("adiallo");

            assertThat(user.getNomComplet()).isEqualTo("Amadou Diallo");
        }

        @Test
        @DisplayName("retourne nom quand prenom null")
        void testNomSeul() {
            LoginResponse.UserInfo user = new LoginResponse.UserInfo();
            user.setNom("Diallo");
            user.setUsername("adiallo");

            assertThat(user.getNomComplet()).isEqualTo("Diallo");
        }

        @Test
        @DisplayName("retourne username quand nom null")
        void testUsernameSeul() {
            LoginResponse.UserInfo user = new LoginResponse.UserInfo();
            user.setPrenom("Amadou");
            user.setUsername("adiallo");

            assertThat(user.getNomComplet()).isEqualTo("adiallo");
        }

        @Test
        @DisplayName("retourne username quand nom et prenom null")
        void testUsernameParDefaut() {
            LoginResponse.UserInfo user = new LoginResponse.UserInfo();
            user.setUsername("admin");

            assertThat(user.getNomComplet()).isEqualTo("admin");
        }

        @Test
        @DisplayName("retourne null quand tout est null")
        void testToutNull() {
            LoginResponse.UserInfo user = new LoginResponse.UserInfo();

            assertThat(user.getNomComplet()).isNull();
        }
    }

    @Nested
    @DisplayName("EntiteInfo - Builder et construction")
    class EntiteInfoBuilder {

        @Test
        @DisplayName("builder EntiteInfo avec tous les champs")
        void testBuilderComplet() {
            UUID entiteId = UUID.randomUUID();

            LoginResponse.EntiteInfo entite = LoginResponse.EntiteInfo.builder()
                .id(entiteId)
                .nom("Association ABC")
                .type("Association")
                .pays("Sénégal")
                .ville("Dakar")
                .build();

            assertThat(entite.getId()).isEqualTo(entiteId);
            assertThat(entite.getNom()).isEqualTo("Association ABC");
            assertThat(entite.getType()).isEqualTo("Association");
            assertThat(entite.getPays()).isEqualTo("Sénégal");
            assertThat(entite.getVille()).isEqualTo("Dakar");
        }

        @Test
        @DisplayName("constructeur NoArgs EntiteInfo")
        void testConstructeurNoArgs() {
            LoginResponse.EntiteInfo entite = new LoginResponse.EntiteInfo();
            assertThat(entite).isNotNull();
            assertThat(entite.getId()).isNull();
        }

        @Test
        @DisplayName("constructeur AllArgs EntiteInfo")
        void testConstructeurAllArgs() {
            UUID entiteId = UUID.randomUUID();

            LoginResponse.EntiteInfo entite = new LoginResponse.EntiteInfo(
                entiteId,
                "Mutuelle XYZ",
                "Mutuelle",
                "Mali",
                "Bamako"
            );

            assertThat(entite.getId()).isEqualTo(entiteId);
            assertThat(entite.getNom()).isEqualTo("Mutuelle XYZ");
            assertThat(entite.getType()).isEqualTo("Mutuelle");
            assertThat(entite.getPays()).isEqualTo("Mali");
            assertThat(entite.getVille()).isEqualTo("Bamako");
        }
    }

    @Nested
    @DisplayName("EntiteInfo - Getters et Setters")
    class EntiteInfoGettersSetters {

        @Test
        @DisplayName("setters et getters fonctionnent")
        void testSettersGetters() {
            LoginResponse.EntiteInfo entite = new LoginResponse.EntiteInfo();
            UUID entiteId = UUID.randomUUID();

            entite.setId(entiteId);
            entite.setNom("Coopérative DEF");
            entite.setType("Coopérative");
            entite.setPays("Côte d'Ivoire");
            entite.setVille("Abidjan");

            assertThat(entite.getId()).isEqualTo(entiteId);
            assertThat(entite.getNom()).isEqualTo("Coopérative DEF");
            assertThat(entite.getType()).isEqualTo("Coopérative");
            assertThat(entite.getPays()).isEqualTo("Côte d'Ivoire");
            assertThat(entite.getVille()).isEqualTo("Abidjan");
        }
    }
}
