package dev.lions.unionflow.server.resource;

import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.when;

import dev.lions.unionflow.server.service.AuditService;
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.Map;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

/**
 * Tests mock pour AuditResource — couvre les blocs catch de chaque méthode.
 *
 * <p>Le service réel ne lève pas d'exception dans les tests (H2 fonctionnel),
 * donc les blocs catch (lignes 47-52, 76-81, 90-95, 105-110) ne sont jamais
 * exécutés sans ce test mock.
 */
@QuarkusTest
@DisplayName("AuditResource (mock — blocs catch)")
class AuditResourceMockTest {

    @InjectMock
    AuditService auditService;

    // =========================================================================
    // listerTous — catch block (ligne 47-52)
    // =========================================================================

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    @DisplayName("GET /api/audit — retourne 500 quand auditService.listerTous() lève une exception")
    void listerTous_serviceThrows_returns500() {
        when(auditService.listerTous(anyInt(), anyInt(), anyString(), anyString()))
                .thenThrow(new RuntimeException("Service indisponible"));

        given()
                .when()
                .get("/api/audit")
                .then()
                .statusCode(500);
    }

    // =========================================================================
    // rechercher — catch block (ligne 76-81)
    // =========================================================================

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    @DisplayName("POST /api/audit/rechercher — retourne 500 quand auditService.rechercher() lève une exception")
    void rechercher_serviceThrows_returns500() {
        when(auditService.rechercher(isNull(), isNull(), isNull(), isNull(), isNull(), isNull(), isNull(), anyInt(), anyInt()))
                .thenThrow(new RuntimeException("Erreur recherche"));

        given()
                .contentType(ContentType.JSON)
                .when()
                .post("/api/audit/rechercher")
                .then()
                .statusCode(500);
    }

    // =========================================================================
    // enregistrerLog — catch block (ligne 90-95)
    // =========================================================================

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    @DisplayName("POST /api/audit — retourne 500 quand auditService.enregistrerLog() lève une exception")
    void enregistrerLog_serviceThrows_returns500() {
        when(auditService.enregistrerLog(any()))
                .thenThrow(new RuntimeException("Erreur enregistrement"));

        given()
                .contentType(ContentType.JSON)
                .body("{\"typeAction\":\"TEST\",\"severite\":\"INFO\",\"utilisateur\":\"admin@test.com\"}")
                .when()
                .post("/api/audit")
                .then()
                .statusCode(500);
    }

    // =========================================================================
    // getStatistiques — catch block (ligne 105-110)
    // =========================================================================

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    @DisplayName("GET /api/audit/statistiques — retourne 500 quand auditService.getStatistiques() lève une exception")
    void getStatistiques_serviceThrows_returns500() {
        when(auditService.getStatistiques())
                .thenThrow(new RuntimeException("Erreur statistiques"));

        given()
                .when()
                .get("/api/audit/statistiques")
                .then()
                .statusCode(500);
    }

    // =========================================================================
    // rechercher:70 — branches dateDebutStr != null et dateFinStr != null (6I, 2B)
    // =========================================================================

    /**
     * Couvre la branche {@code dateDebutStr != null} à la ligne 70 de {@code rechercher}.
     *
     * <p>Quand {@code dateDebutStr} est fourni, il est parsé via {@code LocalDateTime.parse()}.
     * Ce test couvre la branche {@code dateDebutStr != null} true.
     */
    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    @DisplayName("POST /api/audit/rechercher avec dateDebut → couvre branche dateDebutStr != null (L70)")
    void rechercher_avecDateDebut_couvreL70BrancheDateDebutNonNull() {
        Map<String, Object> result = Map.of("data", java.util.List.of(), "total", 0L);
        when(auditService.rechercher(
                any(LocalDateTime.class), isNull(), isNull(), isNull(), isNull(), isNull(), isNull(), anyInt(), anyInt()))
                .thenReturn(result);

        given()
                .contentType(ContentType.JSON)
                .queryParam("dateDebut", "2026-01-01T00:00:00")
                .when()
                .post("/api/audit/rechercher")
                .then()
                .statusCode(200);
    }

    /**
     * Couvre la branche {@code dateFinStr != null} à la ligne 71 de {@code rechercher}.
     *
     * <p>Quand {@code dateFinStr} est fourni, il est parsé via {@code LocalDateTime.parse()}.
     * Ce test couvre la branche {@code dateFinStr != null} true.
     */
    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    @DisplayName("POST /api/audit/rechercher avec dateFin → couvre branche dateFinStr != null (L71)")
    void rechercher_avecDateFin_couvreL71BrancheDateFinNonNull() {
        Map<String, Object> result = Map.of("data", java.util.List.of(), "total", 0L);
        when(auditService.rechercher(
                isNull(), any(LocalDateTime.class), isNull(), isNull(), isNull(), isNull(), isNull(), anyInt(), anyInt()))
                .thenReturn(result);

        given()
                .contentType(ContentType.JSON)
                .queryParam("dateFin", "2026-12-31T23:59:59")
                .when()
                .post("/api/audit/rechercher")
                .then()
                .statusCode(200);
    }

    /**
     * Couvre les deux branches dateDebutStr != null et dateFinStr != null simultanément.
     *
     * <p>Fournit à la fois dateDebut et dateFin pour couvrir les deux branches true
     * des ternaires aux lignes 70-71.
     */
    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    @DisplayName("POST /api/audit/rechercher avec dateDebut ET dateFin → couvre les 2 branches non-null (L70-71)")
    void rechercher_avecDateDebutEtDateFin_couvreLes2Branches() {
        Map<String, Object> result = Map.of("data", java.util.List.of(), "total", 0L);
        when(auditService.rechercher(
                any(LocalDateTime.class), any(LocalDateTime.class),
                isNull(), isNull(), isNull(), isNull(), isNull(), anyInt(), anyInt()))
                .thenReturn(result);

        given()
                .contentType(ContentType.JSON)
                .queryParam("dateDebut", "2026-01-01T00:00:00")
                .queryParam("dateFin", "2026-12-31T23:59:59")
                .when()
                .post("/api/audit/rechercher")
                .then()
                .statusCode(200);
    }

    /**
     * Couvre la branche catch de {@code rechercher} quand le parsing de dateDebut échoue.
     *
     * <p>Un format de date invalide lève une {@code DateTimeParseException} qui est capturée
     * par le bloc catch de la ligne 76-81 → retourne HTTP 500.
     */
    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    @DisplayName("POST /api/audit/rechercher avec date invalide → retourne 500 (catch DateTimeParseException)")
    void rechercher_dateInvalide_catchBlock_returns500() {
        given()
                .contentType(ContentType.JSON)
                .queryParam("dateDebut", "date-invalide-non-parseable")
                .when()
                .post("/api/audit/rechercher")
                .then()
                .statusCode(500);
    }
}
