package dev.lions.unionflow.server.client;

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

import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.security.TestSecurity;
import jakarta.inject.Inject;
import jakarta.ws.rs.core.MultivaluedHashMap;
import jakarta.ws.rs.core.MultivaluedMap;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

/**
 * Tests pour {@link OidcTokenPropagationHeadersFactory}.
 *
 * <p>Couvre :
 * <ul>
 *   <li>Stratégie 1 : propagation depuis incomingHeaders (Authorization présent)</li>
 *   <li>Stratégie 2a : identité anonyme → aucun token propagé</li>
 *   <li>Stratégie 2b : identité authentifiée mais principal non-OidcJwtCallerPrincipal (TestSecurity)</li>
 * </ul>
 */
@QuarkusTest
class OidcTokenPropagationHeadersFactoryTest {

    @Inject
    OidcTokenPropagationHeadersFactory factory;

    // ─── Stratégie 1 : Authorization présent dans incomingHeaders ───────────

    @Test
    @DisplayName("update copie Authorization depuis incomingHeaders si présent")
    void update_withIncomingAuthHeader_propagatesToken() {
        MultivaluedMap<String, String> incoming = new MultivaluedHashMap<>();
        incoming.add("Authorization", "Bearer test-token-xyz");
        MultivaluedMap<String, String> outgoing = new MultivaluedHashMap<>();

        MultivaluedMap<String, String> result = factory.update(incoming, outgoing);

        assertThat(result.getFirst("Authorization")).isEqualTo("Bearer test-token-xyz");
    }

    @Test
    @DisplayName("update avec Authorization vide dans incomingHeaders passe à la stratégie 2")
    void update_withBlankIncomingAuthHeader_fallsToStrategy2() {
        MultivaluedMap<String, String> incoming = new MultivaluedHashMap<>();
        incoming.add("Authorization", "   ");
        MultivaluedMap<String, String> outgoing = new MultivaluedHashMap<>();

        // Stratégie 1 échoue (vide) → tombe sur stratégie 2 (SecurityIdentity)
        MultivaluedMap<String, String> result = factory.update(incoming, outgoing);

        // Pas de token propagé depuis les incomingHeaders (blank)
        assertThat(result).isNotNull();
    }

    @Test
    @DisplayName("update sans Authorization dans incomingHeaders passe à la stratégie 2")
    void update_withoutIncomingAuthHeader_usesStrategy2() {
        MultivaluedMap<String, String> incoming = new MultivaluedHashMap<>();
        MultivaluedMap<String, String> outgoing = new MultivaluedHashMap<>();

        MultivaluedMap<String, String> result = factory.update(incoming, outgoing);

        // Sans identité OIDC → résultat vide (pas de token propagé)
        assertThat(result).isNotNull();
    }

    // ─── Stratégie 2b : identité authentifiée TestSecurity (pas OidcJwtCallerPrincipal) ─

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN"})
    @DisplayName("update avec identité TestSecurity (non-OIDC) retourne map vide (principal non-OIDC)")
    void update_withTestSecurityIdentity_returnsEmptyMap() {
        MultivaluedMap<String, String> incoming = new MultivaluedHashMap<>();
        MultivaluedMap<String, String> outgoing = new MultivaluedHashMap<>();

        // Avec TestSecurity, SecurityIdentity est non-anonyme mais le principal
        // n'est pas un OidcJwtCallerPrincipal → branche "Principal n'est pas OidcJwtCallerPrincipal"
        MultivaluedMap<String, String> result = factory.update(incoming, outgoing);

        // Aucun token propagé (pas d'OidcJwtCallerPrincipal)
        assertThat(result).isNotNull();
        assertThat(result.containsKey("Authorization")).isFalse();
    }

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN"})
    @DisplayName("update avec incomingHeaders Authorization valide retourne le token même avec @TestSecurity")
    void update_withValidIncomingAndTestSecurity_propagatesIncomingToken() {
        MultivaluedMap<String, String> incoming = new MultivaluedHashMap<>();
        incoming.add("Authorization", "Bearer incoming-token");
        MultivaluedMap<String, String> outgoing = new MultivaluedHashMap<>();

        MultivaluedMap<String, String> result = factory.update(incoming, outgoing);

        // Stratégie 1 réussit → retourne le token incoming
        assertThat(result.getFirst("Authorization")).isEqualTo("Bearer incoming-token");
    }
}
