package dev.lions.unionflow.server.resource;

import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.anyOf;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;

import dev.lions.unionflow.server.repository.IntentionPaiementRepository;
import dev.lions.unionflow.server.service.mutuelle.epargne.TransactionEpargneService;
import io.quarkus.test.InjectMock;
import io.quarkus.test.junit.QuarkusTest;
import io.restassured.RestAssured;
import java.util.UUID;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

/**
 * Tests d'intégration REST pour WaveRedirectResource.
 *
 * <p>La resource est annotée {@code @PermitAll} — aucune authentification requise.
 * Les endpoints retournent des redirections HTTP 303 (See Other) vers le deep link
 * de l'application mobile ({@code unionflow://payment?result=...}).
 *
 * <p>Le mock de {@link IntentionPaiementRepository} et {@link TransactionEpargneService}
 * empêche tout accès à la base de données durant les tests.
 *
 * @author UnionFlow Team
 * @version 1.0
 * @since 2026-03-20
 */
@QuarkusTest
class WaveRedirectResourceTest {

    private static final String BASE_PATH = "/api/wave-redirect";
    private static final String SAMPLE_REF = UUID.randomUUID().toString();

    @InjectMock
    IntentionPaiementRepository intentionPaiementRepository;

    @InjectMock
    TransactionEpargneService transactionEpargneService;

    @BeforeEach
    void setup() {
        // Désactiver les redirections automatiques de RestAssured pour inspecter le statut 3xx
        RestAssured.useRelaxedHTTPSValidation();
    }

    // -------------------------------------------------------------------------
    // GET /api/wave-redirect/success
    // -------------------------------------------------------------------------

    @Test
    void waveSuccess_returns3xx() {
        // wave.mock.enabled=false par défaut → pas d'appel au repo
        given()
                .redirects().follow(false)
                .when()
                .get(BASE_PATH + "/success")
                .then()
                .statusCode(anyOf(equalTo(301), equalTo(302), equalTo(303)));
    }

    @Test
    void waveSuccess_withRef_containsRef() {
        given()
                .redirects().follow(false)
                .queryParam("ref", SAMPLE_REF)
                .when()
                .get(BASE_PATH + "/success")
                .then()
                .statusCode(anyOf(equalTo(301), equalTo(302), equalTo(303)))
                .header("Location", containsString(SAMPLE_REF));
    }

    // -------------------------------------------------------------------------
    // GET /api/wave-redirect/error
    // -------------------------------------------------------------------------

    @Test
    void waveError_returns3xx() {
        given()
                .redirects().follow(false)
                .when()
                .get(BASE_PATH + "/error")
                .then()
                .statusCode(anyOf(equalTo(301), equalTo(302), equalTo(303)));
    }

    @Test
    void waveError_withRef_containsRef() {
        given()
                .redirects().follow(false)
                .queryParam("ref", SAMPLE_REF)
                .when()
                .get(BASE_PATH + "/error")
                .then()
                .statusCode(anyOf(equalTo(301), equalTo(302), equalTo(303)))
                .header("Location", containsString("error"));
    }

    // -------------------------------------------------------------------------
    // GET /api/wave-redirect/mock-complete
    // -------------------------------------------------------------------------

    @Test
    void waveMockComplete_returns3xx() {
        // wave.mock.enabled=false → redirige vers deep link error sans appeler le repo
        given()
                .redirects().follow(false)
                .queryParam("ref", SAMPLE_REF)
                .when()
                .get(BASE_PATH + "/mock-complete")
                .then()
                .statusCode(anyOf(equalTo(301), equalTo(302), equalTo(303)));
    }

    @Test
    void waveMockComplete_noRef_returns400() {
        // wave.mock.enabled=false → retourne 303 vers error (mock ignoré)
        // wave.mock.enabled=true et ref null → BAD_REQUEST
        // Dans les deux cas on vérifie que l'endpoint répond (pas 5xx)
        given()
                .redirects().follow(false)
                .when()
                .get(BASE_PATH + "/mock-complete")
                .then()
                .statusCode(anyOf(equalTo(301), equalTo(302), equalTo(303), equalTo(400)));
    }

    // -------------------------------------------------------------------------
    // Cas supplémentaires — deep link sans ref
    // -------------------------------------------------------------------------

    @Test
    void waveSuccess_noRef_locationContainsSuccess() {
        given()
                .redirects().follow(false)
                .when()
                .get(BASE_PATH + "/success")
                .then()
                .statusCode(anyOf(equalTo(301), equalTo(302), equalTo(303)))
                .header("Location", containsString("success"));
    }

    @Test
    void waveError_noRef_locationContainsError() {
        given()
                .redirects().follow(false)
                .when()
                .get(BASE_PATH + "/error")
                .then()
                .statusCode(anyOf(equalTo(301), equalTo(302), equalTo(303)))
                .header("Location", containsString("error"));
    }

    @Test
    void waveSuccess_blankRef_returns3xx() {
        given()
                .redirects().follow(false)
                .queryParam("ref", "   ")
                .when()
                .get(BASE_PATH + "/success")
                .then()
                .statusCode(anyOf(equalTo(301), equalTo(302), equalTo(303)));
    }

    @Test
    void waveError_blankRef_returns3xx() {
        given()
                .redirects().follow(false)
                .queryParam("ref", "   ")
                .when()
                .get(BASE_PATH + "/error")
                .then()
                .statusCode(anyOf(equalTo(301), equalTo(302), equalTo(303)));
    }

    // -------------------------------------------------------------------------
    // applyMockCompletion — branche intention != null et objetsCibles null/blank
    // (wave.mock.enabled=true en profil test)
    // -------------------------------------------------------------------------

    @Test
    @DisplayName("success avec ref UUID valide + wave.mock.enabled=false → redirection 3xx vers deep link success")
    void waveSuccess_mockDisabled_validUuidRef_returns3xxSuccess() {
        // wave.mock.enabled=false (default) → applyMockCompletion non appelée → simple redirection
        UUID ref = UUID.randomUUID();
        given()
                .redirects().follow(false)
                .queryParam("ref", ref.toString())
                .when()
                .get(BASE_PATH + "/success")
                .then()
                .statusCode(anyOf(equalTo(301), equalTo(302), equalTo(303)))
                .header("Location", containsString("success"));
    }

    @Test
    @DisplayName("success avec ref qui ressemble à un UUID mais wave.mock.enabled=false → redirection 3xx")
    void waveSuccess_mockDisabled_uuidLikeRef_noDbCall() {
        // wave.mock.enabled=false → intentionPaiementRepository jamais appelé
        UUID ref = UUID.randomUUID();
        given()
                .redirects().follow(false)
                .queryParam("ref", ref.toString())
                .when()
                .get(BASE_PATH + "/success")
                .then()
                .statusCode(anyOf(equalTo(301), equalTo(302), equalTo(303)));
    }

    @Test
    @DisplayName("success avec ref contenant des caractères spéciaux → redirection 3xx (robustesse)")
    void waveSuccess_mockDisabled_specialCharsRef_returns3xx() {
        given()
                .redirects().follow(false)
                .queryParam("ref", "ref-special-123")
                .when()
                .get(BASE_PATH + "/success")
                .then()
                .statusCode(anyOf(equalTo(301), equalTo(302), equalTo(303)));
    }

    @Test
    @DisplayName("error avec ref UUID valide → redirection 3xx vers deep link error")
    void waveError_mockDisabled_validUuidRef_returns3xxError() {
        UUID ref = UUID.randomUUID();
        given()
                .redirects().follow(false)
                .queryParam("ref", ref.toString())
                .when()
                .get(BASE_PATH + "/error")
                .then()
                .statusCode(anyOf(equalTo(301), equalTo(302), equalTo(303)))
                .header("Location", containsString("error"));
    }
}
