package dev.lions.unionflow.server.resource;

import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.notNullValue;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;

import dev.lions.unionflow.server.api.dto.reference.request.CreateTypeReferenceRequest;
import org.mockito.ArgumentCaptor;

import dev.lions.unionflow.server.api.dto.reference.response.TypeReferenceResponse;
import dev.lions.unionflow.server.service.TypeReferenceService;
import io.quarkus.security.identity.SecurityIdentity;
import io.quarkus.test.InjectMock;
import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.security.TestSecurity;
import io.restassured.http.ContentType;
import java.util.Collections;
import java.util.UUID;
import org.junit.jupiter.api.Test;

/**
 * Tests d'intégration REST pour TypeOrganisationReferenceResource.
 *
 * @author UnionFlow Team
 * @version 1.0
 * @since 2026-03-20
 */
@QuarkusTest
class TypeOrganisationReferenceResourceTest {

    private static final String BASE_PATH = "/api/references/types-organisation";
    private static final String TYPE_ID = "00000000-0000-0000-0000-000000000020";

    @InjectMock
    TypeReferenceService typeReferenceService;

    @InjectMock
    SecurityIdentity securityIdentity;

    // -------------------------------------------------------------------------
    // GET /api/references/types-organisation
    // -------------------------------------------------------------------------

    @Test
    @TestSecurity(user = "test@test.com", roles = {"USER"})
    void listerTypes_returns200() {
        when(typeReferenceService.listerParDomaine(anyString(), any()))
                .thenReturn(Collections.emptyList());

        given()
                .when()
                .get(BASE_PATH)
                .then()
                .statusCode(200)
                .contentType(ContentType.JSON)
                .body("$", notNullValue());
    }

    // -------------------------------------------------------------------------
    // POST /api/references/types-organisation
    // -------------------------------------------------------------------------

    @Test
    @TestSecurity(user = "superadmin@test.com", roles = {"SUPER_ADMIN"})
    void creerType_success_returns201() {
        TypeReferenceResponse response = TypeReferenceResponse.builder()
                .domaine("TYPE_ORGANISATION")
                .code("ASSOC")
                .libelle("Association")
                .build();
        response.setId(UUID.randomUUID());
        when(typeReferenceService.creer(any())).thenReturn(response);

        String body = """
                {"code":"ASSOC","libelle":"Association","domaine":"TYPE_ORGANISATION"}
                """;

        given()
                .contentType(ContentType.JSON)
                .body(body)
                .when()
                .post(BASE_PATH)
                .then()
                .statusCode(201)
                .contentType(ContentType.JSON);
    }

    @Test
    @TestSecurity(user = "superadmin@test.com", roles = {"SUPER_ADMIN"})
    void creerType_withCategorieAndModules_forwardsFields() {
        TypeReferenceResponse response = TypeReferenceResponse.builder()
                .domaine("TYPE_ORGANISATION")
                .code("TONTINE")
                .libelle("Tontine")
                .categorie("FINANCIER_SOLIDAIRE")
                .modulesRequis("TONTINE,COTISATIONS")
                .build();
        response.setId(UUID.randomUUID());
        ArgumentCaptor<CreateTypeReferenceRequest> captor =
                ArgumentCaptor.forClass(CreateTypeReferenceRequest.class);
        when(typeReferenceService.creer(captor.capture())).thenReturn(response);

        String body = """
                {"code":"TONTINE","libelle":"Tontine","domaine":"TYPE_ORGANISATION",
                 "categorie":"FINANCIER_SOLIDAIRE","modulesRequis":"TONTINE,COTISATIONS"}
                """;

        given()
                .contentType(ContentType.JSON)
                .body(body)
                .when()
                .post(BASE_PATH)
                .then()
                .statusCode(201);

        CreateTypeReferenceRequest captured = captor.getValue();
        assertEquals("FINANCIER_SOLIDAIRE", captured.categorie());
        assertEquals("TONTINE,COTISATIONS", captured.modulesRequis());
        assertEquals("TYPE_ORGANISATION", captured.domaine());
    }

    @Test
    @TestSecurity(user = "superadmin@test.com", roles = {"SUPER_ADMIN"})
    void creerType_illegalArg_returns400() {
        when(typeReferenceService.creer(any()))
                .thenThrow(new IllegalArgumentException("Code déjà existant dans ce domaine"));

        String body = """
                {"code":"ASSOC","libelle":"Association","domaine":"TYPE_ORGANISATION"}
                """;

        given()
                .contentType(ContentType.JSON)
                .body(body)
                .when()
                .post(BASE_PATH)
                .then()
                .statusCode(400)
                .body("error", notNullValue());
    }

    // -------------------------------------------------------------------------
    // PUT /api/references/types-organisation/{id}
    // -------------------------------------------------------------------------

    @Test
    @TestSecurity(user = "superadmin@test.com", roles = {"SUPER_ADMIN"})
    void updateType_success_returns200() {
        TypeReferenceResponse response = TypeReferenceResponse.builder()
                .domaine("TYPE_ORGANISATION")
                .code("ASSOC")
                .libelle("Association modifiée")
                .build();
        response.setId(UUID.fromString(TYPE_ID));
        when(typeReferenceService.modifier(any(UUID.class), any())).thenReturn(response);

        String body = """
                {"libelle":"Association modifiée"}
                """;

        given()
                .contentType(ContentType.JSON)
                .pathParam("id", TYPE_ID)
                .body(body)
                .when()
                .put(BASE_PATH + "/{id}")
                .then()
                .statusCode(200)
                .contentType(ContentType.JSON);
    }

    // -------------------------------------------------------------------------
    // DELETE /api/references/types-organisation/{id}
    // -------------------------------------------------------------------------

    @Test
    @TestSecurity(user = "superadmin@test.com", roles = {"SUPER_ADMIN"})
    void deleteType_superAdmin_returns204() {
        when(securityIdentity.hasRole("SUPER_ADMIN")).thenReturn(true);
        when(securityIdentity.hasRole("SUPER_ADMIN")).thenReturn(false);
        doNothing().when(typeReferenceService).supprimerPourSuperAdmin(any(UUID.class));

        given()
                .pathParam("id", TYPE_ID)
                .when()
                .delete(BASE_PATH + "/{id}")
                .then()
                .statusCode(204);
    }

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN"})
    void deleteType_notSuperAdmin_returns204() {
        when(securityIdentity.hasRole("SUPER_ADMIN")).thenReturn(false);
        when(securityIdentity.hasRole("SUPER_ADMIN")).thenReturn(false);
        doNothing().when(typeReferenceService).supprimer(any(UUID.class));

        given()
                .pathParam("id", TYPE_ID)
                .when()
                .delete(BASE_PATH + "/{id}")
                .then()
                .statusCode(204);
    }

    // -------------------------------------------------------------------------
    // PUT /api/references/types-organisation/{id} — IllegalArgumentException branch
    // -------------------------------------------------------------------------

    @Test
    @TestSecurity(user = "superadmin@test.com", roles = {"SUPER_ADMIN"})
    void updateType_illegalArg_returns400() {
        when(typeReferenceService.modifier(any(UUID.class), any()))
                .thenThrow(new IllegalArgumentException("Type non trouvé"));

        String body = """
                {"libelle":"Association modifiée"}
                """;

        given()
                .contentType(ContentType.JSON)
                .pathParam("id", TYPE_ID)
                .body(body)
                .when()
                .put(BASE_PATH + "/{id}")
                .then()
                .statusCode(400)
                .body("error", notNullValue());
    }

    // -------------------------------------------------------------------------
    // DELETE /api/references/types-organisation/{id} — IllegalArgumentException branch
    // -------------------------------------------------------------------------

    @Test
    @TestSecurity(user = "superadmin@test.com", roles = {"SUPER_ADMIN"})
    void deleteType_illegalArg_returns400() {
        when(securityIdentity.hasRole("SUPER_ADMIN")).thenReturn(true);
        doThrow(new IllegalArgumentException("Type introuvable"))
                .when(typeReferenceService).supprimerPourSuperAdmin(any(UUID.class));

        given()
                .pathParam("id", TYPE_ID)
                .when()
                .delete(BASE_PATH + "/{id}")
                .then()
                .statusCode(400)
                .body("error", notNullValue());
    }

    @Test
    @TestSecurity(user = "admin@test.com", roles = {"ADMIN"})
    void deleteType_notSuperAdmin_illegalArg_returns400() {
        when(securityIdentity.hasRole("SUPER_ADMIN")).thenReturn(false);
        when(securityIdentity.hasRole("SUPER_ADMIN")).thenReturn(false);
        doThrow(new IllegalArgumentException("Type introuvable"))
                .when(typeReferenceService).supprimer(any(UUID.class));

        given()
                .pathParam("id", TYPE_ID)
                .when()
                .delete(BASE_PATH + "/{id}")
                .then()
                .statusCode(400)
                .body("error", notNullValue());
    }

    // -------------------------------------------------------------------------
    // DELETE /api/references/types-organisation/{id} — SUPER_ADMIN branch
    // -------------------------------------------------------------------------

    @Test
    @TestSecurity(user = "superadmin@test.com", roles = {"SUPER_ADMIN"})
    void deleteType_superAdministrateur_returns204() {
        when(securityIdentity.hasRole("SUPER_ADMIN")).thenReturn(false);
        when(securityIdentity.hasRole("SUPER_ADMIN")).thenReturn(true);
        doNothing().when(typeReferenceService).supprimerPourSuperAdmin(any(UUID.class));

        given()
                .pathParam("id", TYPE_ID)
                .when()
                .delete(BASE_PATH + "/{id}")
                .then()
                .statusCode(204);
    }
}
