package io.delphiplatform.api.v3.rdb.service;

import com.google.api.client.util.Sets;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import io.delphiplatform.api.util.CollectionUtils;
import io.delphiplatform.api.v3.model.ArtistSimple;
import io.delphiplatform.api.v3.model.service.ImageService;
import io.delphiplatform.api.v3.rdb.entity.ArtistEntity;
import io.delphiplatform.api.v3.rdb.entity.ParticipantEntity;
import io.delphiplatform.api.v3.rdb.entity.ParticipantId;
import io.delphiplatform.api.v3.rdb.repository.ArtistRepository;
import io.delphiplatform.api.v3.rdb.repository.ParticipantRepository;
import io.delphiplatform.api.v3.rdb.service.specification.SpecificationProvider;

@Service
@Transactional(readOnly = true)
public class ArtistService extends EntityService<ArtistEntity> {

    private final ArtistRepository artistRepository;
    private final SpecificationProvider<ArtistEntity> specificationProvider;
    private final ImageService imageService;
    private final ParticipantRepository participantRepository;

    @Autowired
    public ArtistService(ArtistRepository artistRepository, SpecificationProvider<ArtistEntity> specificationProvider,
        ImageService imageService, ParticipantRepository participantRepository) {
        super(artistRepository);
        this.artistRepository = artistRepository;
        this.specificationProvider = specificationProvider;
        this.imageService = imageService;
        this.participantRepository = participantRepository;
    }

    @Transactional(readOnly = true)
    public Set<String> findParticipantIdsByArtistId(String artistId) {
        if (CollectionUtils.isEmpty(artistId)) {
            return Collections.emptySet();
        }
        Set<String> allArtists = Sets.newHashSet();
        allArtists.add(artistId);
        List<ParticipantEntity> participantEntities = participantRepository.findPrimaryByArtistId(artistId);
        allArtists.addAll(participantEntities.stream()
            .map(ParticipantEntity::getParticipantId)
            .map(ParticipantId::getParticipantId)
            .collect(Collectors.toList()));
        return allArtists;
    }

    @Transactional(readOnly = true)
    public Set<String> findArtistIdsByParticipantId(String participantId) {
        if (CollectionUtils.isEmpty(participantId)) {
            return Collections.emptySet();
        }
        Set<String> allArtists = Sets.newHashSet();
        allArtists.add(participantId);
        List<ParticipantEntity> participantEntities = participantRepository.findPrimaryByParticipantId(participantId);
        allArtists.addAll(participantEntities.stream()
            .map(ParticipantEntity::getParticipantId)
            .map(ParticipantId::getArtist)
            .map(ArtistEntity::getArtistId)
            .collect(Collectors.toList()));
        return allArtists;
    }

    @Transactional(readOnly = true)
    public List<ArtistSimple> find(String productId, String trackId, String isrc, List<String> artistIds, Pageable pageable) {
        Specification<ArtistEntity> specification = and(Arrays.asList(
            specificationProvider.singleValueSpec("productId", productId, "products"),
            specificationProvider.singleValueSpec("trackId", trackId, "tracks"),
            specificationProvider.singleValueSpec("isrc", isrc, "tracks"),
            specificationProvider.multiValueSpec("artistId", artistIds)
        ));

        return find(specification, pageable).stream()
            .map(artist -> ArtistSimple.from(artist, imageService))
            .collect(Collectors.toList());
    }

    @Transactional(readOnly = true)
    public ArtistSimple findOne(String artistId) {
        return artistRepository.findById(artistId)
            .map(artist -> ArtistSimple.from(artist, imageService))
            .orElseThrow(NOT_FOUND);
    }

    @Transactional(readOnly = true)
    public List<ArtistSimple> findAllById(List<String> artistIds) {
        return artistRepository.findAllById(artistIds).stream()
            .map(artist -> ArtistSimple.from(artist, imageService))
            .collect(Collectors.toList());
    }

}
