package dev.lions.unionflow.server.entity;

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

import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.UUID;

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

@DisplayName("BudgetLine")
class BudgetLineTest {

    private static Budget newBudget() {
        Budget b = new Budget();
        b.setId(UUID.randomUUID());
        return b;
    }

    // -------------------------------------------------------------------------
    // getRealizationRate
    // -------------------------------------------------------------------------

    @Test
    @DisplayName("getRealizationRate: amountPlanned == 0 renvoie 0.0")
    void getRealizationRate_zeroPlan_returns0() {
        BudgetLine line = new BudgetLine();
        line.setAmountPlanned(BigDecimal.ZERO);
        line.setAmountRealized(new BigDecimal("100.00"));
        assertThat(line.getRealizationRate()).isEqualTo(0.0);
    }

    @Test
    @DisplayName("getRealizationRate: 75 réalisé / 100 prévu = 75%")
    void getRealizationRate_withValues_returnsRatio() {
        BudgetLine line = new BudgetLine();
        line.setAmountPlanned(new BigDecimal("100.00"));
        line.setAmountRealized(new BigDecimal("75.00"));
        assertThat(line.getRealizationRate()).isEqualTo(75.0);
    }

    // -------------------------------------------------------------------------
    // getVariance
    // -------------------------------------------------------------------------

    @Test
    @DisplayName("getVariance: positif quand réalisé > prévu")
    void getVariance_positive_whenOver() {
        BudgetLine line = new BudgetLine();
        line.setAmountPlanned(new BigDecimal("100.00"));
        line.setAmountRealized(new BigDecimal("120.00"));
        assertThat(line.getVariance()).isEqualByComparingTo("20.00");
    }

    @Test
    @DisplayName("getVariance: négatif quand réalisé < prévu")
    void getVariance_negative_whenUnder() {
        BudgetLine line = new BudgetLine();
        line.setAmountPlanned(new BigDecimal("100.00"));
        line.setAmountRealized(new BigDecimal("80.00"));
        assertThat(line.getVariance()).isEqualByComparingTo("-20.00");
    }

    // -------------------------------------------------------------------------
    // isOverBudget
    // -------------------------------------------------------------------------

    @Test
    @DisplayName("isOverBudget: réalisé > prévu renvoie true")
    void isOverBudget_whenOver_returnsTrue() {
        BudgetLine line = new BudgetLine();
        line.setAmountPlanned(new BigDecimal("200.00"));
        line.setAmountRealized(new BigDecimal("201.00"));
        assertThat(line.isOverBudget()).isTrue();
    }

    @Test
    @DisplayName("isOverBudget: réalisé <= prévu renvoie false")
    void isOverBudget_whenNotOver_returnsFalse() {
        BudgetLine line = new BudgetLine();
        line.setAmountPlanned(new BigDecimal("200.00"));
        line.setAmountRealized(new BigDecimal("200.00"));
        assertThat(line.isOverBudget()).isFalse();
    }

    // -------------------------------------------------------------------------
    // onCreate (réflexion)
    // -------------------------------------------------------------------------

    @Test
    @DisplayName("onCreate: initialise amountRealized à ZERO si null")
    void onCreate_initializesAmountRealizedIfNull() throws Exception {
        BudgetLine line = new BudgetLine();
        line.setAmountRealized(null);

        Method onCreate = BudgetLine.class.getDeclaredMethod("onCreate");
        onCreate.setAccessible(true);
        onCreate.invoke(line);

        assertThat(line.getAmountRealized()).isEqualByComparingTo(BigDecimal.ZERO);
    }

    @Test
    @DisplayName("onCreate: ne remplace pas amountRealized si déjà renseigné")
    void onCreate_doesNotOverrideAmountRealized() throws Exception {
        BudgetLine line = new BudgetLine();
        line.setAmountRealized(new BigDecimal("350.00"));

        Method onCreate = BudgetLine.class.getDeclaredMethod("onCreate");
        onCreate.setAccessible(true);
        onCreate.invoke(line);

        assertThat(line.getAmountRealized()).isEqualByComparingTo("350.00");
    }

    // -------------------------------------------------------------------------
    // builder
    // -------------------------------------------------------------------------

    @Test
    @DisplayName("builder: positionne tous les champs correctement")
    void builder_setsAllFields() {
        Budget budget = newBudget();

        BudgetLine line = BudgetLine.builder()
            .budget(budget)
            .category("CONTRIBUTIONS")
            .name("Cotisations membres")
            .description("Cotisations mensuelles")
            .amountPlanned(new BigDecimal("50000.00"))
            .amountRealized(new BigDecimal("42000.00"))
            .notes("Léger retard de collecte")
            .build();

        assertThat(line.getBudget()).isSameAs(budget);
        assertThat(line.getCategory()).isEqualTo("CONTRIBUTIONS");
        assertThat(line.getName()).isEqualTo("Cotisations membres");
        assertThat(line.getDescription()).isEqualTo("Cotisations mensuelles");
        assertThat(line.getAmountPlanned()).isEqualByComparingTo("50000.00");
        assertThat(line.getAmountRealized()).isEqualByComparingTo("42000.00");
        assertThat(line.getNotes()).isEqualTo("Léger retard de collecte");
    }
}
