package dev.lions.unionflow.server.resource;

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

import dev.lions.unionflow.server.entity.Membre;
import dev.lions.unionflow.server.entity.Organisation;
import dev.lions.unionflow.server.repository.MembreRepository;
import dev.lions.unionflow.server.repository.OrganisationRepository;
import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.security.TestSecurity;
import io.restassured.http.ContentType;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
import java.io.ByteArrayOutputStream;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.junit.jupiter.api.*;

/**
 * Tests d'intégration pour les endpoints import/export de MembreResource
 *
 * @author UnionFlow Team
 * @version 1.0
 * @since 2025-01-19
 */
@QuarkusTest
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class MembreResourceImportExportTest {

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

  @Inject MembreRepository membreRepository;
  @Inject OrganisationRepository organisationRepository;

  private Organisation testOrganisation;
  private List<Membre> testMembres;

  @BeforeEach
  @Transactional
  void setupTestData() {
    // Créer une organisation de test
    testOrganisation =
        Organisation.builder()
            .nom("Organisation Test Import/Export")
            .typeOrganisation("ASSOCIATION")
            .statut("ACTIVE")
            .email("org-import-export-" + System.currentTimeMillis() + "@test.com")
            .build();
    testOrganisation.setDateCreation(LocalDateTime.now());
    testOrganisation.setActif(true);
    organisationRepository.persist(testOrganisation);

    // Créer quelques membres de test
    testMembres = new ArrayList<>();
    for (int i = 1; i <= 3; i++) {
      Membre membre =
          Membre.builder()
              .numeroMembre("UF-TEST-IMPORT-" + i)
              .nom("Nom" + i)
              .prenom("Prenom" + i)
              .email("membre" + i + "-import-" + System.currentTimeMillis() + "@test.com")
              .telephone("+22170123456" + i)
              .dateNaissance(LocalDate.of(1990 + i, 1, 1))
              .build();
      membre.setDateCreation(LocalDateTime.now());
      membre.setActif(true);
      membreRepository.persist(membre);
      testMembres.add(membre);
    }
  }

  @AfterEach
  @Transactional
  void cleanupTestData() {
    if (testMembres != null) {
      testMembres.forEach(
          membre -> {
            if (membre.getId() != null) {
              Membre m = membreRepository.findById(membre.getId());
              if (m != null) {
                membreRepository.delete(m);
              }
            }
          });
    }

    if (testOrganisation != null && testOrganisation.getId() != null) {
      Organisation org = organisationRepository.findById(testOrganisation.getId());
      if (org != null) {
        organisationRepository.delete(org);
      }
    }
  }

  @Test
  @Order(1)
  @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
  @DisplayName("GET /api/membres/import/modele doit retourner un fichier Excel template")
  void testTelechargerModeleImport() {
    given()
        .when()
        .get(BASE_PATH + "/import/modele")
        .then()
        .statusCode(200)
        .contentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
        .header("Content-Disposition", containsString("attachment"))
        .header("Content-Disposition", containsString("modele_import_membres"));
  }

  @Test
  @Order(2)
  @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
  @DisplayName("POST /api/membres/import doit importer des membres depuis Excel")
  void testImporterMembresExcel() throws Exception {
    // Créer un fichier Excel de test
    byte[] excelFile = createTestExcelFile();

    given()
        .contentType("multipart/form-data")
        .multiPart("file", "test_import.xlsx", excelFile, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
        .formParam("organisationId", testOrganisation.getId().toString())
        .formParam("typeMembreDefaut", "ACTIVE")
        .formParam("mettreAJourExistants", "false")
        .formParam("ignorerErreurs", "false")
        .when()
        .post(BASE_PATH + "/import")
        .then()
        .statusCode(200)
        .contentType(ContentType.JSON)
        .body("lignesTraitees", greaterThan(0))
        .body("erreurs", notNullValue());
  }

  @Test
  @Order(3)
  @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
  @DisplayName("POST /api/membres/import doit retourner 400 pour fichier vide")
  void testImporterMembresFichierVide() {
    // Envoyer un fichier vide (tableau de bytes vide)
    byte[] emptyFile = new byte[0];
    
    given()
        .contentType("multipart/form-data")
        .multiPart("file", "empty.xlsx", emptyFile, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
        .formParam("organisationId", testOrganisation.getId().toString())
        .when()
        .post(BASE_PATH + "/import")
        .then()
        .statusCode(400);
  }

  @Test
  @Order(4)
  @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
  @DisplayName("GET /api/membres/export doit exporter des membres en Excel")
  void testExporterMembresExcel() {
    given()
        .queryParam("format", "EXCEL")
        .queryParam("statut", "ACTIVE")
        .when()
        .get(BASE_PATH + "/export")
        .then()
        .statusCode(200)
        .contentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
        .header("Content-Disposition", containsString("attachment"));
  }

  @Test
  @Order(5)
  @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
  @DisplayName("GET /api/membres/export doit exporter en CSV")
  void testExporterMembresCSV() {
    given()
        .queryParam("format", "CSV")
        .queryParam("statut", "ACTIVE")
        .when()
        .get(BASE_PATH + "/export")
        .then()
        .statusCode(200)
        .contentType("text/csv")
        .header("Content-Disposition", containsString("attachment"));
  }

  @Test
  @Order(6)
  @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
  @DisplayName("GET /api/membres/export/count doit retourner le nombre de membres à exporter")
  void testCompterMembresPourExport() {
    given()
        .queryParam("statut", "ACTIVE")
        .when()
        .get(BASE_PATH + "/export/count")
        .then()
        .statusCode(200)
        .contentType(ContentType.JSON)
        .body("count", greaterThanOrEqualTo(0));
  }

  @Test
  @Order(7)
  @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
  @DisplayName("POST /api/membres/export/selection doit exporter une sélection de membres")
  void testExporterSelectionMembres() {
    List<UUID> membreIds = new ArrayList<>();
    testMembres.forEach(m -> membreIds.add(m.getId()));

    given()
        .contentType(ContentType.JSON)
        .body(membreIds)
        .queryParam("format", "EXCEL")
        .when()
        .post(BASE_PATH + "/export/selection")
        .then()
        .statusCode(200)
        .contentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
        .header("Content-Disposition", containsString("attachment"));
  }

  @Test
  @Order(8)
  @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
  @DisplayName("GET /api/membres/export doit supporter inclureStatistiques")
  void testExporterAvecStatistiques() {
    given()
        .queryParam("format", "EXCEL")
        .queryParam("inclureStatistiques", "true")
        .queryParam("statut", "ACTIVE")
        .when()
        .get(BASE_PATH + "/export")
        .then()
        .statusCode(200)
        .contentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
  }

  @Test
  @Order(9)
  @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
  @DisplayName("GET /api/membres/export doit supporter le chiffrement")
  void testExporterAvecChiffrement() {
    given()
        .queryParam("format", "EXCEL")
        .queryParam("motDePasse", "testPassword123")
        .queryParam("statut", "ACTIVE")
        .when()
        .get(BASE_PATH + "/export")
        .then()
        .statusCode(200)
        .contentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
  }

  // -----------------------------------------------------------------------
  // exporterMembres() — branche PDF manquante
  // -----------------------------------------------------------------------

  @Test
  @Order(10)
  @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
  @DisplayName("GET /api/membres/export avec format=PDF doit exporter en PDF (branche PDF)")
  void testExporterMembresPDF() {
    given()
        .queryParam("format", "PDF")
        .queryParam("statut", "ACTIVE")
        .when()
        .get(BASE_PATH + "/export")
        .then()
        .statusCode(200)
        .contentType("application/pdf")
        .header("Content-Disposition", containsString("attachment"));
  }

  // -----------------------------------------------------------------------
  // importerMembres() — branche onlyOrgAdmin=true, organisationId absent → 400
  // -----------------------------------------------------------------------

  @Test
  @Order(11)
  @TestSecurity(user = "orgadmin@unionflow.com", roles = {"ADMIN_ORGANISATION"})
  @DisplayName("POST /api/membres/import avec ADMIN_ORGANISATION sans organisationId → 400")
  void testImporterMembres_adminOrg_sansOrganisationId_returns400() throws Exception {
    byte[] excelFile = createTestExcelFile();

    given()
        .contentType("multipart/form-data")
        .multiPart("file", "test_import.xlsx", excelFile,
            "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
        // Pas de organisationId fourni
        .formParam("typeMembreDefaut", "ACTIF")
        .formParam("mettreAJourExistants", "false")
        .formParam("ignorerErreurs", "true")
        .when()
        .post(BASE_PATH + "/import")
        .then()
        .statusCode(400);
  }

  // -----------------------------------------------------------------------
  // importerMembres() — branche onlyOrgAdmin=true, organisationId fourni + email valide
  // L'utilisateur a accès à l'organisation → traitement normal ou 403 si pas d'accès
  // -----------------------------------------------------------------------

  @Test
  @Order(12)
  @TestSecurity(user = "orgadmin@unionflow.com", roles = {"ADMIN_ORGANISATION"})
  @DisplayName("POST /api/membres/import avec ADMIN_ORGANISATION + organisationId non accessible → 403")
  void testImporterMembres_adminOrg_orgNonAccessible_returns403() throws Exception {
    byte[] excelFile = createTestExcelFile();
    // UUID d'une organisation que orgadmin@unionflow.com n'administre pas
    UUID randomOrgId = UUID.randomUUID();

    given()
        .contentType("multipart/form-data")
        .multiPart("file", "test_import.xlsx", excelFile,
            "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
        .formParam("organisationId", randomOrgId.toString())
        .formParam("typeMembreDefaut", "ACTIF")
        .formParam("mettreAJourExistants", "false")
        .formParam("ignorerErreurs", "true")
        .when()
        .post(BASE_PATH + "/import")
        .then()
        // 403 car orgadmin@unionflow.com n'a pas accès à cet UUID d'organisation
        .statusCode(403);
  }

  // -----------------------------------------------------------------------
  // importerMembres() — branche onlyOrgAdmin=false (ADMIN importe sans restriction)
  // -----------------------------------------------------------------------

  @Test
  @Order(13)
  @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
  @DisplayName("POST /api/membres/import avec ADMIN (onlyOrgAdmin=false) → 200 (chemin sans restriction)")
  void testImporterMembres_adminRole_noRestriction_returns200() throws Exception {
    byte[] excelFile = createTestExcelFile();

    given()
        .contentType("multipart/form-data")
        .multiPart("file", "test_import.xlsx", excelFile,
            "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
        .formParam("organisationId", testOrganisation.getId().toString())
        .formParam("typeMembreDefaut", "ACTIF")
        .formParam("mettreAJourExistants", "false")
        .formParam("ignorerErreurs", "true")
        .when()
        .post(BASE_PATH + "/import")
        .then()
        .statusCode(200);
  }

  // -----------------------------------------------------------------------
  // importerMembres() — L544 : typeMembreDefaut null/vide → remplacé par "ACTIF"
  // -----------------------------------------------------------------------

  @Test
  @Order(14)
  @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
  @DisplayName("POST /api/membres/import sans typeMembreDefaut → L544 branche true, défaut 'ACTIF' appliqué")
  void testImporterMembres_sansTypeMembreDefaut_defautActif() throws Exception {
    byte[] excelFile = createTestExcelFile();

    given()
        .contentType("multipart/form-data")
        .multiPart("file", "test_import.xlsx", excelFile,
            "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
        // Pas de typeMembreDefaut → null → L544 branche true → "ACTIF"
        .formParam("mettreAJourExistants", "false")
        .formParam("ignorerErreurs", "true")
        .when()
        .post(BASE_PATH + "/import")
        .then()
        .statusCode(200);
  }

  @Test
  @Order(15)
  @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
  @DisplayName("POST /api/membres/import avec typeMembreDefaut vide → L544 isEmpty() branche, défaut 'ACTIF'")
  void testImporterMembres_typeMembreDefautVide_defautActif() throws Exception {
    byte[] excelFile = createTestExcelFile();

    given()
        .contentType("multipart/form-data")
        .multiPart("file", "test_import.xlsx", excelFile,
            "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
        .formParam("typeMembreDefaut", "") // vide → isEmpty() true → L544
        .formParam("mettreAJourExistants", "false")
        .formParam("ignorerErreurs", "true")
        .when()
        .post(BASE_PATH + "/import")
        .then()
        .statusCode(200);
  }

  // -----------------------------------------------------------------------
  // importerMembres() — L548 : organisationIdStr null → organisationId = null
  // -----------------------------------------------------------------------

  @Test
  @Order(16)
  @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
  @DisplayName("POST /api/membres/import sans organisationId → L548 branche false (null), organisationId=null")
  void testImporterMembres_sansOrganisationId_organisationIdNull() throws Exception {
    byte[] excelFile = createTestExcelFile();

    given()
        .contentType("multipart/form-data")
        .multiPart("file", "test_import.xlsx", excelFile,
            "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
        // Pas d'organisationId → null → L548 branche false → organisationId = null
        .formParam("typeMembreDefaut", "ACTIF")
        .formParam("mettreAJourExistants", "false")
        .formParam("ignorerErreurs", "true")
        .when()
        .post(BASE_PATH + "/import")
        .then()
        .statusCode(200);
  }

  // -----------------------------------------------------------------------
  // exporterMembres() — L645 : inclureStatistiques=true avec format non-EXCEL dans else
  // -----------------------------------------------------------------------

  @Test
  @Order(17)
  @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
  @DisplayName("GET /api/membres/export avec inclureStatistiques=false → L645 branche false (stats=false)")
  void testExporterMembres_inclureStatistiquesFalse_statsParamFalse() {
    // inclureStatistiques=false → stats = false && ... = false → branche false de AND court-circuit
    given()
        .queryParam("format", "EXCEL")
        .queryParam("inclureStatistiques", "false")
        .when()
        .get(BASE_PATH + "/export")
        .then()
        .statusCode(200)
        .contentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
  }

  @Test
  @Order(18)
  @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"})
  @DisplayName("GET /api/membres/export format=XLS + inclureStatistiques=true → L645 stats=false (format!=EXCEL)")
  void testExporterMembres_formatXlsAvecStats_statsFalse() {
    // Format "XLS" tombe dans else (non CSV, non PDF) mais "EXCEL".equalsIgnoreCase("XLS")=false
    // → stats = true && false = false → branche partielle couverte
    given()
        .queryParam("format", "XLS")
        .queryParam("inclureStatistiques", "true")
        .when()
        .get(BASE_PATH + "/export")
        .then()
        .statusCode(200)
        .contentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
  }

  /**
   * Crée un fichier Excel de test avec des données de membres valides
   */
  private byte[] createTestExcelFile() throws Exception {
    try (Workbook workbook = new XSSFWorkbook();
        ByteArrayOutputStream out = new ByteArrayOutputStream()) {

      Sheet sheet = workbook.createSheet("Membres");

      // En-têtes
      Row headerRow = sheet.createRow(0);
      String[] headers = {
        "nom", "prenom", "email", "telephone", "dateNaissance", "dateAdhesion"
      };
      for (int i = 0; i < headers.length; i++) {
        Cell cell = headerRow.createCell(i, org.apache.poi.ss.usermodel.CellType.STRING);
        cell.setCellValue(headers[i]);
      }

      // Données de test
      Row dataRow = sheet.createRow(1);
      Cell nomCell = dataRow.createCell(0, org.apache.poi.ss.usermodel.CellType.STRING);
      nomCell.setCellValue("TestNom");
      Cell prenomCell = dataRow.createCell(1, org.apache.poi.ss.usermodel.CellType.STRING);
      prenomCell.setCellValue("TestPrenom");
      Cell emailCell = dataRow.createCell(2, org.apache.poi.ss.usermodel.CellType.STRING);
      emailCell.setCellValue("test-import-" + System.currentTimeMillis() + "@test.com");
      Cell telephoneCell = dataRow.createCell(3, org.apache.poi.ss.usermodel.CellType.STRING);
      telephoneCell.setCellValue("+221701234999");
      Cell dateNaissanceCell = dataRow.createCell(4, org.apache.poi.ss.usermodel.CellType.STRING);
      dateNaissanceCell.setCellValue("1990-01-01");
      Cell dateAdhesionCell = dataRow.createCell(5, org.apache.poi.ss.usermodel.CellType.STRING);
      dateAdhesionCell.setCellValue("2023-01-01");

      workbook.write(out);
      return out.toByteArray();
    }
  }
}


