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

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

import java.time.LocalDate;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

import io.delphiplatform.api.util.CollectionUtils;
import io.delphiplatform.api.v3.model.tiktok.TikTokStateMetric;
import io.delphiplatform.api.v3.model.tiktok.top.TikTokTopChartTrackState;
import io.delphiplatform.api.v3.model.tiktok.top.TikTokTopChartTrackStates;
import io.delphiplatform.api.v3.model.tiktok.top.TikTokTopTracksAnalytics;
import io.delphiplatform.api.v3.rdb.entity.tiktok.FactTikTokTopChartTrackLifetimeEntity;
import io.delphiplatform.api.v3.rdb.entity.tiktok.FactTikTokTopTrackEntity;
import io.delphiplatform.api.v3.rdb.entity.tiktok.TikTokTopSongEntity;
import io.delphiplatform.api.v3.rdb.repository.FactTikTokTopChartTrackLifetimeRepository;
import io.delphiplatform.api.v3.rdb.repository.FactTikTokTopTrackRepository;
import io.delphiplatform.api.v3.rdb.repository.TikTokTopSongRepository;
import io.delphiplatform.api.v3.rdb.repository.TikTokTopTrackChartRepository;
import io.delphiplatform.api.v3.rdb.service.EntityService;
import io.delphiplatform.api.v3.rdb.service.specification.SpecificationProvider;
import io.delphiplatform.api.v3.view.util.Params;
import lombok.extern.slf4j.Slf4j;

import static io.delphiplatform.api.v3.rdb.entity.CommonJpaPropertyNames.CHART_DATE;
import static io.delphiplatform.api.v3.rdb.entity.CommonJpaPropertyNames.CHART_ID;
import static io.delphiplatform.api.v3.rdb.entity.CommonJpaPropertyNames.CURRENT_POSITION;
import static io.delphiplatform.api.v3.rdb.entity.CommonJpaPropertyNames.ID_PROPERTY;
import static io.delphiplatform.api.v3.rdb.entity.CommonJpaPropertyNames.ISRC;

@Service
@Transactional(readOnly = true)
@Slf4j
public class TikTokTopTracksService extends EntityService<FactTikTokTopTrackEntity> {

    private final SpecificationProvider<FactTikTokTopTrackEntity> specificationProvider;

    private final FactTikTokTopTrackRepository repository;

    private final FactTikTokTopChartTrackLifetimeRepository lifetimeRepository;

    private final TikTokTopSongRepository tikTokTopSongRepository;

    private final TikTokTopTrackChartRepository tiktokTopTrackChartRepository;

    @Autowired
    public TikTokTopTracksService(FactTikTokTopTrackRepository repository,
        FactTikTokTopChartTrackLifetimeRepository lifetimeRepository,
        SpecificationProvider<FactTikTokTopTrackEntity> specificationProvider,
        TikTokTopSongRepository tikTokTopSongRepository,
        TikTokTopTrackChartRepository tiktokTopTrackChartRepository) {
        super(repository, null);
        this.repository = repository;
        this.specificationProvider = specificationProvider;
        this.lifetimeRepository = lifetimeRepository;
        this.tikTokTopSongRepository = tikTokTopSongRepository;
        this.tiktokTopTrackChartRepository = tiktokTopTrackChartRepository;
    }

    public TikTokTopChartTrackStates getTikTokTopTracks(Params params) {
        if (!tiktokTopTrackChartRepository.existsById(params.getSingleChartId())) {
            throw NOT_FOUND.get();
        }

        LocalDate date =
            params.getDate() == null ? repository.findMaxDateByChartId(params.getSingleChartId()) : params.getDate();

        Specification<FactTikTokTopTrackEntity> spec = and(Arrays.asList(
            specificationProvider.singleValueSpec(List.of(ID_PROPERTY, CHART_ID), params.getSingleChartId()),
            specificationProvider.singleValueSpec(List.of(ID_PROPERTY, CHART_DATE), date),
            specificationProvider.lessThanOrEqualToValueSpec(CURRENT_POSITION, params.getMaxPosition()),
            specificationProvider.greaterThanOrEqualToValueSpec(CURRENT_POSITION, params.getMinPosition()),
            specificationProvider.multiValueSpec(List.of(ID_PROPERTY, ISRC), params.getIsrc())
        ));

        List<FactTikTokTopTrackEntity> entities = find(spec);

        if (CollectionUtils.isEmpty(entities)) {
            return new TikTokTopChartTrackStates(Collections.emptyList());
        }

        List<String> isrcs = entities.stream().map(e -> e.getId().getIsrc()).collect(Collectors.toList());

        Map<String, FactTikTokTopChartTrackLifetimeEntity> lifetimeMap = getLifetimeMetrics(params, isrcs);
        Map<String, TikTokTopSongEntity> sourceMap = getSourceMetric(params, isrcs);
        List<TikTokTopChartTrackState> items = entities.stream()
            .map(elem -> TikTokTopChartTrackState
                .of(elem, params.getTikTokTrackStateIncludes(), lifetimeMap, sourceMap))
            .collect(Collectors.toList());
        return new TikTokTopChartTrackStates(items);
    }

    private Map<String, TikTokTopSongEntity> getSourceMetric(Params params, List<String> isrcs) {
        Map<String, TikTokTopSongEntity> sourceMetadata = null;
        if (isSourceMetricsRequested(params.getTikTokTrackStateIncludes())) {
            sourceMetadata = tikTokTopSongRepository.getTikTokTopSongEntityByIsrc(isrcs).stream()
                .collect(Collectors.toMap(
                    TikTokTopSongEntity::getIsrc, Function.identity()));
        }
        return sourceMetadata;
    }

    private Map<String, FactTikTokTopChartTrackLifetimeEntity> getLifetimeMetrics(Params params, List<String> isrcs) {
        if (isLifetimeMetricsRequested(params.getTikTokTrackStateIncludes())) {
            return lifetimeRepository.findByIdChartIdAndIdIsrcIn(params.getSingleChartId(), isrcs)
                .stream().collect(Collectors.toMap(elem -> elem.getId().getIsrc(), Function.identity()));
        }
        return null;
    }


    public TikTokTopTracksAnalytics getTikTokTopTracksAnalytics(Params params) {
        if (!tiktokTopTrackChartRepository.existsById(params.getSingleChartId())) {
            throw NOT_FOUND.get();
        }

        Specification<FactTikTokTopTrackEntity> spec = and(Arrays.asList(
            specificationProvider.singleValueSpec(List.of(ID_PROPERTY, CHART_ID), params.getSingleChartId()),
            specificationProvider.greaterThanOrEqualToValueSpec(CHART_DATE, params.getStartDate()),
            specificationProvider.lessThanOrEqualToValueSpec(CHART_DATE, params.getEndDate()),
            specificationProvider.multiValueSpec(List.of(ID_PROPERTY, ISRC), params.getIsrc())
        ));

        List<FactTikTokTopTrackEntity> entities = find(spec);

        return entities.stream().collect(TikTokTopAnalyticCollector.get());
    }

    public static boolean isLifetimeMetricsRequested(Collection<TikTokStateMetric> collection) {
        return CollectionUtils.contains(collection, TikTokStateMetric.LIFETIME_METRICS);
    }

    public static boolean isSourceMetricsRequested(Collection<TikTokStateMetric> collection) {
        return CollectionUtils.contains(collection, TikTokStateMetric.SOURCE_META);
    }

    public static boolean isAggMetricsRequested(Collection<TikTokStateMetric> collection) {
        return CollectionUtils.contains(collection, TikTokStateMetric.AGGREGATED_METRICS);
    }

    public static boolean isMetricsRequested(Collection<TikTokStateMetric> collection) {
        return CollectionUtils.contains(collection, TikTokStateMetric.METRICS);
    }

}
