package ru.yandex.webmaster3.worker.feeds.download;

import java.io.IOException;
import java.util.Objects;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.opencsv.bean.CsvBindByName;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Value;
import org.joda.time.LocalDate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import ru.yandex.webmaster3.core.download.MdsSerializable;
import ru.yandex.webmaster3.core.util.functional.ThrowingConsumer;
import ru.yandex.webmaster3.core.xcelite.annotations.Column;
import ru.yandex.webmaster3.core.xcelite.annotations.Row;
import ru.yandex.webmaster3.storage.download.common.MdsExportTaskData;
import ru.yandex.webmaster3.storage.feeds.download.FeedsClickStatisticsMdsExportDescriptor.FeedsSetClickStatisticsMdsExportDescriptor;
import ru.yandex.webmaster3.storage.util.yt.AsyncTableReader;
import ru.yandex.webmaster3.storage.util.yt.YtPath;
import ru.yandex.webmaster3.storage.util.yt.YtService;
import ru.yandex.webmaster3.storage.util.yt.YtTableRange;
import ru.yandex.webmaster3.storage.util.yt.YtTableReadDriver;
import ru.yandex.webmaster3.worker.download.AbstractMdsDataProvider;

import static ru.yandex.webmaster3.core.util.functional.ThrowingConsumer.rethrowingUnchecked;

/**
 * Created by Oleg Bazdyrev on 14/03/2022.
 */
@Service
@AllArgsConstructor(onConstructor_ = @Autowired)
public class FeedsSetClickStatisticsMdsDataProvider extends AbstractMdsDataProvider<FeedsSetClickStatisticsMdsDataProvider.CsvRow> {

    @org.springframework.beans.factory.annotation.Value("${external.yt.service.hahn.root.default}/feeds/clicks/set-stats")
    private final YtPath sourceTable;
    private final YtService ytService;

    private static long localDateToDays(LocalDate ld) {
        return ld.toDateTimeAtStartOfDay().getMillis() / 86400000 + 1;
    }

    private static LocalDate daysToLocalDate(long days) {
        return new LocalDate(days * 86400000L);
    }

    @Override
    public void provide(MdsExportTaskData data, ThrowingConsumer<CsvRow, Exception> consumer) throws Exception {
        var descriptor = (FeedsSetClickStatisticsMdsExportDescriptor) data.getDescriptor();
        ytService.withoutTransaction((cypressService) -> {
            AsyncTableReader<YtRow> reader = new AsyncTableReader<>(
                    cypressService,
                    sourceTable,
                    YtTableRange.raw("(\"" + descriptor.getDomain() + "\",\"" + descriptor.getFeedType().name() + "\","  + localDateToDays(descriptor.getFrom()) + ") : " +
                                    "(\"" + descriptor.getDomain() + "\",\"" + descriptor.getFeedType().name() + "\","  + localDateToDays(descriptor.getTo()) + ")"),
                    YtTableReadDriver.createYSONDriver(YtRow.class)
            ).withRetry(3).splitInParts(10000L);

            try (AsyncTableReader.TableIterator<YtRow> iterator = reader.read()) {
                if (!iterator.hasNext()) {
                    rethrowingUnchecked(consumer).accept(new CsvRow(null, null, false, false, null, null, null, null));
                }
                while (iterator.hasNext()) {
                    YtRow row = iterator.next();
                    rethrowingUnchecked(consumer).accept(new CsvRow(
                            daysToLocalDate(row.getDate()).toString(),
                            row.getSetUrl(),
                            row.getDataAvailableCostPlus() != null,
                            row.getDocMarkersCostPlus() != null,
                            Objects.requireNonNullElse(row.getShows(), 0L),
                            Objects.requireNonNullElse(row.getClicks(), 0L),
                            Objects.requireNonNullElse(row.getOfferClicks(), 0L),
                            Objects.requireNonNullElse(row.getMoreLinkClicks(), 0L)
                    ));
                }
            } catch (IOException e) {
                throw new RuntimeException(e);
            }

            return true;
        });
    }

    @Override
    public Class<CsvRow> getRowClass() {
        return CsvRow.class;
    }

    @AllArgsConstructor
    @Getter
    @Row(colsOrder = {"Date", "SetUrl", "HasEnrichment", "Enriched", "Shows", "Clicks", "OfferClicks", "MoreLinkClicks"})
    public static final class CsvRow implements MdsSerializable {

        @CsvBindByName
        @Column(name = "Date")
        private final String date;

        @CsvBindByName
        @Column(name = "SetUrl")
        private final String setUrl;

        @CsvBindByName
        @Column(name = "HasEnrichment")
        private final boolean hasEnrichment;

        @CsvBindByName
        @Column(name = "Enriched")
        private final boolean enriched;

        @CsvBindByName
        @Column(name = "Shows")
        private final Long shows;

        @CsvBindByName
        @Column(name = "Clicks")
        private final Long clicks;

        @CsvBindByName
        @Column(name = "OfferClicks")
        private final Long offerClicks;

        @CsvBindByName
        @Column(name = "MoreLinkClicks")
        private final Long moreLinkClicks;
    }

    @Value
    @AllArgsConstructor(onConstructor_ = @JsonCreator)
    private static final class YtRow {
        @JsonProperty("Date")
        Integer date;
        @JsonProperty("Domain")
        String domain;
        @JsonProperty("SetUrl")
        String setUrl;
        @JsonProperty("Clicks")
        Long clicks;
        @JsonProperty("DataAvailableCostPlus")
        String dataAvailableCostPlus;
        @JsonProperty("DocMarkersCostPlus")
        String docMarkersCostPlus;
        @JsonProperty("OfferClicks")
        Long offerClicks;
        @JsonProperty("MoreLinkClicks")
        Long moreLinkClicks;
        @JsonProperty("Shows")
        Long shows;
    }

}
