package dev.lions.unionflow.server.resource;

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

import dev.lions.unionflow.server.service.ExportService;
import io.quarkus.test.InjectMock;
import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.security.TestSecurity;
import io.restassured.http.ContentType;
import java.nio.charset.StandardCharsets;
import java.util.UUID;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

@QuarkusTest
class ExportResourceTest {

    private static final String COTISATION_ID = "00000000-0000-0000-0000-000000000001";

    @InjectMock
    ExportService exportService;

    // -------------------------------------------------------------------------
    // GET /api/export/cotisations/csv
    // -------------------------------------------------------------------------

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    @DisplayName("GET /api/export/cotisations/csv retourne 200")
    void exporterCotisationsCSV_returns200() {
        byte[] csv = "Reference;Membre\n".getBytes(StandardCharsets.UTF_8);
        when(exportService.exporterToutesCotisationsCSV(isNull(), isNull(), isNull()))
                .thenReturn(csv);

        given()
                .when()
                .get("/api/export/cotisations/csv")
                .then()
                .statusCode(200)
                .contentType(containsString("text/csv"));
    }

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    @DisplayName("GET /api/export/cotisations/csv avec filtres retourne 200")
    void exporterCotisationsCSV_avecFiltres_returns200() {
        byte[] csv = "Reference;Membre\n".getBytes(StandardCharsets.UTF_8);
        when(exportService.exporterToutesCotisationsCSV(eq("PAYEE"), eq("ORDINAIRE"), any()))
                .thenReturn(csv);

        given()
                .queryParam("statut", "PAYEE")
                .queryParam("type", "ORDINAIRE")
                .queryParam("associationId", "00000000-0000-0000-0000-000000000002")
                .when()
                .get("/api/export/cotisations/csv")
                .then()
                .statusCode(200)
                .contentType(containsString("text/csv"));
    }

    // -------------------------------------------------------------------------
    // POST /api/export/cotisations/csv
    // -------------------------------------------------------------------------

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    @DisplayName("POST /api/export/cotisations/csv retourne 200 avec fichier CSV")
    void exporterCotisationsSelectionneesCSV_returns200() {
        byte[] csv = "Reference;Membre\n00001;Diallo Mamadou\n".getBytes(StandardCharsets.UTF_8);
        when(exportService.exporterCotisationsCSV(anyList())).thenReturn(csv);

        String body = "[\"" + COTISATION_ID + "\"]";

        given()
                .contentType(ContentType.JSON)
                .body(body)
                .when()
                .post("/api/export/cotisations/csv")
                .then()
                .statusCode(200)
                .contentType(containsString("text/csv"))
                .header("Content-Disposition", containsString("cotisations.csv"));
    }

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    @DisplayName("POST /api/export/cotisations/csv avec liste vide retourne 200")
    void exporterCotisationsSelectionneesCSV_listeVide_returns200() {
        byte[] csv = "Reference;Membre\n".getBytes(StandardCharsets.UTF_8);
        when(exportService.exporterCotisationsCSV(anyList())).thenReturn(csv);

        given()
                .contentType(ContentType.JSON)
                .body("[]")
                .when()
                .post("/api/export/cotisations/csv")
                .then()
                .statusCode(200)
                .contentType(containsString("text/csv"));
    }

    // -------------------------------------------------------------------------
    // GET /api/export/cotisations/{cotisationId}/recu
    // -------------------------------------------------------------------------

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    @DisplayName("GET /api/export/cotisations/{id}/recu retourne 200 avec texte")
    void genererRecu_returns200() {
        byte[] recu = "RECU DE PAIEMENT".getBytes(StandardCharsets.UTF_8);
        when(exportService.genererRecuPaiement(org.mockito.ArgumentMatchers.any(UUID.class))).thenReturn(recu);

        given()
                .pathParam("cotisationId", COTISATION_ID)
                .when()
                .get("/api/export/cotisations/{cotisationId}/recu")
                .then()
                .statusCode(200)
                .contentType(containsString("text/plain"))
                .header("Content-Disposition", containsString("recu-" + COTISATION_ID));
    }

    // -------------------------------------------------------------------------
    // POST /api/export/cotisations/recus
    // -------------------------------------------------------------------------

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    @DisplayName("POST /api/export/cotisations/recus retourne 200 avec reçus groupés")
    void genererRecusGroupes_returns200() {
        byte[] recus = "RECU 1\n\nRECU 2".getBytes(StandardCharsets.UTF_8);
        when(exportService.genererRecusGroupes(anyList())).thenReturn(recus);

        String body = "[\"" + COTISATION_ID + "\"]";

        given()
                .contentType(ContentType.JSON)
                .body(body)
                .when()
                .post("/api/export/cotisations/recus")
                .then()
                .statusCode(200)
                .contentType(containsString("text/plain"))
                .header("Content-Disposition", containsString("recus-groupes.txt"));
    }

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    @DisplayName("POST /api/export/cotisations/recus avec plusieurs IDs retourne 200")
    void genererRecusGroupes_plusieursIds_returns200() {
        byte[] recus = "RECU1\nRECU2\nRECU3".getBytes(StandardCharsets.UTF_8);
        when(exportService.genererRecusGroupes(anyList())).thenReturn(recus);

        String id2 = "00000000-0000-0000-0000-000000000002";
        String body = "[\"" + COTISATION_ID + "\",\"" + id2 + "\"]";

        given()
                .contentType(ContentType.JSON)
                .body(body)
                .when()
                .post("/api/export/cotisations/recus")
                .then()
                .statusCode(200);
    }

    // -------------------------------------------------------------------------
    // GET /api/export/rapport/mensuel
    // -------------------------------------------------------------------------

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    @DisplayName("GET /api/export/rapport/mensuel retourne 200 avec rapport texte")
    void genererRapportMensuel_returns200() {
        byte[] rapport = "RAPPORT MENSUEL 03/2026".getBytes(StandardCharsets.UTF_8);
        when(exportService.genererRapportMensuel(eq(2026), eq(3), isNull())).thenReturn(rapport);

        given()
                .queryParam("annee", 2026)
                .queryParam("mois", 3)
                .when()
                .get("/api/export/rapport/mensuel")
                .then()
                .statusCode(200)
                .contentType(containsString("text/plain"))
                .header("Content-Disposition", containsString("rapport-2026-03.txt"));
    }

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    @DisplayName("GET /api/export/rapport/mensuel avec associationId retourne 200")
    void genererRapportMensuel_avecAssociationId_returns200() {
        byte[] rapport = "RAPPORT MENSUEL".getBytes(StandardCharsets.UTF_8);
        when(exportService.genererRapportMensuel(eq(2025), eq(12), org.mockito.ArgumentMatchers.any(UUID.class))).thenReturn(rapport);

        given()
                .queryParam("annee", 2025)
                .queryParam("mois", 12)
                .queryParam("associationId", "00000000-0000-0000-0000-000000000002")
                .when()
                .get("/api/export/rapport/mensuel")
                .then()
                .statusCode(200)
                .contentType(containsString("text/plain"))
                .header("Content-Disposition", containsString("rapport-2025-12.txt"));
    }

    // -------------------------------------------------------------------------
    // GET /api/export/cotisations/{cotisationId}/recu/pdf
    // -------------------------------------------------------------------------

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    @DisplayName("GET /api/export/cotisations/{id}/recu/pdf retourne 200 avec PDF")
    void genererRecuPDF_returns200() {
        byte[] pdf = "%PDF-1.4 test content".getBytes(StandardCharsets.UTF_8);
        when(exportService.genererRecuPaiementPDF(org.mockito.ArgumentMatchers.any(UUID.class))).thenReturn(pdf);

        given()
                .pathParam("cotisationId", COTISATION_ID)
                .when()
                .get("/api/export/cotisations/{cotisationId}/recu/pdf")
                .then()
                .statusCode(200)
                .contentType(containsString("application/pdf"))
                .header("Content-Disposition", containsString("recu-" + COTISATION_ID + ".pdf"));
    }

    // -------------------------------------------------------------------------
    // GET /api/export/rapport/mensuel/pdf
    // -------------------------------------------------------------------------

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    @DisplayName("GET /api/export/rapport/mensuel/pdf retourne 200 avec PDF")
    void genererRapportMensuelPDF_returns200() {
        byte[] pdf = "%PDF-1.4 rapport content".getBytes(StandardCharsets.UTF_8);
        when(exportService.genererRapportMensuelPDF(eq(2026), eq(3), isNull())).thenReturn(pdf);

        given()
                .queryParam("annee", 2026)
                .queryParam("mois", 3)
                .when()
                .get("/api/export/rapport/mensuel/pdf")
                .then()
                .statusCode(200)
                .contentType(containsString("application/pdf"))
                .header("Content-Disposition", containsString("rapport-2026-03.pdf"));
    }

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

    @Test
    @DisplayName("GET /api/export/cotisations/csv sans authentification retourne 401")
    void exporterCotisationsCSV_sansAuthentification_returns401() {
        given()
                .when()
                .get("/api/export/cotisations/csv")
                .then()
                .statusCode(401);
    }

    @Test
    @DisplayName("GET /api/export/rapport/mensuel sans authentification retourne 401")
    void genererRapportMensuel_sansAuthentification_returns401() {
        given()
                .queryParam("annee", 2026)
                .queryParam("mois", 3)
                .when()
                .get("/api/export/rapport/mensuel")
                .then()
                .statusCode(401);
    }

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    @DisplayName("GET /api/export/cotisations/{id}/recu/pdf quand service lève exception retourne 500")
    void genererRecuPDF_serviceException_returns500() {
        when(exportService.genererRecuPaiementPDF(org.mockito.ArgumentMatchers.any(UUID.class)))
                .thenThrow(new RuntimeException("Cotisation introuvable"));

        given()
                .pathParam("cotisationId", COTISATION_ID)
                .when()
                .get("/api/export/cotisations/{cotisationId}/recu/pdf")
                .then()
                .statusCode(500);
    }

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
    @DisplayName("GET /api/export/rapport/mensuel/pdf avec associationId retourne 200")
    void genererRapportMensuelPDF_avecAssociationId_returns200() {
        byte[] pdf = "%PDF-1.4 rapport content".getBytes(StandardCharsets.UTF_8);
        when(exportService.genererRapportMensuelPDF(eq(2025), eq(6), org.mockito.ArgumentMatchers.any(UUID.class))).thenReturn(pdf);

        given()
                .queryParam("annee", 2025)
                .queryParam("mois", 6)
                .queryParam("associationId", "00000000-0000-0000-0000-000000000002")
                .when()
                .get("/api/export/rapport/mensuel/pdf")
                .then()
                .statusCode(200)
                .contentType(containsString("application/pdf"))
                .header("Content-Disposition", containsString("rapport-2025-06.pdf"));
    }
}
