package dev.lions.unionflow.server.entity;

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

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.UUID;

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

@DisplayName("BaremeCotisationRole")
class BaremeCotisationRoleTest {

    // -------------------------------------------------------------------------
    // No-args constructor
    // -------------------------------------------------------------------------

    @Test
    @DisplayName("no-args constructor creates non-null instance")
    void noArgsConstructor() {
        BaremeCotisationRole bareme = new BaremeCotisationRole();
        assertThat(bareme).isNotNull();
    }

    @Test
    @DisplayName("no-args constructor: @Builder.Default fields are null (no builder involved)")
    void noArgsConstructor_builderDefaultsAreNull() {
        BaremeCotisationRole bareme = new BaremeCotisationRole();
        // @Builder.Default fields are null when constructed with no-arg ctor
        assertThat(bareme.getMontantMensuel()).isNull();
        assertThat(bareme.getMontantAnnuel()).isNull();
    }

    // -------------------------------------------------------------------------
    // Builder — defaults
    // -------------------------------------------------------------------------

    @Test
    @DisplayName("builder default: montantMensuel = ZERO, montantAnnuel = ZERO")
    void builder_defaultAmounts() {
        BaremeCotisationRole bareme = BaremeCotisationRole.builder()
                .roleOrg("MEMBRE_ORDINAIRE")
                .build();

        assertThat(bareme.getMontantMensuel()).isEqualByComparingTo(BigDecimal.ZERO);
        assertThat(bareme.getMontantAnnuel()).isEqualByComparingTo(BigDecimal.ZERO);
    }

    // -------------------------------------------------------------------------
    // Builder — all fields
    // -------------------------------------------------------------------------

    @Test
    @DisplayName("builder sets all fields for PRESIDENT role")
    void builder_president() {
        Organisation org = new Organisation();
        org.setId(UUID.randomUUID());

        BaremeCotisationRole bareme = BaremeCotisationRole.builder()
                .organisation(org)
                .roleOrg("PRESIDENT")
                .montantMensuel(new BigDecimal("0.00"))
                .montantAnnuel(new BigDecimal("0.00"))
                .description("Exonéré — bureau exécutif")
                .build();

        assertThat(bareme.getOrganisation()).isSameAs(org);
        assertThat(bareme.getRoleOrg()).isEqualTo("PRESIDENT");
        assertThat(bareme.getMontantMensuel()).isEqualByComparingTo("0.00");
        assertThat(bareme.getMontantAnnuel()).isEqualByComparingTo("0.00");
        assertThat(bareme.getDescription()).isEqualTo("Exonéré — bureau exécutif");
    }

    @Test
    @DisplayName("builder sets all fields for TRESORIER role with positive amounts")
    void builder_tresorier() {
        Organisation org = new Organisation();

        BaremeCotisationRole bareme = BaremeCotisationRole.builder()
                .organisation(org)
                .roleOrg("TRESORIER")
                .montantMensuel(new BigDecimal("2500.00"))
                .montantAnnuel(new BigDecimal("25000.00"))
                .description("Taux réduit bureau exécutif")
                .build();

        assertThat(bareme.getRoleOrg()).isEqualTo("TRESORIER");
        assertThat(bareme.getMontantMensuel()).isEqualByComparingTo("2500.00");
        assertThat(bareme.getMontantAnnuel()).isEqualByComparingTo("25000.00");
        assertThat(bareme.getDescription()).isEqualTo("Taux réduit bureau exécutif");
    }

    @Test
    @DisplayName("builder: SECRETAIRE role, no description")
    void builder_secretaireNoDescription() {
        BaremeCotisationRole bareme = BaremeCotisationRole.builder()
                .roleOrg("SECRETAIRE")
                .montantMensuel(new BigDecimal("3000.00"))
                .montantAnnuel(new BigDecimal("30000.00"))
                .build();

        assertThat(bareme.getRoleOrg()).isEqualTo("SECRETAIRE");
        assertThat(bareme.getMontantMensuel()).isEqualByComparingTo("3000.00");
        assertThat(bareme.getMontantAnnuel()).isEqualByComparingTo("30000.00");
        assertThat(bareme.getDescription()).isNull();
    }

    // -------------------------------------------------------------------------
    // All-args constructor
    // -------------------------------------------------------------------------

    @Test
    @DisplayName("all-args constructor populates every field")
    void allArgsConstructor() {
        Organisation org = new Organisation();
        org.setId(UUID.randomUUID());

        BaremeCotisationRole bareme = new BaremeCotisationRole(
                org,
                "MEMBRE_ORDINAIRE",
                new BigDecimal("5000.00"),
                new BigDecimal("50000.00"),
                "Tarif standard membres"
        );

        assertThat(bareme.getOrganisation()).isSameAs(org);
        assertThat(bareme.getRoleOrg()).isEqualTo("MEMBRE_ORDINAIRE");
        assertThat(bareme.getMontantMensuel()).isEqualByComparingTo("5000.00");
        assertThat(bareme.getMontantAnnuel()).isEqualByComparingTo("50000.00");
        assertThat(bareme.getDescription()).isEqualTo("Tarif standard membres");
    }

    // -------------------------------------------------------------------------
    // Getters / Setters (@Data)
    // -------------------------------------------------------------------------

    @Test
    @DisplayName("setters and getters round-trip for all fields")
    void settersGetters() {
        BaremeCotisationRole bareme = new BaremeCotisationRole();

        Organisation org = new Organisation();
        org.setId(UUID.randomUUID());

        bareme.setOrganisation(org);
        bareme.setRoleOrg("VICE_PRESIDENT");
        bareme.setMontantMensuel(new BigDecimal("1500.50"));
        bareme.setMontantAnnuel(new BigDecimal("15005.00"));
        bareme.setDescription("VP taux spécial");

        assertThat(bareme.getOrganisation()).isSameAs(org);
        assertThat(bareme.getRoleOrg()).isEqualTo("VICE_PRESIDENT");
        assertThat(bareme.getMontantMensuel()).isEqualByComparingTo("1500.50");
        assertThat(bareme.getMontantAnnuel()).isEqualByComparingTo("15005.00");
        assertThat(bareme.getDescription()).isEqualTo("VP taux spécial");
    }

    @Test
    @DisplayName("description accepts null (optional field)")
    void descriptionAcceptsNull() {
        BaremeCotisationRole bareme = new BaremeCotisationRole();
        bareme.setDescription(null);
        assertThat(bareme.getDescription()).isNull();
    }

    @Test
    @DisplayName("organisation can be set to null")
    void organisationAcceptsNull() {
        BaremeCotisationRole bareme = new BaremeCotisationRole();
        bareme.setOrganisation(null);
        assertThat(bareme.getOrganisation()).isNull();
    }

    @Test
    @DisplayName("amounts can be set to BigDecimal.ZERO")
    void amountsZero() {
        BaremeCotisationRole bareme = new BaremeCotisationRole();
        bareme.setMontantMensuel(BigDecimal.ZERO);
        bareme.setMontantAnnuel(BigDecimal.ZERO);
        assertThat(bareme.getMontantMensuel()).isEqualByComparingTo(BigDecimal.ZERO);
        assertThat(bareme.getMontantAnnuel()).isEqualByComparingTo(BigDecimal.ZERO);
    }

    // -------------------------------------------------------------------------
    // BaseEntity fields
    // -------------------------------------------------------------------------

    @Test
    @DisplayName("BaseEntity fields accessible via inherited getters/setters")
    void baseEntityFields() {
        BaremeCotisationRole bareme = new BaremeCotisationRole();
        UUID id = UUID.randomUUID();
        LocalDateTime now = LocalDateTime.now();

        bareme.setId(id);
        bareme.setDateCreation(now);
        bareme.setDateModification(now);
        bareme.setCreePar("admin@test.com");
        bareme.setModifiePar("ops@test.com");
        bareme.setVersion(1L);
        bareme.setActif(true);

        assertThat(bareme.getId()).isEqualTo(id);
        assertThat(bareme.getDateCreation()).isEqualTo(now);
        assertThat(bareme.getDateModification()).isEqualTo(now);
        assertThat(bareme.getCreePar()).isEqualTo("admin@test.com");
        assertThat(bareme.getModifiePar()).isEqualTo("ops@test.com");
        assertThat(bareme.getVersion()).isEqualTo(1L);
        assertThat(bareme.getActif()).isTrue();
    }

    // -------------------------------------------------------------------------
    // equals / hashCode / toString  (@Data + @EqualsAndHashCode(callSuper = true))
    // -------------------------------------------------------------------------

    @Test
    @DisplayName("equals and hashCode consistent for identical content")
    void equalsHashCode() {
        BaremeCotisationRole a = BaremeCotisationRole.builder()
                .roleOrg("PRESIDENT")
                .montantMensuel(BigDecimal.ZERO)
                .montantAnnuel(BigDecimal.ZERO)
                .build();
        BaremeCotisationRole b = BaremeCotisationRole.builder()
                .roleOrg("PRESIDENT")
                .montantMensuel(BigDecimal.ZERO)
                .montantAnnuel(BigDecimal.ZERO)
                .build();

        assertThat(a).isEqualTo(b);
        assertThat(a.hashCode()).isEqualTo(b.hashCode());
    }

    @Test
    @DisplayName("equals returns false for different roleOrg")
    void equalsReturnsFalseForDifferentRole() {
        BaremeCotisationRole a = BaremeCotisationRole.builder()
                .roleOrg("PRESIDENT")
                .montantMensuel(BigDecimal.ZERO)
                .montantAnnuel(BigDecimal.ZERO)
                .build();
        BaremeCotisationRole b = BaremeCotisationRole.builder()
                .roleOrg("TRESORIER")
                .montantMensuel(BigDecimal.ZERO)
                .montantAnnuel(BigDecimal.ZERO)
                .build();
        assertThat(a).isNotEqualTo(b);
    }

    @Test
    @DisplayName("toString is non-null and non-empty")
    void toStringNonNull() {
        BaremeCotisationRole bareme = BaremeCotisationRole.builder()
                .roleOrg("MEMBRE_ORDINAIRE")
                .build();
        assertThat(bareme.toString()).isNotNull().isNotEmpty();
    }

    // -------------------------------------------------------------------------
    // Representative role values (no enum — plain String column)
    // -------------------------------------------------------------------------

    @Test
    @DisplayName("all representative role values can be stored and retrieved")
    void representativeRoleValues() {
        String[] roles = {
            "PRESIDENT", "VICE_PRESIDENT", "TRESORIER", "TRESORIER_ADJOINT",
            "SECRETAIRE", "SECRETAIRE_ADJOINT", "MEMBRE_ORDINAIRE", "AUDITEUR"
        };

        for (String role : roles) {
            BaremeCotisationRole bareme = BaremeCotisationRole.builder()
                    .roleOrg(role)
                    .build();
            assertThat(bareme.getRoleOrg()).as("role: %s", role).isEqualTo(role);
        }
    }
}
