package dev.lions.unionflow.server.resource;

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

import dev.lions.unionflow.server.entity.AlerteLcbFt;
import dev.lions.unionflow.server.entity.Membre;
import dev.lions.unionflow.server.entity.Organisation;
import dev.lions.unionflow.server.repository.AlerteLcbFtRepository;
import io.quarkus.test.InjectMock;
import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.security.TestSecurity;
import io.restassured.http.ContentType;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

/**
 * Tests d'intégration REST pour AlerteLcbFtResource.
 *
 * <p>La resource utilise directement {@link AlerteLcbFtRepository} sans couche service.
 * Les NotFoundException sont lancées directement par la resource et gérées par JAX-RS
 * (→ 404 via ExceptionMapper standard).
 *
 * @author UnionFlow Team
 * @version 1.0
 * @since 2026-03-20
 */
@QuarkusTest
class AlerteLcbFtResourceTest {

    private static final String BASE_PATH = "/api/alertes-lcb-ft";
    private static final String ALERTE_ID = "00000000-0000-0000-0000-000000000010";

    @InjectMock
    AlerteLcbFtRepository alerteLcbFtRepository;

    // -------------------------------------------------------------------------
    // GET /api/alertes-lcb-ft
    // -------------------------------------------------------------------------

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN_ORGANISATION"})
    void listerAlertes_returns200() {
        when(alerteLcbFtRepository.search(any(), any(), any(), any(), any(), anyInt(), anyInt()))
                .thenReturn(Collections.emptyList());
        when(alerteLcbFtRepository.count(any(), any(), any(), any(), any()))
                .thenReturn(0L);

        given()
                .queryParam("page", 0)
                .queryParam("size", 20)
                .when()
                .get(BASE_PATH)
                .then()
                .statusCode(200)
                .contentType(ContentType.JSON)
                .body("content", notNullValue())
                .body("totalElements", equalTo(0))
                .body("totalPages", equalTo(0))
                .body("currentPage", equalTo(0));
    }

    // -------------------------------------------------------------------------
    // GET /api/alertes-lcb-ft/{id}
    // -------------------------------------------------------------------------

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN_ORGANISATION"})
    void getAlerteById_notFound_returns404() {
        when(alerteLcbFtRepository.findById(any(UUID.class))).thenReturn(null);

        given()
                .pathParam("id", ALERTE_ID)
                .when()
                .get(BASE_PATH + "/{id}")
                .then()
                .statusCode(404);
    }

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN_ORGANISATION"})
    void getAlerteById_found_returns200() {
        AlerteLcbFt alerte = buildAlerte();
        when(alerteLcbFtRepository.findById(any(UUID.class))).thenReturn(alerte);

        given()
                .pathParam("id", ALERTE_ID)
                .when()
                .get(BASE_PATH + "/{id}")
                .then()
                .statusCode(200)
                .contentType(ContentType.JSON);
    }

    // -------------------------------------------------------------------------
    // POST /api/alertes-lcb-ft/{id}/traiter
    // -------------------------------------------------------------------------

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN_ORGANISATION"})
    void traiterAlerte_notFound_returns404() {
        when(alerteLcbFtRepository.findById(any(UUID.class))).thenReturn(null);

        given()
                .contentType(ContentType.JSON)
                .pathParam("id", ALERTE_ID)
                .body("{\"traitePar\":\"00000000-0000-0000-0000-000000000001\",\"commentaire\":\"Traité\"}")
                .when()
                .post(BASE_PATH + "/{id}/traiter")
                .then()
                .statusCode(404);
    }

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN_ORGANISATION"})
    void traiterAlerte_success_returns200() {
        AlerteLcbFt alerte = buildAlerte();
        when(alerteLcbFtRepository.findById(any(UUID.class))).thenReturn(alerte);
        doNothing().when(alerteLcbFtRepository).persist(any(AlerteLcbFt.class));

        given()
                .contentType(ContentType.JSON)
                .pathParam("id", ALERTE_ID)
                .body("{\"traitePar\":\"00000000-0000-0000-0000-000000000001\",\"commentaire\":\"Traité avec succès\"}")
                .when()
                .post(BASE_PATH + "/{id}/traiter")
                .then()
                .statusCode(200)
                .contentType(ContentType.JSON);
    }

    // -------------------------------------------------------------------------
    // GET /api/alertes-lcb-ft/stats/non-traitees
    // -------------------------------------------------------------------------

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN_ORGANISATION"})
    void getStatsNonTraitees_returns200() {
        when(alerteLcbFtRepository.countNonTraitees(any())).thenReturn(3L);

        given()
                .queryParam("organisationId", "00000000-0000-0000-0000-000000000001")
                .when()
                .get(BASE_PATH + "/stats/non-traitees")
                .then()
                .statusCode(200)
                .contentType(ContentType.JSON)
                .body("count", equalTo(3));
    }

    // -------------------------------------------------------------------------
    // GET /api/alertes-lcb-ft — branches organisationId/dateDebut/dateFin non-null
    // -------------------------------------------------------------------------

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN_ORGANISATION"})
    void listerAlertes_avecOrganisationIdEtDates_returns200() {
        when(alerteLcbFtRepository.search(any(), any(), any(), any(), any(), anyInt(), anyInt()))
                .thenReturn(Collections.emptyList());
        when(alerteLcbFtRepository.count(any(), any(), any(), any(), any()))
                .thenReturn(5L);

        given()
                .queryParam("organisationId", "00000000-0000-0000-0000-000000000001")
                .queryParam("typeAlerte", "TRANSACTION_SUSPECTE")
                .queryParam("traitee", false)
                .queryParam("dateDebut", LocalDateTime.now().minusDays(7).toString())
                .queryParam("dateFin", LocalDateTime.now().toString())
                .queryParam("page", 0)
                .queryParam("size", 10)
                .when()
                .get(BASE_PATH)
                .then()
                .statusCode(200)
                .contentType(ContentType.JSON)
                .body("totalElements", equalTo(5));
    }

    // -------------------------------------------------------------------------
    // GET /api/alertes-lcb-ft/stats/non-traitees — branch organisationId null/blank
    // -------------------------------------------------------------------------

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN_ORGANISATION"})
    void getStatsNonTraitees_sansOrganisationId_returns200() {
        when(alerteLcbFtRepository.countNonTraitees(isNull())).thenReturn(7L);

        given()
                .when()
                .get(BASE_PATH + "/stats/non-traitees")
                .then()
                .statusCode(200)
                .contentType(ContentType.JSON)
                .body("count", equalTo(7));
    }

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN_ORGANISATION"})
    void getStatsNonTraitees_avecOrganisationIdBlank_returns200() {
        when(alerteLcbFtRepository.countNonTraitees(isNull())).thenReturn(2L);

        given()
                .queryParam("organisationId", "")
                .when()
                .get(BASE_PATH + "/stats/non-traitees")
                .then()
                .statusCode(200)
                .contentType(ContentType.JSON)
                .body("count", equalTo(2));
    }

    // -------------------------------------------------------------------------
    // mapToResponse — branch organisation != null et membre != null
    // -------------------------------------------------------------------------

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN_ORGANISATION"})
    void getAlerteById_avecOrganisationEtMembre_returns200() {
        AlerteLcbFt alerte = buildAlerteAvecOrganisationEtMembre();
        when(alerteLcbFtRepository.findById(any(UUID.class))).thenReturn(alerte);

        given()
                .pathParam("id", ALERTE_ID)
                .when()
                .get(BASE_PATH + "/{id}")
                .then()
                .statusCode(200)
                .contentType(ContentType.JSON);
    }

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN_ORGANISATION"})
    void listerAlertes_avecAlertesAvecOrganisationEtMembre_returns200() {
        AlerteLcbFt alerte = buildAlerteAvecOrganisationEtMembre();
        when(alerteLcbFtRepository.search(any(), any(), any(), any(), any(), anyInt(), anyInt()))
                .thenReturn(List.of(alerte));
        when(alerteLcbFtRepository.count(any(), any(), any(), any(), any()))
                .thenReturn(1L);

        given()
                .queryParam("page", 0)
                .queryParam("size", 20)
                .when()
                .get(BASE_PATH)
                .then()
                .statusCode(200)
                .contentType(ContentType.JSON)
                .body("totalElements", equalTo(1));
    }

    // -------------------------------------------------------------------------
    // Helpers
    // -------------------------------------------------------------------------

    // ── Branches manquantes L52-54 : ternaires organisationId/dateDebut/dateFin isBlank ──

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN_ORGANISATION"})
    @DisplayName("GET /api/alertes-lcb-ft avec organisationId vide (isBlank) → orgId null (branche false L52)")
    void listerAlertes_organisationIdBlank_treatedAsNull() {
        when(alerteLcbFtRepository.search(isNull(), any(), any(), any(), any(), anyInt(), anyInt()))
                .thenReturn(Collections.emptyList());
        when(alerteLcbFtRepository.count(isNull(), any(), any(), any(), any()))
                .thenReturn(0L);

        given()
                .queryParam("organisationId", "   ") // non-null mais isBlank → false → null
                .queryParam("page", 0)
                .queryParam("size", 20)
                .when()
                .get(BASE_PATH)
                .then()
                .statusCode(200);
    }

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN_ORGANISATION"})
    @DisplayName("GET /api/alertes-lcb-ft avec dateDebut vide et dateFin vide → branches isBlank false L53-54")
    void listerAlertes_datesBlank_treatedAsNull() {
        when(alerteLcbFtRepository.search(any(), any(), any(), isNull(), isNull(), anyInt(), anyInt()))
                .thenReturn(Collections.emptyList());
        when(alerteLcbFtRepository.count(any(), any(), any(), isNull(), isNull()))
                .thenReturn(0L);

        given()
                .queryParam("dateDebut", "   ") // non-null mais isBlank → false → null
                .queryParam("dateFin", "   ")   // non-null mais isBlank → false → null
                .queryParam("page", 0)
                .queryParam("size", 20)
                .when()
                .get(BASE_PATH)
                .then()
                .statusCode(200);
    }

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN_ORGANISATION"})
    @DisplayName("POST /api/alertes-lcb-ft/{id}/traiter — traitePar vide (isBlank) → skip UUID parsing → 200 (couvre branche false L117)")
    void traiterAlerte_blankTraitePar_skipsUuidParsing_returns200() {
        AlerteLcbFt alerte = buildAlerte();
        when(alerteLcbFtRepository.findById(any(UUID.class))).thenReturn(alerte);
        doNothing().when(alerteLcbFtRepository).persist(any(AlerteLcbFt.class));

        given()
                .contentType(ContentType.JSON)
                .pathParam("id", ALERTE_ID)
                .body("{\"traitePar\":\"\",\"commentaire\":\"Traité sans UUID\"}")
                .when()
                .post(BASE_PATH + "/{id}/traiter")
                .then()
                .statusCode(200);
    }

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN_ORGANISATION"})
    @DisplayName("POST /api/alertes-lcb-ft/{id}/traiter — traitePar UUID invalide → 400 (couvre L120-121)")
    void traiterAlerte_invalidTraiteParUuid_returns400() {
        AlerteLcbFt alerte = buildAlerte();
        when(alerteLcbFtRepository.findById(any(UUID.class))).thenReturn(alerte);
        doNothing().when(alerteLcbFtRepository).persist(any(AlerteLcbFt.class));

        given()
                .contentType(ContentType.JSON)
                .pathParam("id", ALERTE_ID)
                .body("{\"traitePar\":\"not-a-valid-uuid\",\"commentaire\":\"Traité\"}")
                .when()
                .post(BASE_PATH + "/{id}/traiter")
                .then()
                .statusCode(400);
    }

    private AlerteLcbFt buildAlerte() {
        AlerteLcbFt alerte = new AlerteLcbFt();
        alerte.setId(UUID.fromString(ALERTE_ID));
        alerte.setTypeAlerte("TRANSACTION_SUSPECTE");
        alerte.setDescription("Alerte test");
        alerte.setTraitee(false);
        alerte.setActif(true);
        return alerte;
    }

    private AlerteLcbFt buildAlerteAvecOrganisationEtMembre() {
        Organisation organisation = new Organisation();
        organisation.setId(UUID.randomUUID());
        organisation.setNom("Organisation Test");

        Membre membre = new Membre();
        membre.setId(UUID.randomUUID());
        membre.setPrenom("Jean");
        membre.setNom("Dupont");

        AlerteLcbFt alerte = new AlerteLcbFt();
        alerte.setId(UUID.fromString(ALERTE_ID));
        alerte.setOrganisation(organisation);
        alerte.setMembre(membre);
        alerte.setTypeAlerte("SEUIL_DEPASSE");
        alerte.setDescription("Alerte avec organisation et membre");
        alerte.setTraitee(false);
        alerte.setActif(true);
        alerte.setDateAlerte(LocalDateTime.now());
        alerte.setSeverite("WARNING");
        return alerte;
    }
}
