package dev.lions.unionflow.server.repository;

import dev.lions.unionflow.server.entity.Budget;
import dev.lions.unionflow.server.entity.Organisation;
import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.TestTransaction;
import jakarta.inject.Inject;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

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

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

@QuarkusTest
class BudgetRepositoryTest {

    @Inject
    BudgetRepository budgetRepository;

    @Inject
    OrganisationRepository organisationRepository;

    private Organisation newOrganisation() {
        Organisation o = new Organisation();
        o.setNom("Org Budget " + UUID.randomUUID());
        o.setTypeOrganisation("ASSOCIATION");
        o.setStatut("ACTIVE");
        o.setEmail("budget-org-" + UUID.randomUUID() + "@test.com");
        o.setActif(true);
        organisationRepository.persist(o);
        return o;
    }

    private Budget newBudget(Organisation org, String status, int year) {
        return Budget.builder()
                .name("Budget test " + UUID.randomUUID())
                .organisation(org)
                .period("ANNUAL")
                .year(year)
                .status(status)
                .totalPlanned(BigDecimal.valueOf(100000))
                .totalRealized(BigDecimal.ZERO)
                .currency("XOF")
                .createdById(UUID.randomUUID())
                .createdAtBudget(LocalDateTime.now())
                .startDate(LocalDate.of(year, 1, 1))
                .endDate(LocalDate.of(year, 12, 31))
                .build();
    }

    @Test
    @TestTransaction
    @DisplayName("findByOrganisationWithFilters sans status ni year retourne tous les budgets de l'org")
    void findByOrganisationWithFilters_noStatusNoYear_returnsList() {
        Organisation org = newOrganisation();
        Budget b = newBudget(org, "ACTIVE", 2026);
        budgetRepository.persist(b);

        List<Budget> list = budgetRepository.findByOrganisationWithFilters(org.getId(), null, null);
        assertThat(list).isNotNull();
        assertThat(list).isNotEmpty();
    }

    @Test
    @TestTransaction
    @DisplayName("findByOrganisationWithFilters avec status seulement couvre la branche status != null et year == null")
    void findByOrganisationWithFilters_statusOnly_returnsList() {
        Organisation org = newOrganisation();
        Budget b = newBudget(org, "ACTIVE", 2026);
        budgetRepository.persist(b);

        List<Budget> list = budgetRepository.findByOrganisationWithFilters(org.getId(), "ACTIVE", null);
        assertThat(list).isNotNull();
        assertThat(list).isNotEmpty();
    }

    @Test
    @TestTransaction
    @DisplayName("findByOrganisationWithFilters avec year seulement couvre la branche status == null et year != null")
    void findByOrganisationWithFilters_yearOnly_returnsList() {
        Organisation org = newOrganisation();
        Budget b = newBudget(org, "DRAFT", 2026);
        budgetRepository.persist(b);

        List<Budget> list = budgetRepository.findByOrganisationWithFilters(org.getId(), null, 2026);
        assertThat(list).isNotNull();
        assertThat(list).isNotEmpty();
    }

    @Test
    @TestTransaction
    @DisplayName("findByOrganisationWithFilters avec status et year couvre les deux branches true")
    void findByOrganisationWithFilters_statusAndYear_returnsList() {
        Organisation org = newOrganisation();
        Budget b = newBudget(org, "ACTIVE", 2026);
        budgetRepository.persist(b);

        List<Budget> list = budgetRepository.findByOrganisationWithFilters(org.getId(), "ACTIVE", 2026);
        assertThat(list).isNotNull();
        assertThat(list).isNotEmpty();
    }

    // ── Branch coverage manquantes ─────────────────────────────────────────

    /**
     * L55 + L67 branch manquante : status != null mais isEmpty() → false
     * → `if (status != null && !status.isEmpty())` → false (status est "")
     */
    @Test
    @TestTransaction
    @DisplayName("findByOrganisationWithFilters avec status vide (empty) couvre la branche isEmpty")
    void findByOrganisationWithFilters_emptyStatus_treatedAsNoFilter() {
        Organisation org = newOrganisation();
        Budget b = newBudget(org, "ACTIVE", 2026);
        budgetRepository.persist(b);

        // status = "" → !status.isEmpty() est false → branche false → pas filtré par status
        List<Budget> list = budgetRepository.findByOrganisationWithFilters(org.getId(), "", null);
        assertThat(list).isNotNull();
        assertThat(list).isNotEmpty();
    }

    @Test
    @TestTransaction
    @DisplayName("findByOrganisation retourne les budgets de l'organisation")
    void findByOrganisation_returnsList() {
        Organisation org = newOrganisation();
        Budget b = newBudget(org, "ACTIVE", 2026);
        budgetRepository.persist(b);

        List<Budget> list = budgetRepository.findByOrganisation(org.getId());
        assertThat(list).isNotNull();
        assertThat(list).isNotEmpty();
    }

    @Test
    @TestTransaction
    @DisplayName("findActiveByOrganisation retourne les budgets actifs")
    void findActiveByOrganisation_returnsList() {
        Organisation org = newOrganisation();
        Budget b = newBudget(org, "ACTIVE", 2026);
        budgetRepository.persist(b);

        List<Budget> list = budgetRepository.findActiveByOrganisation(org.getId());
        assertThat(list).isNotNull();
        assertThat(list).isNotEmpty();
    }

    @Test
    @TestTransaction
    @DisplayName("countActiveByOrganisation retourne un nombre >= 0")
    void countActiveByOrganisation_returnsNonNegative() {
        long count = budgetRepository.countActiveByOrganisation(UUID.randomUUID());
        assertThat(count).isGreaterThanOrEqualTo(0L);
    }

    @Test
    @TestTransaction
    @DisplayName("findByOrganisationAndYear retourne les budgets de l'organisation pour l'année donnée")
    void findByOrganisationAndYear_returnsList() {
        Organisation org = newOrganisation();
        Budget b = newBudget(org, "ACTIVE", 2026);
        budgetRepository.persist(b);

        List<Budget> list = budgetRepository.findByOrganisationAndYear(org.getId(), 2026);
        assertThat(list).isNotNull();
        assertThat(list).isNotEmpty();
    }

    @Test
    @TestTransaction
    @DisplayName("findByOrganisationAndYear avec année sans budget → retourne liste vide")
    void findByOrganisationAndYear_wrongYear_returnsEmpty() {
        Organisation org = newOrganisation();
        Budget b = newBudget(org, "ACTIVE", 2025);
        budgetRepository.persist(b);

        List<Budget> list = budgetRepository.findByOrganisationAndYear(org.getId(), 9999);
        assertThat(list).isNotNull();
        assertThat(list).isEmpty();
    }
}
