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 dev.lions.unionflow.server.service.ApprovalService;
import dev.lions.unionflow.server.service.BudgetService;
import io.quarkus.test.InjectMock;
import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.security.TestSecurity;
import io.restassured.http.ContentType;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

/**
 * Tests d'intégration REST pour FinanceWorkflowResource.
 *
 * <p>La resource ne délègue pas directement à ces services pour les endpoints testés ici
 * (stats/audit-logs sont implémentés en dur), mais les mocks sont déclarés pour éviter
 * les tentatives d'injection vers la base de données.
 *
 * <p>Les branches catch (lignes 58-62, 84-88, 106-110, 136-140) ne peuvent pas être
 * déclenchées depuis l'extérieur (le code interne ne lève pas d'exception), donc
 * seuls les cas nominaux sont testés ici. La couverture de l'ErrorResponse record
 * (ligne 144) est couverte par les chemins nominaux qui la référencent.
 *
 * @author UnionFlow Team
 * @version 1.0
 * @since 2026-03-20
 */
@QuarkusTest
class FinanceWorkflowResourceTest {

    private static final String BASE_PATH = "/api/finance";

    @InjectMock
    ApprovalService approvalService;

    @InjectMock
    BudgetService budgetService;

    // -------------------------------------------------------------------------
    // GET /api/finance/stats
    // -------------------------------------------------------------------------

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN_ORGANISATION"})
    @DisplayName("GET /api/finance/stats retourne 200 avec les statistiques")
    void getStats_returns200() {
        given()
                .when()
                .get(BASE_PATH + "/stats")
                .then()
                .statusCode(200)
                .contentType(ContentType.JSON)
                .body("totalApprovals", notNullValue())
                .body("pendingApprovals", notNullValue())
                .body("totalBudgets", notNullValue());
    }

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN_ORGANISATION"})
    @DisplayName("GET /api/finance/stats avec paramètres retourne 200")
    void getStats_withParams_returns200() {
        given()
                .queryParam("organizationId", "00000000-0000-0000-0000-000000000001")
                .queryParam("startDate", "2026-01-01T00:00:00")
                .queryParam("endDate", "2026-03-31T23:59:59")
                .when()
                .get(BASE_PATH + "/stats")
                .then()
                .statusCode(200)
                .contentType(ContentType.JSON)
                .body("period", notNullValue())
                .body("averageApprovalTime", notNullValue());
    }

    @Test
    @TestSecurity(user = "super@test.com", roles = {"SUPER_ADMIN"})
    @DisplayName("GET /api/finance/stats avec SUPER_ADMIN retourne 200")
    void getStats_superAdminRole_returns200() {
        given()
                .when()
                .get(BASE_PATH + "/stats")
                .then()
                .statusCode(200);
    }

    // -------------------------------------------------------------------------
    // GET /api/finance/audit-logs
    // -------------------------------------------------------------------------

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN_ORGANISATION"})
    @DisplayName("GET /api/finance/audit-logs retourne 200 avec liste vide")
    void getAuditLogs_returns200() {
        given()
                .when()
                .get(BASE_PATH + "/audit-logs")
                .then()
                .statusCode(200)
                .contentType(ContentType.JSON)
                .body("$", notNullValue());
    }

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN_ORGANISATION"})
    @DisplayName("GET /api/finance/audit-logs avec tous les filtres retourne 200")
    void getAuditLogs_withAllFilters_returns200() {
        given()
                .queryParam("organizationId", "00000000-0000-0000-0000-000000000001")
                .queryParam("startDate", "2026-01-01")
                .queryParam("endDate", "2026-03-31")
                .queryParam("operation", "CREATE")
                .queryParam("entityType", "Cotisation")
                .queryParam("severity", "HIGH")
                .queryParam("limit", 50)
                .when()
                .get(BASE_PATH + "/audit-logs")
                .then()
                .statusCode(200)
                .contentType(ContentType.JSON);
    }

    // -------------------------------------------------------------------------
    // GET /api/finance/audit-logs/anomalies
    // -------------------------------------------------------------------------

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN_ORGANISATION"})
    @DisplayName("GET /api/finance/audit-logs/anomalies retourne 200 avec liste vide")
    void getAuditLogAnomalies_returns200() {
        given()
                .when()
                .get(BASE_PATH + "/audit-logs/anomalies")
                .then()
                .statusCode(200)
                .contentType(ContentType.JSON)
                .body("$", notNullValue());
    }

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN_ORGANISATION"})
    @DisplayName("GET /api/finance/audit-logs/anomalies avec paramètres retourne 200")
    void getAuditLogAnomalies_withParams_returns200() {
        given()
                .queryParam("organizationId", "00000000-0000-0000-0000-000000000001")
                .queryParam("startDate", "2026-01-01")
                .queryParam("endDate", "2026-03-31")
                .when()
                .get(BASE_PATH + "/audit-logs/anomalies")
                .then()
                .statusCode(200)
                .contentType(ContentType.JSON);
    }

    // -------------------------------------------------------------------------
    // POST /api/finance/audit-logs/export
    // -------------------------------------------------------------------------

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN_ORGANISATION"})
    @DisplayName("POST /api/finance/audit-logs/export avec format csv retourne 200")
    void exportAuditLogs_csv_returns200() {
        given()
                .contentType(ContentType.JSON)
                .body("{\"organizationId\":\"00000000-0000-0000-0000-000000000001\",\"format\":\"csv\"}")
                .when()
                .post(BASE_PATH + "/audit-logs/export")
                .then()
                .statusCode(200)
                .contentType(ContentType.JSON)
                .body("exportUrl", notNullValue())
                .body("status", equalTo("generated"))
                .body("expiresAt", notNullValue())
                .body("format", equalTo("csv"));
    }

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN_ORGANISATION"})
    @DisplayName("POST /api/finance/audit-logs/export avec format pdf retourne 200")
    void exportAuditLogs_pdf_returns200() {
        given()
                .contentType(ContentType.JSON)
                .body("{\"organizationId\":\"00000000-0000-0000-0000-000000000001\",\"format\":\"pdf\"}")
                .when()
                .post(BASE_PATH + "/audit-logs/export")
                .then()
                .statusCode(200)
                .contentType(ContentType.JSON)
                .body("format", equalTo("pdf"))
                .body("exportUrl", notNullValue());
    }

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN_ORGANISATION"})
    @DisplayName("POST /api/finance/audit-logs/export sans format utilise csv par défaut")
    void exportAuditLogs_noFormat_defaultsCsv() {
        given()
                .contentType(ContentType.JSON)
                .body("{\"organizationId\":\"00000000-0000-0000-0000-000000000001\"}")
                .when()
                .post(BASE_PATH + "/audit-logs/export")
                .then()
                .statusCode(200)
                .contentType(ContentType.JSON)
                .body("format", equalTo("csv"));
    }

    // -------------------------------------------------------------------------
    // Error cases
    // -------------------------------------------------------------------------

    @Test
    @DisplayName("GET /api/finance/stats sans authentification retourne 401")
    void getStats_sansAuthentification_returns401() {
        given()
                .when()
                .get(BASE_PATH + "/stats")
                .then()
                .statusCode(401);
    }

    @Test
    @DisplayName("GET /api/finance/audit-logs sans authentification retourne 401")
    void getAuditLogs_sansAuthentification_returns401() {
        given()
                .when()
                .get(BASE_PATH + "/audit-logs")
                .then()
                .statusCode(401);
    }

    @Test
    @TestSecurity(user = "membre@test.com", roles = {"MEMBRE"})
    @DisplayName("GET /api/finance/stats avec rôle MEMBRE retourne 403")
    void getStats_avecRoleMembre_returns403() {
        given()
                .when()
                .get(BASE_PATH + "/stats")
                .then()
                .statusCode(403);
    }

    @Test
    @TestSecurity(user = "membre@test.com", roles = {"MEMBRE"})
    @DisplayName("POST /api/finance/audit-logs/export avec rôle MEMBRE retourne 403")
    void exportAuditLogs_avecRoleMembre_returns403() {
        given()
                .contentType(ContentType.JSON)
                .body("{\"organizationId\":\"00000000-0000-0000-0000-000000000001\"}")
                .when()
                .post(BASE_PATH + "/audit-logs/export")
                .then()
                .statusCode(403);
    }

}
