package dev.lions.unionflow.server.security;

import static io.restassured.RestAssured.given;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;

import io.quarkus.test.InjectMock;
import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.security.TestSecurity;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.microprofile.jwt.JsonWebToken;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

/**
 * Tests pour {@link RoleDebugFilter} avec un {@code JsonWebToken} mocké.
 *
 * <p>La classe {@link RoleDebugFilterTest} ne couvre pas les branches {@code if (jwt != null)}
 * car dans un contexte {@code @TestSecurity} sans JWT réel, les claims retournent null.
 * Ce test utilise {@code @InjectMock JsonWebToken} pour rendre le jwt non-null avec des
 * claims renseignés, couvrant les lignes 41-53 et 62-64.
 */
@QuarkusTest
@DisplayName("RoleDebugFilter (JWT mocké)")
class RoleDebugFilterJwtMockTest {

    @InjectMock
    JsonWebToken jwt;

    @InjectMock
    io.quarkus.security.identity.SecurityIdentity securityIdentity;

    @BeforeEach
    void setUp() {
        // JWT non null avec claims renseignés → couvre lignes 41-53 et 62-64
        when(jwt.getSubject()).thenReturn("user@test.com");
        when(jwt.getName()).thenReturn("Test User");

        // realm_access est un Map avec une clé "roles" → couvre lignes 47-53
        Map<String, Object> realmAccess = Map.of("roles", List.of("ADMIN", "USER"));
        when(jwt.<Object>getClaim(eq("realm_access"))).thenReturn(realmAccess);

        // resource_access non null → couvre ligne 63-64
        Map<String, Object> resourceAccess = Map.of("unionflow-mobile", Map.of("roles", List.of("user")));
        when(jwt.<Object>getClaim(eq("resource_access"))).thenReturn(resourceAccess);

        // SecurityIdentity mock
        java.security.Principal principal = () -> "user@test.com";
        when(securityIdentity.getPrincipal()).thenReturn(principal);
        when(securityIdentity.getRoles()).thenReturn(Set.of("ADMIN"));
        when(securityIdentity.isAnonymous()).thenReturn(false);
    }

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN"})
    @DisplayName("filter avec JWT mocké non-null et realm_access Map — couvre branches lignes 41-53 et 62-64")
    void filter_jwtMocked_withRealmAccessMap_coversInnerBranches() {
        // Appel à /api/status : le filtre s'exécute pour ce chemin → jwt != null → Map branch
        given()
                .when()
                .get("/api/status")
                .then()
                .statusCode(200);
    }

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN"})
    @DisplayName("filter avec realm_access non-Map (String) — couvre la branche instanceof Map = false")
    void filter_jwtMocked_realmAccessNotMap_coversNotMapBranch() {
        // Override: realm_access is a String (not a Map) → instanceof Map is false → skip inner block
        when(jwt.<Object>getClaim(eq("realm_access"))).thenReturn("roles:[ADMIN]");

        given()
                .when()
                .get("/api/status")
                .then()
                .statusCode(200);
    }

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN"})
    @DisplayName("filter avec realm_access null — couvre la branche realmAccess == null")
    void filter_jwtMocked_realmAccessNull_coversNullBranch() {
        // Override: realm_access is null → inner if (realmAccess != null) is false
        when(jwt.<Object>getClaim(eq("realm_access"))).thenReturn(null);

        given()
                .when()
                .get("/api/status")
                .then()
                .statusCode(200);
    }

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN"})
    @DisplayName("filter avec resource_access null — couvre la branche resourceAccess == null")
    void filter_jwtMocked_resourceAccessNull_coversNullBranch() {
        // Override: resource_access is null → inner if (resourceAccess != null) is false
        when(jwt.<Object>getClaim(eq("resource_access"))).thenReturn(null);

        given()
                .when()
                .get("/api/status")
                .then()
                .statusCode(200);
    }
}
