package dev.lions.unionflow.server.service;

import dev.lions.unionflow.server.entity.Organisation;
import dev.lions.unionflow.server.repository.OrganisationRepository;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import java.util.Optional;
import java.util.Set;
import java.util.UUID;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
class OrganisationModuleServiceTest {

    @InjectMocks
    OrganisationModuleService service;

    @Mock
    OrganisationRepository organisationRepository;

    // ── getModulesActifs(UUID) ───────────────────────────────────────────────────

    @Test
    void getModulesActifs_whenOrgNotFound_returnsModulesCommuns() {
        UUID id = UUID.randomUUID();
        when(organisationRepository.findByIdOptional(id)).thenReturn(Optional.empty());

        Set<String> modules = service.getModulesActifs(id);

        assertThat(modules).containsAll(OrganisationModuleService.MODULES_COMMUNS);
    }

    @Test
    void getModulesActifs_whenOrgHasModulesActifsField_returnsThem() {
        UUID id = UUID.randomUUID();
        Organisation org = new Organisation();
        org.setModulesActifs("TONTINE,FINANCE,CUSTOM_MODULE");
        when(organisationRepository.findByIdOptional(id)).thenReturn(Optional.of(org));

        Set<String> modules = service.getModulesActifs(id);

        assertThat(modules).containsAll(OrganisationModuleService.MODULES_COMMUNS);
        assertThat(modules).contains("TONTINE", "FINANCE", "CUSTOM_MODULE");
    }

    @Test
    void getModulesActifs_whenOrgModulesActifsBlank_fallsBackToTypeMapping() {
        UUID id = UUID.randomUUID();
        Organisation org = new Organisation();
        org.setModulesActifs("   ");
        org.setTypeOrganisation("TONTINE");
        when(organisationRepository.findByIdOptional(id)).thenReturn(Optional.of(org));

        Set<String> modules = service.getModulesActifs(id);

        assertThat(modules).contains("TONTINE", "FINANCE");
    }

    @Test
    void getModulesActifs_whenOrgModulesActifsNull_fallsBackToTypeMapping() {
        UUID id = UUID.randomUUID();
        Organisation org = new Organisation();
        org.setModulesActifs(null);
        org.setTypeOrganisation("MUTUELLE");
        when(organisationRepository.findByIdOptional(id)).thenReturn(Optional.of(org));

        Set<String> modules = service.getModulesActifs(id);

        assertThat(modules).contains("EPARGNE", "CREDIT", "FINANCE", "LCB_FT");
    }

    // ── getModulesActifs(Organisation) ──────────────────────────────────────────

    @Test
    void getModulesActifs_org_withModulesActifs_returnsParsedModules() {
        Organisation org = new Organisation();
        org.setModulesActifs("TONTINE, FINANCE ");

        Set<String> modules = service.getModulesActifs(org);

        assertThat(modules).containsAll(OrganisationModuleService.MODULES_COMMUNS);
        assertThat(modules).contains("TONTINE", "FINANCE");
    }

    // ── isModuleActif ────────────────────────────────────────────────────────────

    @Test
    void isModuleActif_whenModulePresent_returnsTrue() {
        UUID id = UUID.randomUUID();
        Organisation org = new Organisation();
        org.setModulesActifs("TONTINE,FINANCE");
        when(organisationRepository.findByIdOptional(id)).thenReturn(Optional.of(org));

        assertThat(service.isModuleActif(id, "TONTINE")).isTrue();
    }

    @Test
    void isModuleActif_whenModuleAbsent_returnsFalse() {
        UUID id = UUID.randomUUID();
        Organisation org = new Organisation();
        org.setModulesActifs("TONTINE");
        when(organisationRepository.findByIdOptional(id)).thenReturn(Optional.of(org));

        assertThat(service.isModuleActif(id, "CREDIT")).isFalse();
    }

    @Test
    void isModuleActif_whenModuleIsCommunModule_returnsTrue() {
        UUID id = UUID.randomUUID();
        Organisation org = new Organisation();
        org.setModulesActifs(null);
        org.setTypeOrganisation("ASSOCIATION");
        when(organisationRepository.findByIdOptional(id)).thenReturn(Optional.of(org));

        assertThat(service.isModuleActif(id, "MEMBRES")).isTrue();
    }

    @Test
    void isModuleActif_caseInsensitive() {
        UUID id = UUID.randomUUID();
        Organisation org = new Organisation();
        org.setModulesActifs("TONTINE");
        when(organisationRepository.findByIdOptional(id)).thenReturn(Optional.of(org));

        assertThat(service.isModuleActif(id, "tontine")).isTrue();
    }

    // ── getModulesParType ────────────────────────────────────────────────────────

    @Test
    void getModulesParType_null_returnsEmpty() {
        assertThat(service.getModulesParType(null)).isEmpty();
    }

    @Test
    void getModulesParType_tontine_returnsTontineAndFinance() {
        Set<String> modules = service.getModulesParType("TONTINE");
        assertThat(modules).contains("TONTINE", "FINANCE");
    }

    @Test
    void getModulesParType_mutuelle_returnsMutuelleModules() {
        assertThat(service.getModulesParType("MUTUELLE")).contains("EPARGNE", "CREDIT", "FINANCE", "LCB_FT");
        assertThat(service.getModulesParType("MUTUELLE_EPARGNE")).contains("EPARGNE", "CREDIT");
        assertThat(service.getModulesParType("MUTUELLE_CREDIT")).contains("CREDIT");
    }

    @Test
    void getModulesParType_cooperative_returnsAgricultureAndFinance() {
        assertThat(service.getModulesParType("COOPERATIVE")).contains("AGRICULTURE", "FINANCE");
    }

    @Test
    void getModulesParType_ong_returnsProjetsOngAndCollecteFonds() {
        assertThat(service.getModulesParType("ONG")).contains("PROJETS_ONG", "COLLECTE_FONDS", "FINANCE");
        assertThat(service.getModulesParType("FONDATION")).contains("PROJETS_ONG", "COLLECTE_FONDS");
    }

    @Test
    void getModulesParType_eglise_returnsCulteDons() {
        assertThat(service.getModulesParType("EGLISE")).contains("CULTE_DONS");
        assertThat(service.getModulesParType("GROUPE_PRIERE")).contains("CULTE_DONS");
    }

    @Test
    void getModulesParType_syndicat_returnsVotesAndRegistre() {
        assertThat(service.getModulesParType("SYNDICAT")).contains("VOTES", "REGISTRE_AGREMENT");
        assertThat(service.getModulesParType("ORDRE_PROFESSIONNEL")).contains("VOTES");
        assertThat(service.getModulesParType("FEDERATION")).contains("REGISTRE_AGREMENT");
    }

    @Test
    void getModulesParType_gie_returnsFinance() {
        assertThat(service.getModulesParType("GIE")).contains("FINANCE");
    }

    @Test
    void getModulesParType_association_returnsTontineAndVotes() {
        assertThat(service.getModulesParType("ASSOCIATION")).contains("TONTINE", "VOTES");
        assertThat(service.getModulesParType("CLUB_SERVICE")).contains("TONTINE", "VOTES");
    }

    @Test
    void getModulesParType_clubSportif_returnsVotes() {
        assertThat(service.getModulesParType("CLUB_SPORTIF")).contains("VOTES");
        assertThat(service.getModulesParType("CLUB_CULTUREL")).contains("VOTES");
    }

    @Test
    void getModulesParType_unknown_returnsEmpty() {
        Set<String> modules = service.getModulesParType("UNKNOWN_TYPE");
        // Default case just logs — no modules added
        assertThat(modules).isEmpty();
    }

    @Test
    void getModulesParType_isCaseInsensitive() {
        assertThat(service.getModulesParType("tontine")).contains("TONTINE");
        assertThat(service.getModulesParType("Mutuelle")).contains("EPARGNE");
    }

    // ── getModulesActifsResponse ─────────────────────────────────────────────────

    @Test
    void getModulesActifsResponse_whenOrgNotFound_returnsEmptyWithUnknownType() {
        UUID id = UUID.randomUUID();
        when(organisationRepository.findByIdOptional(id)).thenReturn(Optional.empty());

        OrganisationModuleService.ModulesActifsResponse response = service.getModulesActifsResponse(id);

        assertThat(response.organisationId()).isEqualTo(id);
        assertThat(response.modules()).isEmpty();
        assertThat(response.typeOrganisation()).isEqualTo("UNKNOWN");
    }

    @Test
    void getModulesActifsResponse_whenOrgFound_returnsModulesAndType() {
        UUID id = UUID.randomUUID();
        Organisation org = new Organisation();
        org.setModulesActifs("TONTINE,FINANCE");
        org.setTypeOrganisation("TONTINE");
        when(organisationRepository.findByIdOptional(id)).thenReturn(Optional.of(org));

        OrganisationModuleService.ModulesActifsResponse response = service.getModulesActifsResponse(id);

        assertThat(response.organisationId()).isEqualTo(id);
        assertThat(response.modules()).contains("TONTINE", "FINANCE");
        assertThat(response.typeOrganisation()).isEqualTo("TONTINE");
    }

    // ── ModulesActifsResponse record ─────────────────────────────────────────────

    @Test
    void modulesActifsResponse_record_accessors() {
        UUID id = UUID.randomUUID();
        Set<String> modules = Set.of("MEMBRES", "TONTINE");
        OrganisationModuleService.ModulesActifsResponse response =
                new OrganisationModuleService.ModulesActifsResponse(id, modules, "TONTINE");

        assertThat(response.organisationId()).isEqualTo(id);
        assertThat(response.modules()).isEqualTo(modules);
        assertThat(response.typeOrganisation()).isEqualTo("TONTINE");
    }
}
