package dev.lions.unionflow.server.resource;

import static io.restassured.RestAssured.given;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.anyOf;
import static org.hamcrest.Matchers.equalTo;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.when;

import dev.lions.unionflow.server.api.dto.dashboard.DashboardDataResponse;
import dev.lions.unionflow.server.api.dto.dashboard.DashboardStatsResponse;
import dev.lions.unionflow.server.api.dto.dashboard.RecentActivityResponse;
import dev.lions.unionflow.server.api.dto.dashboard.UpcomingEventResponse;
import dev.lions.unionflow.server.api.service.dashboard.DashboardService;
import io.quarkus.test.InjectMock;
import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.security.TestSecurity;
import io.restassured.response.Response;
import java.util.List;
import java.util.UUID;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

/**
 * Tests unitaires pour DashboardResource avec mocks du service.
 *
 * <p>Couvre les branches non testées dans DashboardResourceTest :
 * <ul>
 *   <li>Branches 500 (exception levée par le service)</li>
 *   <li>Paramètre limit personnalisé pour activities et events</li>
 *   <li>Branches POST /refresh avec erreur</li>
 *   <li>Accès sans authentification (401)</li>
 * </ul>
 *
 * @author UnionFlow Team
 * @version 1.0
 * @since 2026-03-21
 */
@QuarkusTest
@DisplayName("DashboardResource — Tests avec mock du service")
class DashboardResourceMockTest {

    @InjectMock
    DashboardService dashboardService;

    private static final String VALID_ORG_ID  = UUID.randomUUID().toString();
    private static final String VALID_USER_ID = UUID.randomUUID().toString();

    // =========================================================================
    // GET /api/v1/dashboard/data — branche erreur serveur (500)
    // =========================================================================

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    @DisplayName("GET /data — exception du service retourne 500 avec message d'erreur JSON")
    void getDashboardData_serviceThrowsException_returns500WithErrorJson() {
        when(dashboardService.getDashboardData(anyString(), anyString()))
                .thenThrow(new RuntimeException("Erreur base de données simulée"));

        Response response = given()
                .queryParam("organizationId", VALID_ORG_ID)
                .queryParam("userId", VALID_USER_ID)
                .when()
                .get("/api/v1/dashboard/data")
                .then()
                .statusCode(500)
                .contentType("application/json")
                .extract()
                .response();

        assertThat(response.jsonPath().getString("error")).isNotNull();
    }

    // =========================================================================
    // GET /api/v1/dashboard/data — cas nominal mocké (200)
    // =========================================================================

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    @DisplayName("GET /data — service retourne données valides → 200 avec organizationId et stats")
    void getDashboardData_serviceReturnsData_returns200() {
        DashboardStatsResponse stats = DashboardStatsResponse.builder()
                .totalMembers(10)
                .activeMembers(8)
                .build();

        DashboardDataResponse data = DashboardDataResponse.builder()
                .organizationId(VALID_ORG_ID)
                .userId(VALID_USER_ID)
                .stats(stats)
                .recentActivities(List.of())
                .upcomingEvents(List.of())
                .build();

        when(dashboardService.getDashboardData(anyString(), anyString())).thenReturn(data);

        Response response = given()
                .queryParam("organizationId", VALID_ORG_ID)
                .queryParam("userId", VALID_USER_ID)
                .when()
                .get("/api/v1/dashboard/data")
                .then()
                .statusCode(200)
                .contentType("application/json")
                .extract()
                .response();

        assertThat(response.jsonPath().getString("organizationId")).isEqualTo(VALID_ORG_ID);
        assertThat(response.jsonPath().getInt("stats.totalMembers")).isEqualTo(10);
        assertThat(response.jsonPath().getInt("stats.activeMembers")).isEqualTo(8);
    }

    // =========================================================================
    // GET /api/v1/dashboard/stats — branche erreur serveur (500)
    // =========================================================================

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    @DisplayName("GET /stats — exception du service retourne 500 avec message d'erreur JSON")
    void getDashboardStats_serviceThrowsException_returns500() {
        when(dashboardService.getDashboardStats(anyString(), anyString()))
                .thenThrow(new RuntimeException("Timeout simulé"));

        Response response = given()
                .queryParam("organizationId", VALID_ORG_ID)
                .queryParam("userId", VALID_USER_ID)
                .when()
                .get("/api/v1/dashboard/stats")
                .then()
                .statusCode(500)
                .contentType("application/json")
                .extract()
                .response();

        assertThat(response.jsonPath().getString("error")).isEqualTo("Erreur serveur");
    }

    // =========================================================================
    // GET /api/v1/dashboard/stats — cas nominal mocké (200)
    // =========================================================================

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    @DisplayName("GET /stats — service retourne stats → 200 avec les champs de base")
    void getDashboardStats_serviceReturnsStats_returns200() {
        DashboardStatsResponse stats = DashboardStatsResponse.builder()
                .totalMembers(42)
                .activeMembers(35)
                .totalEvents(5)
                .upcomingEvents(2)
                .build();

        when(dashboardService.getDashboardStats(anyString(), anyString())).thenReturn(stats);

        Response response = given()
                .queryParam("organizationId", VALID_ORG_ID)
                .queryParam("userId", VALID_USER_ID)
                .when()
                .get("/api/v1/dashboard/stats")
                .then()
                .statusCode(200)
                .contentType("application/json")
                .extract()
                .response();

        assertThat(response.jsonPath().getInt("totalMembers")).isEqualTo(42);
        assertThat(response.jsonPath().getInt("activeMembers")).isEqualTo(35);
    }

    // =========================================================================
    // GET /api/v1/dashboard/activities — branche erreur serveur (500)
    // =========================================================================

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    @DisplayName("GET /activities — exception du service retourne 500 avec message d'erreur JSON")
    void getRecentActivities_serviceThrowsException_returns500() {
        when(dashboardService.getRecentActivities(anyString(), anyString(), anyInt()))
                .thenThrow(new RuntimeException("Erreur service simulée"));

        Response response = given()
                .queryParam("organizationId", VALID_ORG_ID)
                .queryParam("userId", VALID_USER_ID)
                .when()
                .get("/api/v1/dashboard/activities")
                .then()
                .statusCode(500)
                .contentType("application/json")
                .extract()
                .response();

        assertThat(response.jsonPath().getString("error")).isEqualTo("Erreur serveur");
    }

    // =========================================================================
    // GET /api/v1/dashboard/activities — limit personnalisé et données mockées
    // =========================================================================

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    @DisplayName("GET /activities — limit=5 transmis au service et wrapper contient limit=5")
    void getRecentActivities_customLimit_wrapperContainsCorrectLimit() {
        RecentActivityResponse activity = RecentActivityResponse.builder()
                .id(UUID.randomUUID().toString())
                .type("ADHESION")
                .description("Nouveau membre ajouté")
                .build();

        when(dashboardService.getRecentActivities(anyString(), anyString(), anyInt()))
                .thenReturn(List.of(activity));

        Response response = given()
                .queryParam("organizationId", VALID_ORG_ID)
                .queryParam("userId", VALID_USER_ID)
                .queryParam("limit", 5)
                .when()
                .get("/api/v1/dashboard/activities")
                .then()
                .statusCode(200)
                .contentType("application/json")
                .extract()
                .response();

        assertThat(response.jsonPath().getInt("limit")).isEqualTo(5);
        assertThat(response.jsonPath().getInt("total")).isEqualTo(1);
        assertThat(response.jsonPath().getList("activities")).hasSize(1);
    }

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    @DisplayName("GET /activities — liste vide retourne 200 avec total=0 et limit=10 par défaut")
    void getRecentActivities_emptyList_returns200WithZeroTotal() {
        when(dashboardService.getRecentActivities(anyString(), anyString(), anyInt()))
                .thenReturn(List.of());

        Response response = given()
                .queryParam("organizationId", VALID_ORG_ID)
                .queryParam("userId", VALID_USER_ID)
                .when()
                .get("/api/v1/dashboard/activities")
                .then()
                .statusCode(200)
                .contentType("application/json")
                .extract()
                .response();

        assertThat(response.jsonPath().getInt("total")).isEqualTo(0);
        assertThat(response.jsonPath().getInt("limit")).isEqualTo(10); // DefaultValue
        assertThat(response.jsonPath().getList("activities")).isEmpty();
    }

    // =========================================================================
    // GET /api/v1/dashboard/events/upcoming — branche erreur serveur (500)
    // =========================================================================

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    @DisplayName("GET /events/upcoming — exception du service retourne 500 avec message d'erreur JSON")
    void getUpcomingEvents_serviceThrowsException_returns500() {
        when(dashboardService.getUpcomingEvents(anyString(), anyString(), anyInt()))
                .thenThrow(new RuntimeException("Erreur service simulée"));

        Response response = given()
                .queryParam("organizationId", VALID_ORG_ID)
                .queryParam("userId", VALID_USER_ID)
                .when()
                .get("/api/v1/dashboard/events/upcoming")
                .then()
                .statusCode(500)
                .contentType("application/json")
                .extract()
                .response();

        assertThat(response.jsonPath().getString("error")).isEqualTo("Erreur serveur");
    }

    // =========================================================================
    // GET /api/v1/dashboard/events/upcoming — limit personnalisé
    // =========================================================================

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    @DisplayName("GET /events/upcoming — limit=3 transmis et wrapper contient limit=3")
    void getUpcomingEvents_customLimit_wrapperContainsCorrectLimit() {
        UpcomingEventResponse event = UpcomingEventResponse.builder()
                .id(UUID.randomUUID().toString())
                .title("Assemblée Générale")
                .build();

        when(dashboardService.getUpcomingEvents(anyString(), anyString(), anyInt()))
                .thenReturn(List.of(event));

        Response response = given()
                .queryParam("organizationId", VALID_ORG_ID)
                .queryParam("userId", VALID_USER_ID)
                .queryParam("limit", 3)
                .when()
                .get("/api/v1/dashboard/events/upcoming")
                .then()
                .statusCode(200)
                .contentType("application/json")
                .extract()
                .response();

        assertThat(response.jsonPath().getInt("limit")).isEqualTo(3);
        assertThat(response.jsonPath().getInt("total")).isEqualTo(1);
        assertThat(response.jsonPath().getList("events")).hasSize(1);
    }

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    @DisplayName("GET /events/upcoming — liste vide retourne 200 avec total=0 et limit=5 par défaut")
    void getUpcomingEvents_emptyList_returns200WithZeroTotal() {
        when(dashboardService.getUpcomingEvents(anyString(), anyString(), anyInt()))
                .thenReturn(List.of());

        Response response = given()
                .queryParam("organizationId", VALID_ORG_ID)
                .queryParam("userId", VALID_USER_ID)
                .when()
                .get("/api/v1/dashboard/events/upcoming")
                .then()
                .statusCode(200)
                .contentType("application/json")
                .extract()
                .response();

        assertThat(response.jsonPath().getInt("total")).isEqualTo(0);
        assertThat(response.jsonPath().getInt("limit")).isEqualTo(5); // DefaultValue
        assertThat(response.jsonPath().getList("events")).isEmpty();
    }

    // =========================================================================
    // GET /api/v1/dashboard/health — accessible sans dépendance service
    // =========================================================================

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    @DisplayName("GET /health — retourne status=UP, service=dashboard, version=1.0.0 et timestamp > 0")
    void getHealth_returnsFullHealthPayload() {
        Response response = given()
                .when()
                .get("/api/v1/dashboard/health")
                .then()
                .statusCode(200)
                .contentType("application/json")
                .extract()
                .response();

        assertThat(response.jsonPath().getString("status")).isEqualTo("UP");
        assertThat(response.jsonPath().getString("service")).isEqualTo("dashboard");
        assertThat(response.jsonPath().getString("version")).isEqualTo("1.0.0");
        assertThat(response.jsonPath().getLong("timestamp")).isGreaterThan(0L);
    }

    // =========================================================================
    // POST /api/v1/dashboard/refresh — branche erreur serveur (500)
    // =========================================================================

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    @DisplayName("POST /refresh — exception du service retourne 500 avec message d'erreur JSON")
    void postRefresh_serviceThrowsException_returns500() {
        when(dashboardService.getDashboardData(anyString(), anyString()))
                .thenThrow(new RuntimeException("Cache service indisponible"));

        Response response = given()
                .contentType("application/json")
                .queryParam("organizationId", VALID_ORG_ID)
                .queryParam("userId", VALID_USER_ID)
                .when()
                .post("/api/v1/dashboard/refresh")
                .then()
                .statusCode(500)
                .contentType("application/json")
                .extract()
                .response();

        assertThat(response.jsonPath().getString("error")).isEqualTo("Erreur serveur");
    }

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    @DisplayName("POST /refresh — succès retourne status=refreshed, timestamp > 0 et data non null")
    void postRefresh_success_returnsRefreshedPayload() {
        DashboardDataResponse data = DashboardDataResponse.builder()
                .organizationId(VALID_ORG_ID)
                .userId(VALID_USER_ID)
                .stats(DashboardStatsResponse.builder().totalMembers(5).activeMembers(4).build())
                .build();

        when(dashboardService.getDashboardData(anyString(), anyString())).thenReturn(data);

        Response response = given()
                .contentType("application/json")
                .queryParam("organizationId", VALID_ORG_ID)
                .queryParam("userId", VALID_USER_ID)
                .when()
                .post("/api/v1/dashboard/refresh")
                .then()
                .statusCode(200)
                .contentType("application/json")
                .extract()
                .response();

        assertThat(response.jsonPath().getString("status")).isEqualTo("refreshed");
        assertThat(response.jsonPath().getLong("timestamp")).isGreaterThan(0L);
        assertThat(response.jsonPath().getMap("data")).isNotNull();
    }

    // =========================================================================
    // Tests de sécurité — sans authentification → 401
    // =========================================================================

    @Test
    @DisplayName("GET /data sans authentification retourne 401")
    void getDashboardData_noAuth_returns401() {
        given()
                .queryParam("organizationId", VALID_ORG_ID)
                .queryParam("userId", VALID_USER_ID)
                .when()
                .get("/api/v1/dashboard/data")
                .then()
                .statusCode(401);
    }

    @Test
    @DisplayName("GET /stats sans authentification retourne 401")
    void getDashboardStats_noAuth_returns401() {
        given()
                .queryParam("organizationId", VALID_ORG_ID)
                .queryParam("userId", VALID_USER_ID)
                .when()
                .get("/api/v1/dashboard/stats")
                .then()
                .statusCode(401);
    }

    @Test
    @DisplayName("GET /activities sans authentification retourne 401")
    void getRecentActivities_noAuth_returns401() {
        given()
                .queryParam("organizationId", VALID_ORG_ID)
                .queryParam("userId", VALID_USER_ID)
                .when()
                .get("/api/v1/dashboard/activities")
                .then()
                .statusCode(401);
    }

    @Test
    @DisplayName("GET /events/upcoming sans authentification retourne 401")
    void getUpcomingEvents_noAuth_returns401() {
        given()
                .queryParam("organizationId", VALID_ORG_ID)
                .queryParam("userId", VALID_USER_ID)
                .when()
                .get("/api/v1/dashboard/events/upcoming")
                .then()
                .statusCode(401);
    }

    @Test
    @DisplayName("GET /health sans authentification retourne 401")
    void getHealth_noAuth_returns401() {
        given()
                .when()
                .get("/api/v1/dashboard/health")
                .then()
                .statusCode(401);
    }

    @Test
    @DisplayName("POST /refresh sans authentification retourne 401")
    void postRefresh_noAuth_returns401() {
        given()
                .contentType("application/json")
                .queryParam("organizationId", VALID_ORG_ID)
                .queryParam("userId", VALID_USER_ID)
                .when()
                .post("/api/v1/dashboard/refresh")
                .then()
                .statusCode(401);
    }

    // =========================================================================
    // GET /api/v1/dashboard/data — paramètres manquants → 400
    // =========================================================================

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    @DisplayName("GET /stats sans paramètres retourne 400")
    void getDashboardStats_missingParams_returns400() {
        given()
                .when()
                .get("/api/v1/dashboard/stats")
                .then()
                .statusCode(400);
    }

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    @DisplayName("GET /activities sans paramètres obligatoires retourne 400")
    void getRecentActivities_missingParams_returns400() {
        given()
                .when()
                .get("/api/v1/dashboard/activities")
                .then()
                .statusCode(400);
    }

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    @DisplayName("GET /events/upcoming sans paramètres obligatoires retourne 400")
    void getUpcomingEvents_missingParams_returns400() {
        given()
                .when()
                .get("/api/v1/dashboard/events/upcoming")
                .then()
                .statusCode(400);
    }

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    @DisplayName("POST /refresh sans paramètres retourne 400")
    void postRefresh_missingParams_returns400() {
        given()
                .contentType("application/json")
                .when()
                .post("/api/v1/dashboard/refresh")
                .then()
                .statusCode(400);
    }
}
