package dev.lions.unionflow.server.service;

import dev.lions.unionflow.server.api.dto.document.request.CreateDocumentRequest;
import dev.lions.unionflow.server.api.dto.document.request.CreatePieceJointeRequest;
import dev.lions.unionflow.server.api.dto.document.response.DocumentResponse;
import dev.lions.unionflow.server.api.dto.document.response.PieceJointeResponse;
import dev.lions.unionflow.server.api.enums.document.TypeDocument;
import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.TestTransaction;
import io.quarkus.test.security.TestSecurity;
import jakarta.inject.Inject;
import jakarta.ws.rs.NotFoundException;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import java.util.List;
import java.util.UUID;

import static org.assertj.core.api.Assertions.*;

@QuarkusTest
class DocumentServiceTest {

    @Inject
    DocumentService documentService;

    @Test
    @TestTransaction
    @TestSecurity(user = "test@example.com", roles = {"ADMIN"})
    @DisplayName("creerDocument avec données valides crée le document")
    void creerDocument_validRequest_createsDocument() {
        CreateDocumentRequest request = CreateDocumentRequest.builder()
                .nomFichier("test-doc.pdf")
                .nomOriginal("Document Original.pdf")
                .cheminStockage("/storage/documents/test-doc.pdf")
                .typeMime("application/pdf")
                .tailleOctets(1024L)
                .typeDocument(TypeDocument.FACTURE)
                .hashMd5("abc123")
                .hashSha256("def456")
                .description("Document de test")
                .build();

        DocumentResponse response = documentService.creerDocument(request);

        assertThat(response).isNotNull();
        assertThat(response.getId()).isNotNull();
        assertThat(response.getNomFichier()).isEqualTo("test-doc.pdf");
        assertThat(response.getNomOriginal()).isEqualTo("Document Original.pdf");
        assertThat(response.getTypeDocument()).isEqualTo(TypeDocument.FACTURE);
        assertThat(response.getTailleOctets()).isEqualTo(1024L);
    }

    @Test
    @TestTransaction
    @TestSecurity(user = "test@example.com", roles = {"ADMIN"})
    @DisplayName("creerDocument sans typeDocument utilise AUTRE par défaut")
    void creerDocument_noTypeDocument_defaultsToAutre() {
        CreateDocumentRequest request = CreateDocumentRequest.builder()
                .nomFichier("test.txt")
                .nomOriginal("test.txt")
                .cheminStockage("/storage/test.txt")
                .typeMime("text/plain")
                .tailleOctets(100L)
                .build();

        DocumentResponse response = documentService.creerDocument(request);

        assertThat(response.getTypeDocument()).isEqualTo(TypeDocument.AUTRE);
    }

    @Test
    @TestTransaction
    @DisplayName("trouverParId avec ID valide retourne le document")
    void trouverParId_validId_returnsDocument() {
        CreateDocumentRequest request = CreateDocumentRequest.builder()
                .nomFichier("find-me.pdf")
                .nomOriginal("Find Me.pdf")
                .cheminStockage("/storage/find-me.pdf")
                .typeMime("application/pdf")
                .tailleOctets(500L)
                .build();

        DocumentResponse created = documentService.creerDocument(request);

        DocumentResponse found = documentService.trouverParId(created.getId());

        assertThat(found).isNotNull();
        assertThat(found.getId()).isEqualTo(created.getId());
        assertThat(found.getNomFichier()).isEqualTo("find-me.pdf");
    }

    @Test
    @TestTransaction
    @DisplayName("trouverParId avec ID inexistant lance NotFoundException")
    void trouverParId_inexistant_throws() {
        assertThatThrownBy(() -> documentService.trouverParId(UUID.randomUUID()))
                .isInstanceOf(NotFoundException.class)
                .hasMessageContaining("Document non trouvé");
    }

    @Test
    @TestTransaction
    @TestSecurity(user = "downloader@example.com", roles = {"MEMBRE"})
    @DisplayName("enregistrerTelechargement incrémente le compteur de téléchargements")
    void enregistrerTelechargement_incrementsCounter() {
        CreateDocumentRequest request = CreateDocumentRequest.builder()
                .nomFichier("download-test.pdf")
                .nomOriginal("Download Test.pdf")
                .cheminStockage("/storage/download-test.pdf")
                .typeMime("application/pdf")
                .tailleOctets(2048L)
                .build();

        DocumentResponse created = documentService.creerDocument(request);
        assertThat(created.getNombreTelechargements()).satisfiesAnyOf(
                val -> assertThat(val).isNull(),
                val -> assertThat(val).isEqualTo(0)
        );

        documentService.enregistrerTelechargement(created.getId());

        DocumentResponse afterFirstDownload = documentService.trouverParId(created.getId());
        assertThat(afterFirstDownload.getNombreTelechargements()).isEqualTo(1);

        documentService.enregistrerTelechargement(created.getId());

        DocumentResponse afterSecondDownload = documentService.trouverParId(created.getId());
        assertThat(afterSecondDownload.getNombreTelechargements()).isEqualTo(2);
    }

    @Test
    @TestTransaction
    @DisplayName("enregistrerTelechargement avec ID inexistant lance NotFoundException")
    void enregistrerTelechargement_invalidId_throws() {
        assertThatThrownBy(() -> documentService.enregistrerTelechargement(UUID.randomUUID()))
                .isInstanceOf(NotFoundException.class)
                .hasMessageContaining("Document non trouvé");
    }

    @Test
    @TestTransaction
    @TestSecurity(user = "attach@example.com", roles = {"ADMIN"})
    @DisplayName("creerPieceJointe avec données valides crée la pièce jointe")
    void creerPieceJointe_validRequest_createsPieceJointe() {
        CreateDocumentRequest docRequest = CreateDocumentRequest.builder()
                .nomFichier("base-doc.pdf")
                .nomOriginal("Base Document.pdf")
                .cheminStockage("/storage/base-doc.pdf")
                .typeMime("application/pdf")
                .tailleOctets(1000L)
                .build();

        DocumentResponse doc = documentService.creerDocument(docRequest);

        CreatePieceJointeRequest pjRequest = CreatePieceJointeRequest.builder()
                .ordre(1)
                .libelle("Pièce Jointe Test")
                .commentaire("Commentaire de test")
                .documentId(doc.getId())
                .typeEntiteRattachee("MEMBRE")
                .entiteRattacheeId(UUID.randomUUID())
                .build();

        PieceJointeResponse response = documentService.creerPieceJointe(pjRequest);

        assertThat(response).isNotNull();
        assertThat(response.getId()).isNotNull();
        assertThat(response.getLibelle()).isEqualTo("Pièce Jointe Test");
        assertThat(response.getCommentaire()).isEqualTo("Commentaire de test");
        assertThat(response.getOrdre()).isEqualTo(1);
        assertThat(response.getDocumentId()).isEqualTo(doc.getId());
        assertThat(response.getTypeEntiteRattachee()).isEqualTo("MEMBRE");
    }

    @Test
    @TestTransaction
    @TestSecurity(user = "attach@example.com", roles = {"ADMIN"})
    @DisplayName("creerPieceJointe sans ordre utilise 1 par défaut")
    void creerPieceJointe_noOrdre_defaultsToOne() {
        CreateDocumentRequest docRequest = CreateDocumentRequest.builder()
                .nomFichier("doc-ordre.pdf")
                .nomOriginal("Doc Ordre.pdf")
                .cheminStockage("/storage/doc-ordre.pdf")
                .typeMime("application/pdf")
                .tailleOctets(500L)
                .build();

        DocumentResponse doc = documentService.creerDocument(docRequest);

        CreatePieceJointeRequest pjRequest = CreatePieceJointeRequest.builder()
                .libelle("PJ sans ordre")
                .documentId(doc.getId())
                .typeEntiteRattachee("ORGANISATION")
                .entiteRattacheeId(UUID.randomUUID())
                .build();

        PieceJointeResponse response = documentService.creerPieceJointe(pjRequest);

        assertThat(response.getOrdre()).isEqualTo(1);
    }

    @Test
    @TestTransaction
    @TestSecurity(user = "attach@example.com", roles = {"ADMIN"})
    @DisplayName("creerPieceJointe avec documentId invalide lance NotFoundException")
    void creerPieceJointe_invalidDocumentId_throws() {
        CreatePieceJointeRequest pjRequest = CreatePieceJointeRequest.builder()
                .libelle("PJ avec doc invalide")
                .documentId(UUID.randomUUID())
                .typeEntiteRattachee("MEMBRE")
                .entiteRattacheeId(UUID.randomUUID())
                .build();

        assertThatThrownBy(() -> documentService.creerPieceJointe(pjRequest))
                .isInstanceOf(NotFoundException.class)
                .hasMessageContaining("Document non trouvé");
    }

    @Test
    @TestTransaction
    @TestSecurity(user = "attach@example.com", roles = {"ADMIN"})
    @DisplayName("creerPieceJointe sans typeEntiteRattachee lance IllegalArgumentException")
    void creerPieceJointe_noTypeEntite_throws() {
        CreateDocumentRequest docRequest = CreateDocumentRequest.builder()
                .nomFichier("doc-validation.pdf")
                .nomOriginal("Doc Validation.pdf")
                .cheminStockage("/storage/doc-validation.pdf")
                .typeMime("application/pdf")
                .tailleOctets(500L)
                .build();

        DocumentResponse doc = documentService.creerDocument(docRequest);

        CreatePieceJointeRequest pjRequest = CreatePieceJointeRequest.builder()
                .libelle("PJ sans type entité")
                .documentId(doc.getId())
                .entiteRattacheeId(UUID.randomUUID())
                .build();

        assertThatThrownBy(() -> documentService.creerPieceJointe(pjRequest))
                .isInstanceOf(IllegalArgumentException.class)
                .hasMessageContaining("type_entite_rattachee");
    }

    @Test
    @TestTransaction
    @TestSecurity(user = "attach@example.com", roles = {"ADMIN"})
    @DisplayName("creerPieceJointe avec typeEntiteRattachee vide lance IllegalArgumentException")
    void creerPieceJointe_emptyTypeEntite_throws() {
        CreateDocumentRequest docRequest = CreateDocumentRequest.builder()
                .nomFichier("doc-empty.pdf")
                .nomOriginal("Doc Empty.pdf")
                .cheminStockage("/storage/doc-empty.pdf")
                .typeMime("application/pdf")
                .tailleOctets(500L)
                .build();

        DocumentResponse doc = documentService.creerDocument(docRequest);

        CreatePieceJointeRequest pjRequest = CreatePieceJointeRequest.builder()
                .libelle("PJ type vide")
                .documentId(doc.getId())
                .typeEntiteRattachee("")
                .entiteRattacheeId(UUID.randomUUID())
                .build();

        assertThatThrownBy(() -> documentService.creerPieceJointe(pjRequest))
                .isInstanceOf(IllegalArgumentException.class);
    }

    @Test
    @TestTransaction
    @TestSecurity(user = "attach@example.com", roles = {"ADMIN"})
    @DisplayName("creerPieceJointe sans entiteRattacheeId lance IllegalArgumentException")
    void creerPieceJointe_noEntiteId_throws() {
        CreateDocumentRequest docRequest = CreateDocumentRequest.builder()
                .nomFichier("doc-id.pdf")
                .nomOriginal("Doc ID.pdf")
                .cheminStockage("/storage/doc-id.pdf")
                .typeMime("application/pdf")
                .tailleOctets(500L)
                .build();

        DocumentResponse doc = documentService.creerDocument(docRequest);

        CreatePieceJointeRequest pjRequest = CreatePieceJointeRequest.builder()
                .libelle("PJ sans entité ID")
                .documentId(doc.getId())
                .typeEntiteRattachee("MEMBRE")
                .build();

        assertThatThrownBy(() -> documentService.creerPieceJointe(pjRequest))
                .isInstanceOf(IllegalArgumentException.class)
                .hasMessageContaining("entite_rattachee_id");
    }

    @Test
    @TestTransaction
    @TestSecurity(user = "list@example.com", roles = {"ADMIN"})
    @DisplayName("listerPiecesJointesParDocument retourne toutes les pièces jointes du document")
    void listerPiecesJointesParDocument_returnsAllAttachments() {
        CreateDocumentRequest docRequest = CreateDocumentRequest.builder()
                .nomFichier("doc-with-pj.pdf")
                .nomOriginal("Doc with PJ.pdf")
                .cheminStockage("/storage/doc-with-pj.pdf")
                .typeMime("application/pdf")
                .tailleOctets(2000L)
                .build();

        DocumentResponse doc = documentService.creerDocument(docRequest);

        UUID entiteId = UUID.randomUUID();

        CreatePieceJointeRequest pj1 = CreatePieceJointeRequest.builder()
                .ordre(1)
                .libelle("PJ 1")
                .documentId(doc.getId())
                .typeEntiteRattachee("MEMBRE")
                .entiteRattacheeId(entiteId)
                .build();

        CreatePieceJointeRequest pj2 = CreatePieceJointeRequest.builder()
                .ordre(2)
                .libelle("PJ 2")
                .documentId(doc.getId())
                .typeEntiteRattachee("MEMBRE")
                .entiteRattacheeId(entiteId)
                .build();

        documentService.creerPieceJointe(pj1);
        documentService.creerPieceJointe(pj2);

        List<PieceJointeResponse> pjList = documentService.listerPiecesJointesParDocument(doc.getId());

        assertThat(pjList).hasSize(2);
        assertThat(pjList).extracting(PieceJointeResponse::getLibelle)
                .containsExactlyInAnyOrder("PJ 1", "PJ 2");
    }

    @Test
    @TestTransaction
    @DisplayName("listerPiecesJointesParDocument sans pièces jointes retourne liste vide")
    void listerPiecesJointesParDocument_noPJ_returnsEmpty() {
        CreateDocumentRequest docRequest = CreateDocumentRequest.builder()
                .nomFichier("doc-no-pj.pdf")
                .nomOriginal("Doc No PJ.pdf")
                .cheminStockage("/storage/doc-no-pj.pdf")
                .typeMime("application/pdf")
                .tailleOctets(1000L)
                .build();

        DocumentResponse doc = documentService.creerDocument(docRequest);

        List<PieceJointeResponse> pjList = documentService.listerPiecesJointesParDocument(doc.getId());

        assertThat(pjList).isEmpty();
    }

    @Test
    @TestTransaction
    @TestSecurity(user = "dl-null@example.com", roles = {"MEMBRE"})
    @DisplayName("enregistrerTelechargement — nombreTelechargements null → premier téléchargement → 1")
    void enregistrerTelechargement_nombreTelechargements_null_branch() {
        // Document sans nombreTelechargements initialisé (null) → premier téléchargement
        CreateDocumentRequest request = CreateDocumentRequest.builder()
                .nomFichier("dl-null-branch.pdf")
                .nomOriginal("DL Null Branch.pdf")
                .cheminStockage("/storage/dl-null-branch.pdf")
                .typeMime("application/pdf")
                .tailleOctets(512L)
                .build();

        DocumentResponse created = documentService.creerDocument(request);
        // nombreTelechargements est null après création

        documentService.enregistrerTelechargement(created.getId());

        DocumentResponse after = documentService.trouverParId(created.getId());
        assertThat(after.getNombreTelechargements()).isEqualTo(1);
    }

    @Test
    @TestTransaction
    @TestSecurity(user = "dl-notnull@example.com", roles = {"MEMBRE"})
    @DisplayName("enregistrerTelechargement — branche nombreTelechargements!=null (deuxième téléchargement)")
    void enregistrerTelechargement_nombreTelechargements_notNull_branch() {
        // Couvre la branche ternaire vraie : (1 != null ? 1 : 0) + 1 = 2
        CreateDocumentRequest request = CreateDocumentRequest.builder()
                .nomFichier("dl-notnull-branch.pdf")
                .nomOriginal("DL NotNull Branch.pdf")
                .cheminStockage("/storage/dl-notnull-branch.pdf")
                .typeMime("application/pdf")
                .tailleOctets(512L)
                .build();

        DocumentResponse created = documentService.creerDocument(request);
        documentService.enregistrerTelechargement(created.getId()); // compteur → 1 (branch null)
        documentService.enregistrerTelechargement(created.getId()); // compteur → 2 (branch not-null)

        DocumentResponse after = documentService.trouverParId(created.getId());
        assertThat(after.getNombreTelechargements()).isEqualTo(2);
    }

    @Test
    @TestTransaction
    @TestSecurity(user = "pj-no-doc@example.com", roles = {"ADMIN"})
    @DisplayName("creerPieceJointe documentId null → L230 false (pas de lookup document) → pièce jointe créée sans document")
    void creerPieceJointe_documentIdNull_L230FalseBranch() {
        UUID entiteId = UUID.randomUUID();

        CreatePieceJointeRequest request = CreatePieceJointeRequest.builder()
                .libelle("PJ sans document")
                .documentId(null)
                .typeEntiteRattachee("COTISATION")
                .entiteRattacheeId(entiteId)
                .build();

        PieceJointeResponse response = documentService.creerPieceJointe(request);

        assertThat(response).isNotNull();
        assertThat(response.getId()).isNotNull();
    }
}
