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

import com.healthmarketscience.sqlbuilder.CommonTableExpression;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Set;

import io.delphiplatform.api.util.CollectionUtils;
import io.delphiplatform.api.util.OffsetBasedPageRequest;
import io.delphiplatform.api.v3.model.ads.AdCurrency;
import io.delphiplatform.api.v3.model.ads.AdMeta;
import io.delphiplatform.api.v3.model.ads.AdPerformanceSummaryItem;
import io.delphiplatform.api.v3.model.ads.GroupByAdsPerformance;
import io.delphiplatform.api.v3.rdb.service.ads.query.sqlbuilder.AdsReportRequestSortingService;
import io.delphiplatform.api.v3.rdb.service.dto.AdsReportQueryFilterParams;
import io.delphiplatform.api.v3.view.util.Params;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
public class AdsPerformanceSummaryItemsService {

    private final AdsCampaignService adsCampaignService;
    private final AdsMetricService adsMetricService;
    private final DecibelProjectService decibelProjectService;
    private final LinkfireDimLinkService linkService;
    private final AdsReportRequestSortingService sortingService;

    @Autowired
    public AdsPerformanceSummaryItemsService(AdsCampaignService adsCampaignService,
        AdsMetricService adsMetricService,
        DecibelProjectService decibelProjectService,
        LinkfireDimLinkService linkService,
        AdsReportRequestSortingService sortingService) {
        this.adsCampaignService = adsCampaignService;
        this.adsMetricService = adsMetricService;
        this.decibelProjectService = decibelProjectService;
        this.linkService = linkService;
        this.sortingService = sortingService;
    }

    public List<AdPerformanceSummaryItem> getModels(Params params) {
        sortingService.validateGroupBySorting(params.getSortBy(), params.getAdsPerformancesGroupBy());
        Set<GroupByAdsPerformance> groupBys = params.getAdsPerformancesGroupBy();
        Set<String> requestedProjects = decibelProjectService
            .findProjects(params.getArtistId(), params.getProjectIds());

        if (CollectionUtils.isEmpty(requestedProjects) && (
            CollectionUtils.isNotEmpty(params.getArtistId()) || CollectionUtils.isNotEmpty(params.getProjectIds()))) {
            return Collections.emptyList();
        }

        boolean considerConfidential = CollectionUtils.isEmpty(params.getProjectIds());

        Params paramsWithProjectIds = params.toBuilder()
            .projectIds(requestedProjects)
            .considerConfidential(considerConfidential)
            .build();

        return getModelUsingCteExpression(params, groupBys, requestedProjects, paramsWithProjectIds);
    }

    private List<AdPerformanceSummaryItem> getModelUsingCteExpression(Params params,
        Set<GroupByAdsPerformance> groupBys, Set<String> requestedProjects, Params paramsWithProjectIds) {

        Set<String> projectsToQueryAdsData = Collections.emptySet();
        Set<String> projectsIds = Collections.emptySet();

        boolean emptyAdditionalParams =
            CollectionUtils.isEmpty(params.getArtistId()) &&
                CollectionUtils.areEmpty(
                    params.getCampaignIds(),
                    params.getDecibelLabelIds(),
                    params.getPlatforms(),
                    params.getProviderIds(),
                    params.getSubCategoryIds());

        CommonTableExpression decibelProjectsCte = null;
        if (CollectionUtils.containsAny(groupBys, GroupByAdsPerformance.CAMPAIGN_BASED)) {
            projectsToQueryAdsData = Collections.emptySet();
        } else if (CollectionUtils.isNotEmpty(requestedProjects)) {
            projectsToQueryAdsData = requestedProjects;
            projectsIds = requestedProjects;
        } else if (emptyAdditionalParams) {
            decibelProjectsCte = decibelProjectService.findProjectsIdsExpression(params.getUserId(),
                params.getDecibelLabelIds());
            if (CollectionUtils.containsAny(groupBys, GroupByAdsPerformance.PROJECT_BASED)) {
                projectsIds = Set.of(""); // Act as marker only on CTE Condition
            }
        }
        CommonTableExpression campaignsCte = adsCampaignService.findCampaignIdsExpression(paramsWithProjectIds);
        CommonTableExpression linksCte = linkService.findByProjectsAndCampaignsExpression(campaignsCte.getTable(),
            projectsToQueryAdsData, decibelProjectsCte == null ? null : decibelProjectsCte.getTable());

        AdsReportQueryFilterParams queryFilterParams = AdsReportQueryFilterParams.builder()
            .startDate(params.getStartDate())
            .endDate(params.getEndDate())
            .campaignIdsCte(campaignsCte)
            .projectIdsCte(decibelProjectsCte)
            .artistId(params.getArtistId())
            .projectIds(projectsIds)
            .linkIdsCte(linksCte)
            .filterResponseByCampaignIds(CollectionUtils.containsAny(groupBys, GroupByAdsPerformance.CAMPAIGN_BASED))
            .dsps(params.getAdsDsps())
            .countryCodes(params.getCountryCode())
            .currencies(Optional.ofNullable(params.getCurrency())
                .map(currency -> Set.of(currency))
                .orElse(Collections.emptySet()))
            .build();

        log.debug("Ads request params are {}", queryFilterParams);

        return adsMetricService.getMetrics(queryFilterParams,
            groupBys, OffsetBasedPageRequest.ofParamsKeepCase(params));
    }

    public AdMeta getMeta(Params params) {
        return new AdMeta()
            .currency(getDefaultAdCurrency());
    }

    private AdCurrency getDefaultAdCurrency() {
        // temporary hard-coding for API clients
        return new AdCurrency()
            .currencyCode("USD")
            .currencySymbol("$");
    }
}
