package dev.lions.unionflow.server.resource;

import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.notNullValue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.when;

import dev.lions.unionflow.server.api.dto.communication.request.SendMessageRequest;
import dev.lions.unionflow.server.api.dto.communication.response.MessageResponse;
import dev.lions.unionflow.server.api.enums.communication.MessagePriority;
import dev.lions.unionflow.server.api.enums.communication.MessageStatus;
import dev.lions.unionflow.server.api.enums.communication.MessageType;
import dev.lions.unionflow.server.service.MessageService;
import dev.lions.unionflow.server.service.support.SecuriteHelper;
import io.quarkus.test.InjectMock;
import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.security.TestSecurity;
import io.restassured.http.ContentType;
import jakarta.ws.rs.NotFoundException;

import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.UUID;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

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

    private static final String BASE_PATH = "/api/messages";
    private static final String MEMBRE_ID = "00000000-0000-0000-0000-000000000020";
    private static final String MESSAGE_ID = "00000000-0000-0000-0000-000000000021";
    private static final String CONVERSATION_ID = "00000000-0000-0000-0000-000000000022";

    @InjectMock
    MessageService messageService;

    @InjectMock
    SecuriteHelper securiteHelper;

    @BeforeEach
    void setup() {
        when(securiteHelper.resolveMembreId()).thenReturn(UUID.fromString(MEMBRE_ID));
    }

    private MessageResponse buildMessageResponse() {
        return MessageResponse.builder()
                .id(UUID.fromString(MESSAGE_ID))
                .conversationId(UUID.fromString(CONVERSATION_ID))
                .senderId(UUID.fromString(MEMBRE_ID))
                .senderName("Alice Martin")
                .content("Bonjour !")
                .type(MessageType.INDIVIDUAL)
                .status(MessageStatus.SENT)
                .priority(MessagePriority.NORMAL)
                .isEdited(false)
                .isDeleted(false)
                .createdAt(LocalDateTime.now())
                .build();
    }

    // -------------------------------------------------------------------------
    // GET /api/messages
    // -------------------------------------------------------------------------

    @Test
    @TestSecurity(user = "alice@test.com", roles = {"MEMBRE"})
    @DisplayName("getMessages sans conversationId retourne 400")
    void getMessages_missingConversationId_returns400() {
        given()
                .when()
                .get(BASE_PATH)
                .then()
                .statusCode(400);
    }

    @Test
    @TestSecurity(user = "alice@test.com", roles = {"MEMBRE"})
    @DisplayName("getMessages avec conversationId valide retourne 200")
    void getMessages_withConversationId_returns200() {
        when(messageService.getMessages(eq(UUID.fromString(CONVERSATION_ID)), any(), anyInt()))
                .thenReturn(List.of(buildMessageResponse()));

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

    @Test
    @TestSecurity(user = "alice@test.com", roles = {"MEMBRE"})
    @DisplayName("getMessages avec conversationId valide retourne liste vide")
    void getMessages_emptyConversation_returns200() {
        when(messageService.getMessages(any(), any(), anyInt()))
                .thenReturn(Collections.emptyList());

        given()
                .queryParam("conversationId", CONVERSATION_ID)
                .when()
                .get(BASE_PATH)
                .then()
                .statusCode(200)
                .contentType(ContentType.JSON);
    }

    @Test
    @TestSecurity(user = "alice@test.com", roles = {"MEMBRE"})
    @DisplayName("getMessages avec limit personnalisé retourne 200")
    void getMessages_withLimit_returns200() {
        when(messageService.getMessages(any(), any(), eq(10)))
                .thenReturn(List.of(buildMessageResponse()));

        given()
                .queryParam("conversationId", CONVERSATION_ID)
                .queryParam("limit", 10)
                .when()
                .get(BASE_PATH)
                .then()
                .statusCode(200)
                .contentType(ContentType.JSON);
    }

    @Test
    @TestSecurity(user = "alice@test.com", roles = {"MEMBRE"})
    @DisplayName("getMessages conversation non trouvée retourne 404")
    void getMessages_conversationNotFound_returns404() {
        when(messageService.getMessages(any(), any(), anyInt()))
                .thenThrow(new NotFoundException("Conversation non trouvée ou accès refusé"));

        given()
                .queryParam("conversationId", CONVERSATION_ID)
                .when()
                .get(BASE_PATH)
                .then()
                .statusCode(404);
    }

    // -------------------------------------------------------------------------
    // POST /api/messages
    // -------------------------------------------------------------------------

    @Test
    @TestSecurity(user = "alice@test.com", roles = {"MEMBRE"})
    @DisplayName("sendMessage avec body valide retourne 201")
    void sendMessage_validRequest_returns201() {
        when(messageService.sendMessage(any(SendMessageRequest.class), any()))
                .thenReturn(buildMessageResponse());

        String body = """
                {
                    "conversationId": "%s",
                    "content": "Bonjour à tous !"
                }
                """.formatted(CONVERSATION_ID);

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

    @Test
    @TestSecurity(user = "alice@test.com", roles = {"MEMBRE"})
    @DisplayName("sendMessage sans conversationId retourne 400")
    void sendMessage_missingConversationId_returns400() {
        String body = """
                {
                    "content": "Message sans conversation"
                }
                """;

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

    @Test
    @TestSecurity(user = "alice@test.com", roles = {"MEMBRE"})
    @DisplayName("sendMessage sans contenu retourne 400")
    void sendMessage_missingContent_returns400() {
        String body = """
                {
                    "conversationId": "%s"
                }
                """.formatted(CONVERSATION_ID);

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

    @Test
    @TestSecurity(user = "alice@test.com", roles = {"MEMBRE"})
    @DisplayName("sendMessage conversation non trouvée retourne 404")
    void sendMessage_conversationNotFound_returns404() {
        when(messageService.sendMessage(any(), any()))
                .thenThrow(new NotFoundException("Conversation non trouvée ou accès refusé"));

        String body = """
                {
                    "conversationId": "%s",
                    "content": "Test"
                }
                """.formatted(CONVERSATION_ID);

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

    // -------------------------------------------------------------------------
    // PUT /api/messages/{id}
    // -------------------------------------------------------------------------

    @Test
    @TestSecurity(user = "alice@test.com", roles = {"MEMBRE"})
    @DisplayName("editMessage avec body valide retourne 200")
    void editMessage_validRequest_returns200() {
        when(messageService.editMessage(any(), any(), anyString()))
                .thenReturn(buildMessageResponse());

        given()
                .contentType(ContentType.JSON)
                .body("{\"content\": \"Message modifié\"}")
                .when()
                .put(BASE_PATH + "/{id}", MESSAGE_ID)
                .then()
                .statusCode(200)
                .contentType(ContentType.JSON);
    }

    @Test
    @TestSecurity(user = "alice@test.com", roles = {"MEMBRE"})
    @DisplayName("editMessage sans contenu retourne 400")
    void editMessage_missingContent_returns400() {
        given()
                .contentType(ContentType.JSON)
                .body("{}")
                .when()
                .put(BASE_PATH + "/{id}", MESSAGE_ID)
                .then()
                .statusCode(400);
    }

    @Test
    @TestSecurity(user = "alice@test.com", roles = {"MEMBRE"})
    @DisplayName("editMessage avec contenu vide retourne 400")
    void editMessage_emptyContent_returns400() {
        given()
                .contentType(ContentType.JSON)
                .body("{\"content\": \"\"}")
                .when()
                .put(BASE_PATH + "/{id}", MESSAGE_ID)
                .then()
                .statusCode(400);
    }

    @Test
    @TestSecurity(user = "alice@test.com", roles = {"MEMBRE"})
    @DisplayName("editMessage message non trouvé retourne 404")
    void editMessage_notFound_returns404() {
        when(messageService.editMessage(any(), any(), anyString()))
                .thenThrow(new NotFoundException("Message non trouvé"));

        given()
                .contentType(ContentType.JSON)
                .body("{\"content\": \"Contenu modifié\"}")
                .when()
                .put(BASE_PATH + "/{id}", UUID.randomUUID().toString())
                .then()
                .statusCode(404);
    }

    // -------------------------------------------------------------------------
    // DELETE /api/messages/{id}
    // -------------------------------------------------------------------------

    @Test
    @TestSecurity(user = "alice@test.com", roles = {"MEMBRE"})
    @DisplayName("deleteMessage retourne 204 quand succès")
    void deleteMessage_success_returns204() {
        doNothing().when(messageService).deleteMessage(any(), any());

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

    @Test
    @TestSecurity(user = "alice@test.com", roles = {"MEMBRE"})
    @DisplayName("deleteMessage message non trouvé retourne 404")
    void deleteMessage_notFound_returns404() {
        doThrow(new NotFoundException("Message non trouvé"))
                .when(messageService).deleteMessage(any(), any());

        given()
                .when()
                .delete(BASE_PATH + "/{id}", UUID.randomUUID().toString())
                .then()
                .statusCode(404);
    }

    // -------------------------------------------------------------------------
    // Sécurité — non authentifié
    // -------------------------------------------------------------------------

    @Test
    @DisplayName("getMessages sans authentification retourne 401")
    void getMessages_unauthenticated_returns401() {
        given()
                .queryParam("conversationId", CONVERSATION_ID)
                .when()
                .get(BASE_PATH)
                .then()
                .statusCode(401);
    }

    @Test
    @DisplayName("sendMessage sans authentification retourne 401")
    void sendMessage_unauthenticated_returns401() {
        String body = """
                {
                    "conversationId": "%s",
                    "content": "Message non auth"
                }
                """.formatted(CONVERSATION_ID);

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