package com.oguerreiro.resilient.repository;

import com.oguerreiro.resilient.domain.OrganizationType;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Optional;
import java.util.stream.IntStream;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;

/**
 * Utility repository to load bag relationships based on https://vladmihalcea.com/hibernate-multiplebagfetchexception/
 */
public class OrganizationTypeRepositoryWithBagRelationshipsImpl implements OrganizationTypeRepositoryWithBagRelationships {

    private static final String ID_PARAMETER = "id";
    private static final String ORGANIZATIONTYPES_PARAMETER = "organizationTypes";

    @PersistenceContext
    private EntityManager entityManager;

    @Override
    public Optional<OrganizationType> fetchBagRelationships(Optional<OrganizationType> organizationType) {
        return organizationType.map(this::fetchMetadataProperties);
    }

    @Override
    public Page<OrganizationType> fetchBagRelationships(Page<OrganizationType> organizationTypes) {
        return new PageImpl<>(
            fetchBagRelationships(organizationTypes.getContent()),
            organizationTypes.getPageable(),
            organizationTypes.getTotalElements()
        );
    }

    @Override
    public List<OrganizationType> fetchBagRelationships(List<OrganizationType> organizationTypes) {
        return Optional.of(organizationTypes).map(this::fetchMetadataProperties).orElse(Collections.emptyList());
    }

    OrganizationType fetchMetadataProperties(OrganizationType result) {
        return entityManager
            .createQuery(
                "select organizationType from OrganizationType organizationType left join fetch organizationType.metadataProperties where organizationType.id = :id",
                OrganizationType.class
            )
            .setParameter(ID_PARAMETER, result.getId())
            .getSingleResult();
    }

    List<OrganizationType> fetchMetadataProperties(List<OrganizationType> organizationTypes) {
        HashMap<Object, Integer> order = new HashMap<>();
        IntStream.range(0, organizationTypes.size()).forEach(index -> order.put(organizationTypes.get(index).getId(), index));
        List<OrganizationType> result = entityManager
            .createQuery(
                "select organizationType from OrganizationType organizationType left join fetch organizationType.metadataProperties where organizationType in :organizationTypes",
                OrganizationType.class
            )
            .setParameter(ORGANIZATIONTYPES_PARAMETER, organizationTypes)
            .getResultList();
        Collections.sort(result, (o1, o2) -> Integer.compare(order.get(o1.getId()), order.get(o2.getId())));
        return result;
    }
}
