package dev.lions.unionflow.server.entity;

import dev.lions.unionflow.server.api.enums.membre.NiveauRisqueKyc;
import dev.lions.unionflow.server.api.enums.membre.StatutKyc;
import dev.lions.unionflow.server.api.enums.membre.TypePieceIdentite;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.UUID;

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

@DisplayName("KycDossier entity")
class KycDossierTest {

    // -------------------------------------------------------------------------
    // No-args constructor
    // -------------------------------------------------------------------------
    @Test
    @DisplayName("no-args constructor creates instance with default field values")
    void noArgsConstructor_createsInstanceWithDefaults() {
        KycDossier dossier = new KycDossier();

        assertThat(dossier).isNotNull();
        assertThat(dossier.getMembre()).isNull();
        assertThat(dossier.getTypePiece()).isNull();
        assertThat(dossier.getNumeroPiece()).isNull();
        assertThat(dossier.getStatut()).isNull();
        assertThat(dossier.getNiveauRisque()).isNull();
        assertThat(dossier.getScoreRisque()).isZero();
        assertThat(dossier.isEstPep()).isFalse();
    }

    // -------------------------------------------------------------------------
    // Builder — all fields
    // -------------------------------------------------------------------------
    @Nested
    @DisplayName("Builder")
    class BuilderTests {

        @Test
        @DisplayName("builder sets all explicit fields")
        void builder_setsAllFields() {
            UUID validateurId = UUID.randomUUID();
            LocalDate expiration = LocalDate.of(2030, 6, 15);
            LocalDateTime now = LocalDateTime.now();

            KycDossier dossier = KycDossier.builder()
                    .typePiece(TypePieceIdentite.PASSEPORT)
                    .numeroPiece("AB123456")
                    .dateExpirationPiece(expiration)
                    .pieceIdentiteRectoFileId("file-recto-001")
                    .pieceIdentiteVersoFileId("file-verso-001")
                    .justifDomicileFileId("file-justif-001")
                    .statut(StatutKyc.EN_COURS)
                    .niveauRisque(NiveauRisqueKyc.MOYEN)
                    .scoreRisque(55)
                    .estPep(true)
                    .nationalite("SEN")
                    .dateVerification(now)
                    .validateurId(validateurId)
                    .notesValidateur("Dossier complet")
                    .anneeReference(2026)
                    .build();

            assertThat(dossier.getTypePiece()).isEqualTo(TypePieceIdentite.PASSEPORT);
            assertThat(dossier.getNumeroPiece()).isEqualTo("AB123456");
            assertThat(dossier.getDateExpirationPiece()).isEqualTo(expiration);
            assertThat(dossier.getPieceIdentiteRectoFileId()).isEqualTo("file-recto-001");
            assertThat(dossier.getPieceIdentiteVersoFileId()).isEqualTo("file-verso-001");
            assertThat(dossier.getJustifDomicileFileId()).isEqualTo("file-justif-001");
            assertThat(dossier.getStatut()).isEqualTo(StatutKyc.EN_COURS);
            assertThat(dossier.getNiveauRisque()).isEqualTo(NiveauRisqueKyc.MOYEN);
            assertThat(dossier.getScoreRisque()).isEqualTo(55);
            assertThat(dossier.isEstPep()).isTrue();
            assertThat(dossier.getNationalite()).isEqualTo("SEN");
            assertThat(dossier.getDateVerification()).isEqualTo(now);
            assertThat(dossier.getValidateurId()).isEqualTo(validateurId);
            assertThat(dossier.getNotesValidateur()).isEqualTo("Dossier complet");
            assertThat(dossier.getAnneeReference()).isEqualTo(2026);
        }

        @Test
        @DisplayName("builder uses default statut NON_VERIFIE when not specified")
        void builder_defaultStatutIsNonVerifie() {
            KycDossier dossier = KycDossier.builder()
                    .numeroPiece("XY999")
                    .typePiece(TypePieceIdentite.CNI)
                    .build();

            assertThat(dossier.getStatut()).isEqualTo(StatutKyc.NON_VERIFIE);
        }

        @Test
        @DisplayName("builder uses default niveauRisque FAIBLE when not specified")
        void builder_defaultNiveauRisqueIsFaible() {
            KycDossier dossier = KycDossier.builder()
                    .numeroPiece("XY999")
                    .typePiece(TypePieceIdentite.CNI)
                    .build();

            assertThat(dossier.getNiveauRisque()).isEqualTo(NiveauRisqueKyc.FAIBLE);
        }

        @Test
        @DisplayName("builder uses default scoreRisque 0 when not specified")
        void builder_defaultScoreRisqueIsZero() {
            KycDossier dossier = KycDossier.builder().build();

            assertThat(dossier.getScoreRisque()).isZero();
        }

        @Test
        @DisplayName("builder uses default estPep false when not specified")
        void builder_defaultEstPepIsFalse() {
            KycDossier dossier = KycDossier.builder().build();

            assertThat(dossier.isEstPep()).isFalse();
        }

        @Test
        @DisplayName("builder uses current year as default anneeReference")
        void builder_defaultAnneeReferenceIsCurrentYear() {
            KycDossier dossier = KycDossier.builder().build();

            assertThat(dossier.getAnneeReference()).isEqualTo(LocalDate.now().getYear());
        }
    }

    // -------------------------------------------------------------------------
    // All-args constructor
    // -------------------------------------------------------------------------
    @Test
    @DisplayName("all-args constructor (via setter chain) round-trips correctly")
    void allArgsConstructor_roundTrips() {
        // KycDossier's @AllArgsConstructor includes parent fields via Lombok,
        // but since we cannot call super fields directly here, we verify via setters.
        KycDossier dossier = new KycDossier();
        UUID id = UUID.randomUUID();
        dossier.setId(id);
        dossier.setNumeroPiece("CNI-001");
        dossier.setTypePiece(TypePieceIdentite.CNI);
        dossier.setStatut(StatutKyc.VERIFIE);
        dossier.setNiveauRisque(NiveauRisqueKyc.ELEVE);

        assertThat(dossier.getId()).isEqualTo(id);
        assertThat(dossier.getNumeroPiece()).isEqualTo("CNI-001");
        assertThat(dossier.getTypePiece()).isEqualTo(TypePieceIdentite.CNI);
        assertThat(dossier.getStatut()).isEqualTo(StatutKyc.VERIFIE);
        assertThat(dossier.getNiveauRisque()).isEqualTo(NiveauRisqueKyc.ELEVE);
    }

    // -------------------------------------------------------------------------
    // Getters / Setters
    // -------------------------------------------------------------------------
    @Nested
    @DisplayName("Getters and Setters")
    class GettersSettersTests {

        @Test
        @DisplayName("setMembre / getMembre round-trips")
        void membre_roundTrips() {
            Membre membre = new Membre();
            KycDossier dossier = new KycDossier();
            dossier.setMembre(membre);
            assertThat(dossier.getMembre()).isSameAs(membre);
        }

        @Test
        @DisplayName("setNumeroPiece / getNumeroPiece round-trips")
        void numeroPiece_roundTrips() {
            KycDossier dossier = new KycDossier();
            dossier.setNumeroPiece("PASS-9876");
            assertThat(dossier.getNumeroPiece()).isEqualTo("PASS-9876");
        }

        @Test
        @DisplayName("setDateExpirationPiece / getDateExpirationPiece round-trips")
        void dateExpirationPiece_roundTrips() {
            LocalDate date = LocalDate.of(2028, 12, 31);
            KycDossier dossier = new KycDossier();
            dossier.setDateExpirationPiece(date);
            assertThat(dossier.getDateExpirationPiece()).isEqualTo(date);
        }

        @Test
        @DisplayName("setScoreRisque / getScoreRisque round-trips")
        void scoreRisque_roundTrips() {
            KycDossier dossier = new KycDossier();
            dossier.setScoreRisque(75);
            assertThat(dossier.getScoreRisque()).isEqualTo(75);
        }

        @Test
        @DisplayName("setEstPep / isEstPep round-trips")
        void estPep_roundTrips() {
            KycDossier dossier = new KycDossier();
            dossier.setEstPep(true);
            assertThat(dossier.isEstPep()).isTrue();
            dossier.setEstPep(false);
            assertThat(dossier.isEstPep()).isFalse();
        }

        @Test
        @DisplayName("setNationalite / getNationalite round-trips")
        void nationalite_roundTrips() {
            KycDossier dossier = new KycDossier();
            dossier.setNationalite("CIV");
            assertThat(dossier.getNationalite()).isEqualTo("CIV");
        }

        @Test
        @DisplayName("setDateVerification / getDateVerification round-trips")
        void dateVerification_roundTrips() {
            LocalDateTime dt = LocalDateTime.of(2026, 3, 10, 14, 30);
            KycDossier dossier = new KycDossier();
            dossier.setDateVerification(dt);
            assertThat(dossier.getDateVerification()).isEqualTo(dt);
        }

        @Test
        @DisplayName("setValidateurId / getValidateurId round-trips")
        void validateurId_roundTrips() {
            UUID uuid = UUID.randomUUID();
            KycDossier dossier = new KycDossier();
            dossier.setValidateurId(uuid);
            assertThat(dossier.getValidateurId()).isEqualTo(uuid);
        }

        @Test
        @DisplayName("setNotesValidateur / getNotesValidateur round-trips")
        void notesValidateur_roundTrips() {
            KycDossier dossier = new KycDossier();
            dossier.setNotesValidateur("Notes de validation");
            assertThat(dossier.getNotesValidateur()).isEqualTo("Notes de validation");
        }

        @Test
        @DisplayName("setAnneeReference / getAnneeReference round-trips")
        void anneeReference_roundTrips() {
            KycDossier dossier = new KycDossier();
            dossier.setAnneeReference(2025);
            assertThat(dossier.getAnneeReference()).isEqualTo(2025);
        }

        @Test
        @DisplayName("file IDs round-trip")
        void fileIds_roundTrip() {
            KycDossier dossier = new KycDossier();
            dossier.setPieceIdentiteRectoFileId("recto-42");
            dossier.setPieceIdentiteVersoFileId("verso-42");
            dossier.setJustifDomicileFileId("domicile-42");
            assertThat(dossier.getPieceIdentiteRectoFileId()).isEqualTo("recto-42");
            assertThat(dossier.getPieceIdentiteVersoFileId()).isEqualTo("verso-42");
            assertThat(dossier.getJustifDomicileFileId()).isEqualTo("domicile-42");
        }
    }

    // -------------------------------------------------------------------------
    // Business method: isPieceExpiree()
    // -------------------------------------------------------------------------
    @Nested
    @DisplayName("isPieceExpiree()")
    class IsPieceExpireeTests {

        @Test
        @DisplayName("returns true when dateExpirationPiece is in the past")
        void isPieceExpiree_returnsTrue_whenExpired() {
            KycDossier dossier = KycDossier.builder()
                    .dateExpirationPiece(LocalDate.now().minusDays(1))
                    .build();
            assertThat(dossier.isPieceExpiree()).isTrue();
        }

        @Test
        @DisplayName("returns false when dateExpirationPiece is in the future")
        void isPieceExpiree_returnsFalse_whenNotExpired() {
            KycDossier dossier = KycDossier.builder()
                    .dateExpirationPiece(LocalDate.now().plusYears(1))
                    .build();
            assertThat(dossier.isPieceExpiree()).isFalse();
        }

        @Test
        @DisplayName("returns false when dateExpirationPiece is null")
        void isPieceExpiree_returnsFalse_whenNull() {
            KycDossier dossier = KycDossier.builder().build();
            assertThat(dossier.isPieceExpiree()).isFalse();
        }

        @Test
        @DisplayName("returns false when dateExpirationPiece is today")
        void isPieceExpiree_returnsFalse_whenToday() {
            KycDossier dossier = KycDossier.builder()
                    .dateExpirationPiece(LocalDate.now())
                    .build();
            // isBefore(now) is false for today
            assertThat(dossier.isPieceExpiree()).isFalse();
        }
    }

    // -------------------------------------------------------------------------
    // Enum coverage: StatutKyc
    // -------------------------------------------------------------------------
    @Test
    @DisplayName("all StatutKyc values are assignable")
    void statutKyc_allValues() {
        KycDossier dossier = new KycDossier();
        for (StatutKyc statut : StatutKyc.values()) {
            dossier.setStatut(statut);
            assertThat(dossier.getStatut()).isEqualTo(statut);
        }
        assertThat(StatutKyc.NON_VERIFIE.getLibelle()).isEqualTo("Non vérifié");
        assertThat(StatutKyc.EN_COURS.getLibelle()).isEqualTo("En cours");
        assertThat(StatutKyc.VERIFIE.getLibelle()).isEqualTo("Vérifié");
        assertThat(StatutKyc.REFUSE.getLibelle()).isEqualTo("Refusé");
    }

    // -------------------------------------------------------------------------
    // Enum coverage: NiveauRisqueKyc
    // -------------------------------------------------------------------------
    @Test
    @DisplayName("all NiveauRisqueKyc values are assignable")
    void niveauRisqueKyc_allValues() {
        KycDossier dossier = new KycDossier();
        for (NiveauRisqueKyc niveau : NiveauRisqueKyc.values()) {
            dossier.setNiveauRisque(niveau);
            assertThat(dossier.getNiveauRisque()).isEqualTo(niveau);
        }
        assertThat(NiveauRisqueKyc.FAIBLE.getLibelle()).isEqualTo("Risque faible");
        assertThat(NiveauRisqueKyc.MOYEN.getLibelle()).isEqualTo("Risque moyen");
        assertThat(NiveauRisqueKyc.ELEVE.getLibelle()).isEqualTo("Risque élevé");
        assertThat(NiveauRisqueKyc.CRITIQUE.getLibelle()).isEqualTo("Risque critique");
        assertThat(NiveauRisqueKyc.FAIBLE.getScoreMin()).isZero();
        assertThat(NiveauRisqueKyc.FAIBLE.getScoreMax()).isEqualTo(39);
        assertThat(NiveauRisqueKyc.MOYEN.getScoreMin()).isEqualTo(40);
        assertThat(NiveauRisqueKyc.MOYEN.getScoreMax()).isEqualTo(69);
        assertThat(NiveauRisqueKyc.ELEVE.getScoreMin()).isEqualTo(70);
        assertThat(NiveauRisqueKyc.ELEVE.getScoreMax()).isEqualTo(89);
        assertThat(NiveauRisqueKyc.CRITIQUE.getScoreMin()).isEqualTo(90);
        assertThat(NiveauRisqueKyc.CRITIQUE.getScoreMax()).isEqualTo(100);
    }

    // -------------------------------------------------------------------------
    // Enum coverage: TypePieceIdentite
    // -------------------------------------------------------------------------
    @Test
    @DisplayName("all TypePieceIdentite values are assignable")
    void typePieceIdentite_allValues() {
        KycDossier dossier = new KycDossier();
        for (TypePieceIdentite type : TypePieceIdentite.values()) {
            dossier.setTypePiece(type);
            assertThat(dossier.getTypePiece()).isEqualTo(type);
        }
        assertThat(TypePieceIdentite.CNI.getLibelle()).isEqualTo("Carte Nationale d'Identité");
        assertThat(TypePieceIdentite.PASSEPORT.getLibelle()).isEqualTo("Passeport");
        assertThat(TypePieceIdentite.TITRE_SEJOUR.getLibelle()).isEqualTo("Titre de séjour");
        assertThat(TypePieceIdentite.CARTE_CONSULAIRE.getLibelle()).isEqualTo("Carte consulaire");
        assertThat(TypePieceIdentite.PERMIS_CONDUIRE.getLibelle()).isEqualTo("Permis de conduire");
        assertThat(TypePieceIdentite.AUTRE.getLibelle()).isEqualTo("Autre pièce officielle");
    }

    // -------------------------------------------------------------------------
    // UUID id (inherited from BaseEntity)
    // -------------------------------------------------------------------------
    @Test
    @DisplayName("UUID id field is settable and gettable")
    void uuidId_settableAndGettable() {
        UUID id = UUID.randomUUID();
        KycDossier dossier = new KycDossier();
        dossier.setId(id);
        assertThat(dossier.getId()).isEqualTo(id);
    }

    // -------------------------------------------------------------------------
    // equals / hashCode  (Lombok @EqualsAndHashCode(callSuper=true))
    // -------------------------------------------------------------------------
    @Test
    @DisplayName("two instances with same id are equal")
    void equals_sameId_areEqual() {
        UUID id = UUID.randomUUID();
        KycDossier a = new KycDossier();
        a.setId(id);
        a.setNumeroPiece("P1");
        KycDossier b = new KycDossier();
        b.setId(id);
        b.setNumeroPiece("P1");
        assertThat(a).isEqualTo(b);
        assertThat(a.hashCode()).isEqualTo(b.hashCode());
    }

    @Test
    @DisplayName("two instances with different ids are not equal")
    void equals_differentId_areNotEqual() {
        KycDossier a = new KycDossier();
        a.setId(UUID.randomUUID());
        KycDossier b = new KycDossier();
        b.setId(UUID.randomUUID());
        assertThat(a).isNotEqualTo(b);
    }
}
