package dev.lions.unionflow.server.service.gouvernance;

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.anyString;
import static org.mockito.Mockito.*;

import dev.lions.unionflow.server.api.dto.gouvernance.EchelonOrganigrammeDTO;
import dev.lions.unionflow.server.entity.Organisation;
import dev.lions.unionflow.server.entity.gouvernance.EchelonOrganigramme;
import dev.lions.unionflow.server.mapper.gouvernance.EchelonOrganigrammeMapper;
import dev.lions.unionflow.server.repository.OrganisationRepository;
import dev.lions.unionflow.server.repository.gouvernance.EchelonOrganigrammeRepository;
import io.quarkus.hibernate.orm.panache.PanacheQuery;
import io.quarkus.test.InjectMock;
import io.quarkus.test.junit.QuarkusTest;
import jakarta.inject.Inject;
import jakarta.ws.rs.NotFoundException;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

@QuarkusTest
class EchelonOrganigrammeServiceTest {

    @Inject
    EchelonOrganigrammeService service;

    @InjectMock
    EchelonOrganigrammeRepository repository;

    @InjectMock
    OrganisationRepository organisationRepository;

    @InjectMock
    EchelonOrganigrammeMapper mapper;

    @Test
    @DisplayName("creerEchelon lie l'organisation et persiste l'échelon")
    void creerEchelon_success() {
        UUID orgId = UUID.randomUUID();
        EchelonOrganigrammeDTO dto = new EchelonOrganigrammeDTO();
        dto.setOrganisationId(orgId.toString());

        Organisation org = new Organisation();
        org.setId(orgId);

        EchelonOrganigramme entity = new EchelonOrganigramme();

        when(organisationRepository.findByIdOptional(orgId)).thenReturn(Optional.of(org));
        when(mapper.toEntity(dto)).thenReturn(entity);
        when(mapper.toDto(entity)).thenReturn(dto);

        service.creerEchelon(dto);

        assertThat(entity.getOrganisation()).isEqualTo(org);
        verify(repository).persist(any(EchelonOrganigramme.class));
    }

    @Test
    @DisplayName("creerEchelon lance NotFoundException si l'organisation n'existe pas")
    void creerEchelon_orgNotFound_throwsNotFound() {
        UUID orgId = UUID.randomUUID();
        EchelonOrganigrammeDTO dto = new EchelonOrganigrammeDTO();
        dto.setOrganisationId(orgId.toString());

        when(organisationRepository.findByIdOptional(orgId)).thenReturn(Optional.empty());

        assertThatThrownBy(() -> service.creerEchelon(dto))
                .isInstanceOf(NotFoundException.class);
    }

    @Test
    @DisplayName("creerEchelon avec parent lance NotFoundException si organisation parente n'existe pas")
    void creerEchelon_parentOrgNotFound_throwsNotFound() {
        UUID orgId = UUID.randomUUID();
        UUID parentOrgId = UUID.randomUUID();
        EchelonOrganigrammeDTO dto = new EchelonOrganigrammeDTO();
        dto.setOrganisationId(orgId.toString());
        dto.setEchelonParentId(parentOrgId.toString());

        Organisation org = new Organisation();
        org.setId(orgId);
        EchelonOrganigramme entity = new EchelonOrganigramme();

        when(organisationRepository.findByIdOptional(orgId)).thenReturn(Optional.of(org));
        when(organisationRepository.findByIdOptional(parentOrgId)).thenReturn(Optional.empty());
        when(mapper.toEntity(dto)).thenReturn(entity);

        assertThatThrownBy(() -> service.creerEchelon(dto))
                .isInstanceOf(NotFoundException.class);
    }

    @Test
    @DisplayName("creerEchelon avec parent lie l'organisation parente à l'échelon")
    void creerEchelon_avecParent_success() {
        UUID orgId = UUID.randomUUID();
        UUID parentOrgId = UUID.randomUUID();
        EchelonOrganigrammeDTO dto = new EchelonOrganigrammeDTO();
        dto.setOrganisationId(orgId.toString());
        dto.setEchelonParentId(parentOrgId.toString());

        Organisation org = new Organisation();
        org.setId(orgId);
        Organisation parentOrg = new Organisation();
        parentOrg.setId(parentOrgId);

        EchelonOrganigramme entity = new EchelonOrganigramme();

        when(organisationRepository.findByIdOptional(orgId)).thenReturn(Optional.of(org));
        when(organisationRepository.findByIdOptional(parentOrgId)).thenReturn(Optional.of(parentOrg));
        when(mapper.toEntity(dto)).thenReturn(entity);
        when(mapper.toDto(entity)).thenReturn(dto);

        service.creerEchelon(dto);

        assertThat(entity.getOrganisation()).isEqualTo(org);
        assertThat(entity.getEchelonParent()).isEqualTo(parentOrg);
        verify(repository).persist(any(EchelonOrganigramme.class));
    }

    @Test
    @DisplayName("getEchelonById retourne le DTO quand l'échelon existe")
    void getEchelonById_found_returnsDto() {
        UUID id = UUID.randomUUID();
        EchelonOrganigramme entity = new EchelonOrganigramme();
        EchelonOrganigrammeDTO dto = new EchelonOrganigrammeDTO();

        when(repository.findByIdOptional(id)).thenReturn(Optional.of(entity));
        when(mapper.toDto(entity)).thenReturn(dto);

        EchelonOrganigrammeDTO result = service.getEchelonById(id);

        assertThat(result).isEqualTo(dto);
    }

    @Test
    @DisplayName("getEchelonById lance NotFoundException si l'échelon n'existe pas")
    void getEchelonById_notFound_throws() {
        UUID id = UUID.randomUUID();
        when(repository.findByIdOptional(id)).thenReturn(Optional.empty());

        assertThatThrownBy(() -> service.getEchelonById(id))
                .isInstanceOf(NotFoundException.class);
    }

    @Test
    @SuppressWarnings("unchecked")
    @DisplayName("getOrganigrammeByOrganisation retourne la liste des échelons")
    void getOrganigrammeByOrganisation_returnsList() {
        UUID orgId = UUID.randomUUID();
        EchelonOrganigramme entity = new EchelonOrganigramme();
        EchelonOrganigrammeDTO dto = new EchelonOrganigrammeDTO();

        PanacheQuery<EchelonOrganigramme> mockQuery = mock(PanacheQuery.class);
        when(repository.find(anyString(), (Object[]) any())).thenReturn(mockQuery);
        when(mockQuery.stream()).thenReturn(List.of(entity).stream());
        when(mapper.toDto(entity)).thenReturn(dto);

        List<EchelonOrganigrammeDTO> result = service.getOrganigrammeByOrganisation(orgId);

        assertThat(result).hasSize(1).containsExactly(dto);
    }
}
