package dev.lions.unionflow.server.service;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import dev.lions.unionflow.server.api.dto.finance_workflow.request.CreateBudgetLineRequest;
import dev.lions.unionflow.server.api.dto.finance_workflow.request.CreateBudgetRequest;
import dev.lions.unionflow.server.api.dto.finance_workflow.response.BudgetLineResponse;
import dev.lions.unionflow.server.api.dto.finance_workflow.response.BudgetResponse;
import dev.lions.unionflow.server.entity.Budget;
import dev.lions.unionflow.server.entity.BudgetLine;
import dev.lions.unionflow.server.entity.Organisation;
import dev.lions.unionflow.server.repository.BudgetRepository;
import dev.lions.unionflow.server.repository.OrganisationRepository;
import io.quarkus.test.InjectMock;
import io.quarkus.test.junit.QuarkusTest;
import jakarta.inject.Inject;
import jakarta.ws.rs.BadRequestException;
import jakarta.ws.rs.NotFoundException;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import org.eclipse.microprofile.jwt.JsonWebToken;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

@QuarkusTest
class BudgetServiceTest {

    @Inject
    BudgetService budgetService;

    @InjectMock
    BudgetRepository budgetRepository;

    @InjectMock
    OrganisationRepository organisationRepository;

    @InjectMock
    JsonWebToken jwt;

    private Organisation org;
    private final UUID ORG_ID = UUID.randomUUID();
    private final UUID USER_ID = UUID.randomUUID();
    private final UUID BUDGET_ID = UUID.randomUUID();

    @BeforeEach
    void setup() {
        org = new Organisation();
        org.setId(ORG_ID);
        when(jwt.<String>getClaim("sub")).thenReturn(USER_ID.toString());
    }

    // ─── helpers ────────────────────────────────────────────────────────────────

    private Budget buildBudget(String period, Integer month) {
        Budget b = Budget.builder()
                .name("Test Budget")
                .description("Desc")
                .organisation(org)
                .period(period)
                .year(2026)
                .month(month)
                .status("DRAFT")
                .currency("XOF")
                .createdById(USER_ID)
                .createdAtBudget(LocalDateTime.now())
                .startDate(LocalDate.of(2026, 1, 1))
                .endDate(LocalDate.of(2026, 12, 31))
                .build();
        b.setId(BUDGET_ID);
        return b;
    }

    private Budget buildAnnualBudget() {
        return buildBudget("ANNUAL", null);
    }

    // ─── getBudgets ──────────────────────────────────────────────────────────────

    @Test
    @DisplayName("getBudgets_nullOrgId_throwsBadRequest")
    void getBudgets_nullOrgId_throwsBadRequest() {
        assertThatThrownBy(() -> budgetService.getBudgets(null, null, null))
                .isInstanceOf(BadRequestException.class);
    }

    @Test
    @DisplayName("getBudgets_withAllFilters_returnsList")
    void getBudgets_withAllFilters_returnsList() {
        Budget b = buildAnnualBudget();
        when(budgetRepository.findByOrganisationWithFilters(ORG_ID, "ACTIVE", 2026))
                .thenReturn(List.of(b));

        List<BudgetResponse> result = budgetService.getBudgets(ORG_ID, "ACTIVE", 2026);

        assertThat(result).hasSize(1);
        assertThat(result.get(0).getName()).isEqualTo("Test Budget");
        verify(budgetRepository).findByOrganisationWithFilters(ORG_ID, "ACTIVE", 2026);
    }

    @Test
    @DisplayName("getBudgets_withNullFilters_returnsList")
    void getBudgets_withNullFilters_returnsList() {
        when(budgetRepository.findByOrganisationWithFilters(ORG_ID, null, null))
                .thenReturn(Collections.emptyList());

        List<BudgetResponse> result = budgetService.getBudgets(ORG_ID, null, null);

        assertThat(result).isEmpty();
        verify(budgetRepository).findByOrganisationWithFilters(ORG_ID, null, null);
    }

    // ─── getBudgetById ───────────────────────────────────────────────────────────

    @Test
    @DisplayName("getBudgetById_notFound_throwsNotFound")
    void getBudgetById_notFound_throwsNotFound() {
        when(budgetRepository.findByIdOptional(any())).thenReturn(Optional.empty());

        assertThatThrownBy(() -> budgetService.getBudgetById(BUDGET_ID))
                .isInstanceOf(NotFoundException.class);
    }

    @Test
    @DisplayName("getBudgetById_found_returnsDto")
    void getBudgetById_found_returnsDto() {
        Budget b = buildAnnualBudget();
        when(budgetRepository.findByIdOptional(BUDGET_ID)).thenReturn(Optional.of(b));

        BudgetResponse result = budgetService.getBudgetById(BUDGET_ID);

        assertThat(result).isNotNull();
        assertThat(result.getId()).isEqualTo(BUDGET_ID);
        assertThat(result.getName()).isEqualTo("Test Budget");
    }

    // ─── createBudget ────────────────────────────────────────────────────────────

    @Test
    @DisplayName("createBudget_orgNotFound_throwsNotFound")
    void createBudget_orgNotFound_throwsNotFound() {
        when(organisationRepository.findByIdOptional(any())).thenReturn(Optional.empty());

        CreateBudgetRequest req = CreateBudgetRequest.builder()
                .name("Budget")
                .organizationId(ORG_ID)
                .period("ANNUAL")
                .year(2026)
                .build();

        assertThatThrownBy(() -> budgetService.createBudget(req))
                .isInstanceOf(NotFoundException.class);
    }

    @Test
    @DisplayName("createBudget_monthlyWithoutMonth_throwsBadRequest")
    void createBudget_monthlyWithoutMonth_throwsBadRequest() {
        when(organisationRepository.findByIdOptional(ORG_ID)).thenReturn(Optional.of(org));

        CreateBudgetRequest req = CreateBudgetRequest.builder()
                .name("Budget Mensuel")
                .organizationId(ORG_ID)
                .period("MONTHLY")
                .year(2026)
                .month(null)
                .build();

        assertThatThrownBy(() -> budgetService.createBudget(req))
                .isInstanceOf(BadRequestException.class);
    }

    @Test
    @DisplayName("createBudget_monthly_success")
    void createBudget_monthly_success() {
        when(organisationRepository.findByIdOptional(ORG_ID)).thenReturn(Optional.of(org));

        CreateBudgetRequest req = CreateBudgetRequest.builder()
                .name("Budget Mars")
                .organizationId(ORG_ID)
                .period("MONTHLY")
                .year(2026)
                .month(3)
                .currency("XOF")
                .build();

        BudgetResponse result = budgetService.createBudget(req);

        assertThat(result).isNotNull();
        assertThat(result.getPeriod()).isEqualTo("MONTHLY");
        assertThat(result.getStartDate()).isEqualTo(LocalDate.of(2026, 3, 1));
        assertThat(result.getEndDate()).isEqualTo(LocalDate.of(2026, 3, 31));
        verify(budgetRepository).persist(any(Budget.class));
    }

    @Test
    @DisplayName("createBudget_quarterly_success")
    void createBudget_quarterly_success() {
        when(organisationRepository.findByIdOptional(ORG_ID)).thenReturn(Optional.of(org));

        CreateBudgetRequest req = CreateBudgetRequest.builder()
                .name("Budget Q1")
                .organizationId(ORG_ID)
                .period("QUARTERLY")
                .year(2026)
                .currency("XOF")
                .build();

        BudgetResponse result = budgetService.createBudget(req);

        assertThat(result).isNotNull();
        assertThat(result.getPeriod()).isEqualTo("QUARTERLY");
        assertThat(result.getStartDate()).isEqualTo(LocalDate.of(2026, 1, 1));
        assertThat(result.getEndDate()).isEqualTo(LocalDate.of(2026, 3, 31));
        verify(budgetRepository).persist(any(Budget.class));
    }

    @Test
    @DisplayName("createBudget_semiannual_success")
    void createBudget_semiannual_success() {
        when(organisationRepository.findByIdOptional(ORG_ID)).thenReturn(Optional.of(org));

        CreateBudgetRequest req = CreateBudgetRequest.builder()
                .name("Budget S1")
                .organizationId(ORG_ID)
                .period("SEMIANNUAL")
                .year(2026)
                .currency("XOF")
                .build();

        BudgetResponse result = budgetService.createBudget(req);

        assertThat(result).isNotNull();
        assertThat(result.getPeriod()).isEqualTo("SEMIANNUAL");
        assertThat(result.getStartDate()).isEqualTo(LocalDate.of(2026, 1, 1));
        assertThat(result.getEndDate()).isEqualTo(LocalDate.of(2026, 6, 30));
        verify(budgetRepository).persist(any(Budget.class));
    }

    @Test
    @DisplayName("createBudget_annual_success")
    void createBudget_annual_success() {
        when(organisationRepository.findByIdOptional(ORG_ID)).thenReturn(Optional.of(org));

        CreateBudgetRequest req = CreateBudgetRequest.builder()
                .name("Budget Annuel")
                .organizationId(ORG_ID)
                .period("ANNUAL")
                .year(2026)
                .currency("XOF")
                .build();

        BudgetResponse result = budgetService.createBudget(req);

        assertThat(result).isNotNull();
        assertThat(result.getPeriod()).isEqualTo("ANNUAL");
        assertThat(result.getStartDate()).isEqualTo(LocalDate.of(2026, 1, 1));
        assertThat(result.getEndDate()).isEqualTo(LocalDate.of(2026, 12, 31));
        verify(budgetRepository).persist(any(Budget.class));
    }

    @Test
    @DisplayName("createBudget_defaultPeriod_success")
    void createBudget_defaultPeriod_success() {
        when(organisationRepository.findByIdOptional(ORG_ID)).thenReturn(Optional.of(org));

        CreateBudgetRequest req = CreateBudgetRequest.builder()
                .name("Budget Other")
                .organizationId(ORG_ID)
                .period("OTHER")
                .year(2026)
                .currency("XOF")
                .build();

        BudgetResponse result = budgetService.createBudget(req);

        assertThat(result).isNotNull();
        assertThat(result.getStartDate()).isEqualTo(LocalDate.of(2026, 1, 1));
        assertThat(result.getEndDate()).isEqualTo(LocalDate.of(2026, 12, 31));
        verify(budgetRepository).persist(any(Budget.class));
    }

    @Test
    @DisplayName("createBudget_withLines_success")
    void createBudget_withLines_success() {
        when(organisationRepository.findByIdOptional(ORG_ID)).thenReturn(Optional.of(org));

        CreateBudgetLineRequest line1 = CreateBudgetLineRequest.builder()
                .category("CONTRIBUTIONS")
                .name("Cotisations membres")
                .description("Cotisations annuelles")
                .amountPlanned(BigDecimal.valueOf(1_000_000))
                .notes("Prévision conservatrice")
                .build();

        CreateBudgetLineRequest line2 = CreateBudgetLineRequest.builder()
                .category("OPERATIONAL")
                .name("Frais bureau")
                .amountPlanned(BigDecimal.valueOf(200_000))
                .build();

        List<CreateBudgetLineRequest> lines = new ArrayList<>();
        lines.add(line1);
        lines.add(line2);

        CreateBudgetRequest req = CreateBudgetRequest.builder()
                .name("Budget avec lignes")
                .organizationId(ORG_ID)
                .period("ANNUAL")
                .year(2026)
                .currency("XOF")
                .lines(lines)
                .build();

        BudgetResponse result = budgetService.createBudget(req);

        assertThat(result).isNotNull();
        assertThat(result.getLines()).hasSize(2);
        verify(budgetRepository).persist(any(Budget.class));
    }

    // ─── getBudgetTracking ───────────────────────────────────────────────────────

    @Test
    @DisplayName("getBudgetTracking_notFound_throwsNotFound")
    void getBudgetTracking_notFound_throwsNotFound() {
        when(budgetRepository.findByIdOptional(any())).thenReturn(Optional.empty());

        assertThatThrownBy(() -> budgetService.getBudgetTracking(BUDGET_ID))
                .isInstanceOf(NotFoundException.class);
    }

    @Test
    @DisplayName("getBudgetTracking_withLines_returnsTracking")
    void getBudgetTracking_withLines_returnsTracking() {
        Budget b = buildAnnualBudget();

        BudgetLine line = BudgetLine.builder()
                .budget(b)
                .category("CONTRIBUTIONS")
                .name("Cotisations")
                .amountPlanned(BigDecimal.valueOf(500_000))
                .amountRealized(BigDecimal.valueOf(300_000))
                .build();
        line.setId(UUID.randomUUID());

        b.addLine(line);

        when(budgetRepository.findByIdOptional(BUDGET_ID)).thenReturn(Optional.of(b));

        Map<String, Object> tracking = budgetService.getBudgetTracking(BUDGET_ID);

        assertThat(tracking).isNotNull();
        assertThat(tracking).containsKey("budgetId");
        assertThat(tracking).containsKey("totalPlanned");
        assertThat(tracking).containsKey("totalRealized");
        assertThat(tracking).containsKey("realizationRate");
        assertThat(tracking).containsKey("variance");
        assertThat(tracking).containsKey("byCategory");
        assertThat(tracking).containsKey("topVariances");

        @SuppressWarnings("unchecked")
        Map<String, Map<String, Object>> byCategory =
                (Map<String, Map<String, Object>>) tracking.get("byCategory");
        assertThat(byCategory).containsKey("CONTRIBUTIONS");

        @SuppressWarnings("unchecked")
        List<Map<String, Object>> topVariances =
                (List<Map<String, Object>>) tracking.get("topVariances");
        assertThat(topVariances).hasSize(1);
    }

    @Test
    @DisplayName("getBudgetTracking_emptyLines_returnsTracking")
    void getBudgetTracking_emptyLines_returnsTracking() {
        Budget b = buildAnnualBudget();
        when(budgetRepository.findByIdOptional(BUDGET_ID)).thenReturn(Optional.of(b));

        Map<String, Object> tracking = budgetService.getBudgetTracking(BUDGET_ID);

        assertThat(tracking).isNotNull();
        assertThat(tracking).containsKey("topVariances");

        @SuppressWarnings("unchecked")
        List<Map<String, Object>> topVariances =
                (List<Map<String, Object>>) tracking.get("topVariances");
        assertThat(topVariances).isEmpty();
    }

    // ─── updateBudget ────────────────────────────────────────────────────────────

    @Test
    @DisplayName("updateBudget_notFound_throwsNotFound")
    void updateBudget_notFound_throwsNotFound() {
        when(budgetRepository.findByIdOptional(any())).thenReturn(Optional.empty());

        assertThatThrownBy(() -> budgetService.updateBudget(BUDGET_ID, Map.of("name", "Nouveau Nom")))
                .isInstanceOf(NotFoundException.class);
    }

    @Test
    @DisplayName("updateBudget_invalidStatus_throwsBadRequest")
    void updateBudget_invalidStatus_throwsBadRequest() {
        Budget b = buildAnnualBudget();
        when(budgetRepository.findByIdOptional(BUDGET_ID)).thenReturn(Optional.of(b));

        assertThatThrownBy(() -> budgetService.updateBudget(BUDGET_ID, Map.of("status", "INVALID")))
                .isInstanceOf(BadRequestException.class);
    }

    @Test
    @DisplayName("updateBudget_updateName_success")
    void updateBudget_updateName_success() {
        Budget b = buildAnnualBudget();
        when(budgetRepository.findByIdOptional(BUDGET_ID)).thenReturn(Optional.of(b));

        BudgetResponse result = budgetService.updateBudget(BUDGET_ID, Map.of("name", "Nouveau Nom"));

        assertThat(result).isNotNull();
        assertThat(result.getName()).isEqualTo("Nouveau Nom");
        verify(budgetRepository).persist(any(Budget.class));
    }

    @Test
    @DisplayName("updateBudget_updateDescription_success")
    void updateBudget_updateDescription_success() {
        Budget b = buildAnnualBudget();
        when(budgetRepository.findByIdOptional(BUDGET_ID)).thenReturn(Optional.of(b));

        BudgetResponse result = budgetService.updateBudget(BUDGET_ID, Map.of("description", "Nouvelle description"));

        assertThat(result).isNotNull();
        assertThat(result.getDescription()).isEqualTo("Nouvelle description");
        verify(budgetRepository).persist(any(Budget.class));
    }

    @Test
    @DisplayName("updateBudget_statusActive_setsApprovalDate")
    void updateBudget_statusActive_setsApprovalDate() {
        Budget b = buildAnnualBudget();
        // approvedAt == null → doit être défini lors de l'activation
        assertThat(b.getApprovedAt()).isNull();
        when(budgetRepository.findByIdOptional(BUDGET_ID)).thenReturn(Optional.of(b));

        BudgetResponse result = budgetService.updateBudget(BUDGET_ID, Map.of("status", "ACTIVE"));

        assertThat(result).isNotNull();
        assertThat(result.getStatus()).isEqualTo("ACTIVE");
        assertThat(result.getApprovedAt()).isNotNull();
        assertThat(result.getApprovedById()).isEqualTo(USER_ID);
        verify(budgetRepository).persist(any(Budget.class));
    }

    @Test
    @DisplayName("updateBudget_statusActiveAlreadyApproved_noDateChange")
    void updateBudget_statusActiveAlreadyApproved_noDateChange() {
        Budget b = buildAnnualBudget();
        LocalDateTime existingApproval = LocalDateTime.of(2026, 1, 10, 12, 0);
        b.setApprovedAt(existingApproval);
        b.setApprovedById(UUID.randomUUID());
        when(budgetRepository.findByIdOptional(BUDGET_ID)).thenReturn(Optional.of(b));

        BudgetResponse result = budgetService.updateBudget(BUDGET_ID, Map.of("status", "ACTIVE"));

        assertThat(result.getApprovedAt()).isEqualTo(existingApproval);
        verify(budgetRepository).persist(any(Budget.class));
    }

    @Test
    @DisplayName("updateBudget_statusDraft_success")
    void updateBudget_statusDraft_success() {
        Budget b = buildAnnualBudget();
        b.setStatus("ACTIVE");
        when(budgetRepository.findByIdOptional(BUDGET_ID)).thenReturn(Optional.of(b));

        BudgetResponse result = budgetService.updateBudget(BUDGET_ID, Map.of("status", "DRAFT"));

        assertThat(result).isNotNull();
        assertThat(result.getStatus()).isEqualTo("DRAFT");
        verify(budgetRepository).persist(any(Budget.class));
    }

    // ─── deleteBudget ────────────────────────────────────────────────────────────

    @Test
    @DisplayName("updateBudget_statusClosed_success")
    void updateBudget_statusClosed_success() {
        Budget b = buildAnnualBudget();
        b.setStatus("ACTIVE");
        when(budgetRepository.findByIdOptional(BUDGET_ID)).thenReturn(Optional.of(b));

        BudgetResponse result = budgetService.updateBudget(BUDGET_ID, Map.of("status", "CLOSED"));

        assertThat(result).isNotNull();
        assertThat(result.getStatus()).isEqualTo("CLOSED");
        verify(budgetRepository).persist(any(Budget.class));
    }

    @Test
    @DisplayName("updateBudget_statusArchived_success")
    void updateBudget_statusArchived_success() {
        Budget b = buildAnnualBudget();
        b.setStatus("ACTIVE");
        when(budgetRepository.findByIdOptional(BUDGET_ID)).thenReturn(Optional.of(b));

        BudgetResponse result = budgetService.updateBudget(BUDGET_ID, Map.of("status", "ARCHIVED"));

        assertThat(result).isNotNull();
        assertThat(result.getStatus()).isEqualTo("ARCHIVED");
        verify(budgetRepository).persist(any(Budget.class));
    }

    @Test
    @DisplayName("createBudget_withLines_lineResponseLambdaExecuted")
    void createBudget_withLines_linesResponseContainsCalculatedFields() {
        when(organisationRepository.findByIdOptional(ORG_ID)).thenReturn(Optional.of(org));

        CreateBudgetLineRequest line = CreateBudgetLineRequest.builder()
                .category("CONTRIBUTIONS")
                .name("Cotisations membres")
                .description("Cotisations annuelles")
                .amountPlanned(BigDecimal.valueOf(500_000))
                .notes("Note")
                .build();

        CreateBudgetRequest req = CreateBudgetRequest.builder()
                .name("Budget Annuel Lignes")
                .organizationId(ORG_ID)
                .period("ANNUAL")
                .year(2026)
                .currency("XOF")
                .lines(List.of(line))
                .build();

        BudgetResponse result = budgetService.createBudget(req);

        assertThat(result).isNotNull();
        assertThat(result.getLines()).hasSize(1);
        // Vérifie que les champs calculés du lambda sont bien présents
        BudgetLineResponse lineResponse = result.getLines().get(0);
        assertThat(lineResponse.getCategory()).isEqualTo("CONTRIBUTIONS");
        assertThat(lineResponse.getName()).isEqualTo("Cotisations membres");
        assertThat(lineResponse.getAmountPlanned()).isEqualByComparingTo(BigDecimal.valueOf(500_000));
        assertThat(lineResponse.getVariance()).isNotNull();
        assertThat(lineResponse.getRealizationRate()).isNotNull();
        assertThat(lineResponse).isNotNull(); // isOverBudget est dans le builder
    }

    @Test
    @DisplayName("getBudgetTracking_withMultipleCategories_aggregatesCorrectly")
    void getBudgetTracking_withMultipleCategories_aggregatesCorrectly() {
        Budget b = buildAnnualBudget();

        BudgetLine line1 = BudgetLine.builder()
                .budget(b)
                .category("CONTRIBUTIONS")
                .name("Cotisations")
                .amountPlanned(BigDecimal.valueOf(500_000))
                .amountRealized(BigDecimal.valueOf(400_000))
                .build();
        line1.setId(UUID.randomUUID());

        BudgetLine line2 = BudgetLine.builder()
                .budget(b)
                .category("CONTRIBUTIONS") // même catégorie → agrégation dans byCategory
                .name("Cotisations extras")
                .amountPlanned(BigDecimal.valueOf(200_000))
                .amountRealized(BigDecimal.valueOf(100_000))
                .build();
        line2.setId(UUID.randomUUID());

        BudgetLine line3 = BudgetLine.builder()
                .budget(b)
                .category("OPERATIONAL")
                .name("Frais")
                .amountPlanned(BigDecimal.valueOf(300_000))
                .amountRealized(BigDecimal.valueOf(350_000)) // dépasse le budget
                .build();
        line3.setId(UUID.randomUUID());

        b.addLine(line1);
        b.addLine(line2);
        b.addLine(line3);

        when(budgetRepository.findByIdOptional(BUDGET_ID)).thenReturn(Optional.of(b));

        Map<String, Object> tracking = budgetService.getBudgetTracking(BUDGET_ID);

        assertThat(tracking).isNotNull();

        @SuppressWarnings("unchecked")
        Map<String, Map<String, Object>> byCategory =
                (Map<String, Map<String, Object>>) tracking.get("byCategory");
        // Deux catégories : CONTRIBUTIONS (agrégée) et OPERATIONAL
        assertThat(byCategory).containsKey("CONTRIBUTIONS");
        assertThat(byCategory).containsKey("OPERATIONAL");

        // Vérifier l'agrégation de CONTRIBUTIONS (500k + 200k = 700k planned)
        Map<String, Object> contribData = byCategory.get("CONTRIBUTIONS");
        assertThat((BigDecimal) contribData.get("planned"))
                .isEqualByComparingTo(BigDecimal.valueOf(700_000));

        @SuppressWarnings("unchecked")
        List<Map<String, Object>> topVariances =
                (List<Map<String, Object>>) tracking.get("topVariances");
        // 3 lignes → top 5 → toutes les 3
        assertThat(topVariances).hasSize(3);
    }

    @Test
    @DisplayName("deleteBudget_notFound_throwsNotFound")
    void deleteBudget_notFound_throwsNotFound() {
        when(budgetRepository.findByIdOptional(any())).thenReturn(Optional.empty());

        assertThatThrownBy(() -> budgetService.deleteBudget(BUDGET_ID))
                .isInstanceOf(NotFoundException.class);
    }

    @Test
    @DisplayName("deleteBudget_success_softDeletes")
    void deleteBudget_success_softDeletes() {
        Budget b = buildAnnualBudget();
        b.setActif(true);
        when(budgetRepository.findByIdOptional(BUDGET_ID)).thenReturn(Optional.of(b));

        budgetService.deleteBudget(BUDGET_ID);

        assertThat(b.getActif()).isFalse();
        verify(budgetRepository).persist(b);
    }
}
