package dev.lions.unionflow.server.resource.mutuelle.epargne;

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

import dev.lions.unionflow.server.api.enums.mutuelle.epargne.StatutCompteEpargne;
import dev.lions.unionflow.server.api.enums.mutuelle.epargne.TypeCompteEpargne;
import dev.lions.unionflow.server.entity.Membre;
import dev.lions.unionflow.server.entity.Organisation;
import dev.lions.unionflow.server.entity.mutuelle.epargne.CompteEpargne;
import dev.lions.unionflow.server.repository.MembreRepository;
import dev.lions.unionflow.server.repository.OrganisationRepository;
import dev.lions.unionflow.server.repository.mutuelle.epargne.CompteEpargneRepository;
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.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.UUID;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

@QuarkusTest
class CompteEpargneResourceTest {

    @Inject
    MembreRepository membreRepository;

    @Inject
    OrganisationRepository organisationRepository;

    @Inject
    CompteEpargneRepository compteEpargneRepository;

    private Membre testMembre;
    private Organisation testOrganisation;
    private CompteEpargne testCompte;

    @BeforeEach
    @Transactional
    void setupTestData() {
        testOrganisation = new Organisation();
        testOrganisation.setNom("Mutuelle Test " + UUID.randomUUID().toString().substring(0, 6));
        testOrganisation.setTypeOrganisation("MUTUELLE");
        testOrganisation.setStatut("ACTIVE");
        testOrganisation.setEmail("mutuelle-res-" + UUID.randomUUID() + "@test.com");
        testOrganisation.setActif(true);
        testOrganisation.setDateCreation(LocalDateTime.now());
        organisationRepository.persist(testOrganisation);

        testMembre = Membre.builder()
                .numeroMembre("MBR-EPS-" + UUID.randomUUID().toString().substring(0, 6))
                .prenom("Marie")
                .nom("Epargne")
                .email("epargne-res-" + UUID.randomUUID() + "@test.com")
                .dateNaissance(LocalDate.of(1990, 3, 20))
                .build();
        testMembre.setActif(true);
        testMembre.setDateCreation(LocalDateTime.now());
        membreRepository.persist(testMembre);

        testCompte = CompteEpargne.builder()
                .membre(testMembre)
                .organisation(testOrganisation)
                .numeroCompte("CPT-" + UUID.randomUUID().toString().substring(0, 8))
                .typeCompte(TypeCompteEpargne.EPARGNE_LIBRE)
                .soldeActuel(BigDecimal.ZERO)
                .statut(StatutCompteEpargne.ACTIF)
                .dateOuverture(LocalDate.now())
                .build();
        testCompte.setActif(true);
        testCompte.setDateCreation(LocalDateTime.now());
        compteEpargneRepository.persist(testCompte);
    }

    @AfterEach
    @Transactional
    void cleanupTestData() {
        if (testCompte != null && testCompte.getId() != null) {
            compteEpargneRepository.deleteById(testCompte.getId());
        }
        if (testMembre != null && testMembre.getId() != null) {
            membreRepository.deleteById(testMembre.getId());
        }
        if (testOrganisation != null && testOrganisation.getId() != null) {
            organisationRepository.deleteById(testOrganisation.getId());
        }
    }

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = { "ADMIN", "ADMIN_ORGANISATION", "MUTUELLE_RESP" })
    @DisplayName("GET /api/v1/epargne/comptes/{id} inexistant retourne 404")
    void getCompteById_inexistant_returns404() {
        given()
                .pathParam("id", UUID.randomUUID())
                .when()
                .get("/api/v1/epargne/comptes/{id}")
                .then()
                .statusCode(404);
    }

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = { "ADMIN", "ADMIN_ORGANISATION", "MUTUELLE_RESP", "MEMBRE" })
    @DisplayName("GET /api/v1/epargne/comptes/{id} existant retourne 200")
    void getCompteById_existant_returns200() {
        given()
                .pathParam("id", testCompte.getId())
                .when()
                .get("/api/v1/epargne/comptes/{id}")
                .then()
                .statusCode(200)
                .body("id", notNullValue());
    }

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = { "ADMIN", "ADMIN_ORGANISATION", "MUTUELLE_RESP" })
    @DisplayName("GET /api/v1/epargne/comptes/membre/{id} retourne 200")
    void getComptesByMembre_returns200() {
        given()
                .pathParam("membreId", UUID.randomUUID())
                .when()
                .get("/api/v1/epargne/comptes/membre/{membreId}")
                .then()
                .statusCode(200)
                .body("$", notNullValue());
    }

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = { "ADMIN", "ADMIN_ORGANISATION", "MUTUELLE_RESP" })
    @DisplayName("GET /api/v1/epargne/comptes/organisation/{id} retourne 200")
    void getComptesByOrganisation_returns200() {
        given()
                .pathParam("organisationId", UUID.randomUUID())
                .when()
                .get("/api/v1/epargne/comptes/organisation/{organisationId}")
                .then()
                .statusCode(200)
                .body("$", notNullValue());
    }

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = { "ADMIN", "ADMIN_ORGANISATION", "MUTUELLE_RESP" })
    @DisplayName("POST /api/v1/epargne/comptes avec membre et organisation valides retourne 201")
    void creerCompte_valid_returns201() {
        String body = "{"
                + "\"membreId\": \"" + testMembre.getId() + "\","
                + "\"organisationId\": \"" + testOrganisation.getId() + "\","
                + "\"typeCompte\": \"EPARGNE_LIBRE\""
                + "}";
        String compteId = given()
                .contentType(ContentType.JSON)
                .body(body)
                .when()
                .post("/api/v1/epargne/comptes")
                .then()
                .statusCode(201)
                .body("id", notNullValue())
                .extract().path("id");

        if (compteId != null) {
            try {
                cleanupCompte(UUID.fromString(compteId));
            } catch (Exception e) {
                // best effort
            }
        }
    }

    @Transactional
    void cleanupCompte(UUID id) {
        compteEpargneRepository.deleteById(id);
    }

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = { "ADMIN", "ADMIN_ORGANISATION", "MUTUELLE_RESP" })
    @DisplayName("POST /api/v1/epargne/comptes avec corps vide retourne 4xx")
    void creerCompte_corpsVide_returns4xx() {
        given()
                .contentType(ContentType.JSON)
                .body("{}")
                .when()
                .post("/api/v1/epargne/comptes")
                .then()
                .statusCode(anyOf(equalTo(400), equalTo(500)));
    }

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = { "ADMIN", "ADMIN_ORGANISATION", "MUTUELLE_RESP" })
    @DisplayName("PATCH /api/v1/epargne/comptes/{id}/statut sans statut retourne 400")
    void changerStatut_sansStatut_returns400() {
        given()
                .pathParam("id", UUID.randomUUID())
                .when()
                .patch("/api/v1/epargne/comptes/{id}/statut")
                .then()
                .statusCode(400);
    }

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = { "ADMIN", "ADMIN_ORGANISATION", "MUTUELLE_RESP" })
    @DisplayName("PATCH /api/v1/epargne/comptes/{id}/statut avec statut valide et ID inexistant retourne 404")
    void changerStatut_avecStatutEtIdInexistant_returns404() {
        given()
                .pathParam("id", UUID.randomUUID())
                .queryParam("statut", "CLOTURE")
                .when()
                .patch("/api/v1/epargne/comptes/{id}/statut")
                .then()
                .statusCode(anyOf(equalTo(404), equalTo(400)));
    }

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = { "ADMIN", "ADMIN_ORGANISATION", "MUTUELLE_RESP", "MEMBRE" })
    @DisplayName("GET /api/v1/epargne/comptes/mes-comptes retourne 200")
    void getMesComptes_returns200() {
        given()
                .when()
                .get("/api/v1/epargne/comptes/mes-comptes")
                .then()
                .statusCode(200)
                .body("$", notNullValue());
    }

    @Test
    @TestSecurity(user = "admin@unionflow.com", roles = { "ADMIN", "ADMIN_ORGANISATION", "MUTUELLE_RESP" })
    @DisplayName("PATCH /api/v1/epargne/comptes/{id}/statut avec statut valide et ID existant retourne 200")
    void changerStatut_avecStatutEtIdExistant_returns200() {
        given()
                .pathParam("id", testCompte.getId())
                .queryParam("statut", "BLOQUE")
                .when()
                .patch("/api/v1/epargne/comptes/{id}/statut")
                .then()
                .statusCode(200)
                .body("id", notNullValue());
    }
}
