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

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.time.LocalDate;
import java.util.List;

import io.delphiplatform.api.util.CollectionUtils;
import io.delphiplatform.api.v3.model.datahealth.DataHealthBreakdownGroup;
import io.delphiplatform.api.v3.model.datahealth.DataHealthRangeBreakdown;
import io.delphiplatform.api.v3.model.datahealth.DataHealthRangeReport;
import io.delphiplatform.api.v3.model.datahealth.DataHealthStatus;
import io.delphiplatform.api.v3.rdb.entity.DspDataStatusDailyEntity;
import io.delphiplatform.api.v3.rdb.repository.DspDataStatusDailyRepository;
import io.delphiplatform.api.v3.rdb.service.specification.SpecificationProvider;
import io.delphiplatform.api.v3.view.util.Params;

import static io.delphiplatform.api.v3.model.datahealth.DataHealthBreakdown.DAYS;

@Service
@Transactional(readOnly = true)
public class DataHealthStatusAnalyticsService extends EntityService<DspDataStatusDailyEntity> {

    private final SpecificationProvider<DspDataStatusDailyEntity> specificationProvider;
    private final BreakdownHelper breakdownHelper;

    protected DataHealthStatusAnalyticsService(
        DspDataStatusDailyRepository repository,
        SpecificationProvider<DspDataStatusDailyEntity> specificationProvider,
        BreakdownHelper breakdownHelper) {
        super(repository);
        this.specificationProvider = specificationProvider;
        this.breakdownHelper = breakdownHelper;
    }

    public DataHealthRangeReport getDataHealthRangeReport(Params params) {
        List<DspDataStatusDailyEntity> dailyStatuses = getDailyStatuses(params);

        DataHealthRangeReport report = new DataHealthRangeReport();
        report.setMinDate(dailyStatuses.stream()
            .filter(ds -> CollectionUtils.notContains(DataHealthStatus.NO_DATA_STATUSES, ds.getStatus()))
            .map(DspDataStatusDailyEntity::getDate)
            .min(LocalDate::compareTo)
            .orElse(null));
        report.setMaxDate(dailyStatuses.stream()
            .filter(ds -> CollectionUtils.notContains(DataHealthStatus.NO_DATA_STATUSES, ds.getStatus()))
            .map(DspDataStatusDailyEntity::getDate)
            .max(LocalDate::compareTo)
            .orElse(null));

        if (report.getMinDate() == null && report.getMaxDate() == null) {
            return report;
        }

        if (CollectionUtils.contains(params.getDataHealthBreakdownGroups(), DataHealthBreakdownGroup.DSP_SEGMENT)) {
            report.getBreakdowns().setDspSegment(breakdownHelper
                .breakdown(dailyStatuses.stream(),
                    DspDataStatusDailyEntity::getDsp,
                    DspDataStatusDailyEntity::getSegment,
                    (e, dspSegmentBreakdown) -> {
                        if (CollectionUtils.contains(params.getDataHealthBreakdowns(), DAYS)) {
                            dspSegmentBreakdown.putDays(e.getDate(), e.getStatus());
                        }
                    },
                    () -> new DataHealthRangeBreakdown(report.getMinDate(), report.getMaxDate())));
        }

        return report;
    }

    private List<DspDataStatusDailyEntity> getDailyStatuses(Params params) {
        Specification<DspDataStatusDailyEntity> spec = and(List.of(
            specificationProvider.greaterThanOrEqualToValueSpec("date", params.getStartDate()),
            specificationProvider.lessThanOrEqualToValueSpec("date", params.getEndDate()),
            specificationProvider.multiValueSpec("dsp", params.getDsp())
        ));
        return getResult(spec, Pageable.unpaged());
    }

}
