package ru.yandex.webmaster3.storage.hoststat;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import com.fasterxml.jackson.core.type.TypeReference;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Repository;

import ru.yandex.webmaster3.core.data.WebmasterHostId;
import ru.yandex.webmaster3.core.hoststat.HostStatistics;
import ru.yandex.webmaster3.core.hoststat.HostStatistics.ContentAttrSample;
import ru.yandex.webmaster3.core.hoststat.HostStatistics.HostStatisticsBuilder;
import ru.yandex.webmaster3.core.hoststat.HostStatistics.PageSample;
import ru.yandex.webmaster3.core.util.json.JsonMapping;
import ru.yandex.webmaster3.storage.clickhouse.ClickhouseTableInfo;
import ru.yandex.webmaster3.storage.clickhouse.TableProvider;
import ru.yandex.webmaster3.storage.clickhouse.TableType;
import ru.yandex.webmaster3.storage.util.clickhouse2.AbstractClickhouseDao;
import ru.yandex.webmaster3.storage.util.clickhouse2.CHField;
import ru.yandex.webmaster3.storage.util.clickhouse2.CHPrimitiveType;
import ru.yandex.webmaster3.storage.util.clickhouse2.CHRow;
import ru.yandex.webmaster3.storage.util.clickhouse2.CHTable;
import ru.yandex.webmaster3.storage.util.clickhouse2.ClickhouseServer;
import ru.yandex.webmaster3.storage.util.clickhouse2.query.QueryBuilder;

/**
 * Created by Oleg Bazdyrev on 12/03/2021.
 */
@Repository
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class HostStatisticsCHDao extends AbstractClickhouseDao {

    public static final CHTable TABLE = CHTable.builder()
            .database(AbstractClickhouseDao.DB_WEBMASTER3_CHECKLIST)
            .name("host_statistics_%s")
            .partitionBy("")
            .sharded(true)
            .keyField(F.HOST_ID, CHPrimitiveType.String)
            .field(F.DOCS, CHPrimitiveType.Int64)
            .field(F.DOCS_ON_SEARCH, CHPrimitiveType.Int64)
            .field(F.HTML_DOCS, CHPrimitiveType.Int64)
            .field(F.GOOD_DOCS_ON_SEARCH, CHPrimitiveType.Int64)
            .field(F.EMPTY_TITLES, CHPrimitiveType.Int64)
            .field(F.EMPTY_DESCRIPTIONS, CHPrimitiveType.Int64)
            .field(F.SLOW_PAGES, CHPrimitiveType.Int64)
            .field(F.PAGES_WITH_FETCH_TIME, CHPrimitiveType.Int64)
            .field(F.TOTAL_FETCH_TIME, CHPrimitiveType.Int64)
            .field(F.UNIQUE_TITLES, CHPrimitiveType.Int64)
            .field(F.UNIQUE_DESCRIPTIONS, CHPrimitiveType.Int64)
            .field(F.DUPLICATE_TITLES, CHPrimitiveType.Int64)
            .field(F.DUPLICATE_DESCRIPTIONS, CHPrimitiveType.Int64)
            .field(F.MORDA_SAMPLE, CHPrimitiveType.String)
            .field(F.SLOW_PAGE_SAMPLES, CHPrimitiveType.String)
            .field(F.EMPTY_TITLE_SAMPLES, CHPrimitiveType.String)
            .field(F.EMPTY_DESCRIPTION_SAMPLES, CHPrimitiveType.String)
            .field(F.PREVAILING_TITLES, CHPrimitiveType.String)
            .field(F.PREVAILING_DESCRIPTIONS, CHPrimitiveType.String)
            .field(F.DUPLICATE_PAGE_SAMPLES, CHPrimitiveType.String)
            .build();

    private static final List<String> ALL_FIELDS = TABLE.getFields().stream().map(CHField::getName).collect(Collectors.toUnmodifiableList());

    private static final List<String> FIELDS_WITHOUT_SAMPLES = List.of(F.HOST_ID, F.DOCS, F.DOCS_ON_SEARCH, F.HTML_DOCS, F.GOOD_DOCS_ON_SEARCH,
            F.EMPTY_TITLES, F.EMPTY_DESCRIPTIONS, F.SLOW_PAGES, F.PAGES_WITH_FETCH_TIME, F.TOTAL_FETCH_TIME, F.UNIQUE_TITLES, F.UNIQUE_DESCRIPTIONS,
            F.DUPLICATE_TITLES, F.DUPLICATE_DESCRIPTIONS);

    private static final Map<String, BuilderMapper<HostStatisticsBuilder, ?>> MAPPERS = Stream.of(
            BuilderMapper.of(F.HOST_ID, CHRow::getHostId, HostStatisticsBuilder::hostId),
            BuilderMapper.of(F.DOCS, CHRow::getLong, HostStatisticsBuilder::docs),
            BuilderMapper.of(F.DOCS_ON_SEARCH, CHRow::getLong, HostStatisticsBuilder::docsOnSearch),
            BuilderMapper.of(F.HTML_DOCS, CHRow::getLong, HostStatisticsBuilder::htmlDocs),
            BuilderMapper.of(F.GOOD_DOCS_ON_SEARCH, CHRow::getLong, HostStatisticsBuilder::goodDocsOnSearch),
            BuilderMapper.of(F.EMPTY_TITLES, CHRow::getLong, HostStatisticsBuilder::emptyTitles),
            BuilderMapper.of(F.EMPTY_DESCRIPTIONS, CHRow::getLong, HostStatisticsBuilder::emptyDescriptions),
            BuilderMapper.of(F.SLOW_PAGES, CHRow::getLong, HostStatisticsBuilder::slowPages),
            BuilderMapper.of(F.PAGES_WITH_FETCH_TIME, CHRow::getLong, HostStatisticsBuilder::pagesWithFetchTime),
            BuilderMapper.of(F.TOTAL_FETCH_TIME, CHRow::getLong, HostStatisticsBuilder::totalFetchTime),
            BuilderMapper.of(F.UNIQUE_TITLES, CHRow::getLong, HostStatisticsBuilder::uniqueTitles),
            BuilderMapper.of(F.UNIQUE_DESCRIPTIONS, CHRow::getLong, HostStatisticsBuilder::uniqueDescriptions),
            BuilderMapper.of(F.DUPLICATE_TITLES, CHRow::getLong, HostStatisticsBuilder::duplicateTitles),
            BuilderMapper.of(F.DUPLICATE_DESCRIPTIONS, CHRow::getLong, HostStatisticsBuilder::duplicateDescriptions),
            BuilderMapper.of(F.MORDA_SAMPLE, jsonMapper(PageSample.REFERENCE), HostStatisticsBuilder::mordaSample),
            BuilderMapper.of(F.SLOW_PAGE_SAMPLES, jsonMapper(PageSample.LIST_REFERENCE), HostStatisticsBuilder::slowPageSamples),
            BuilderMapper.of(F.EMPTY_TITLE_SAMPLES, jsonMapper(PageSample.LIST_REFERENCE), HostStatisticsBuilder::emptyTitleSamples),
            BuilderMapper.of(F.EMPTY_DESCRIPTION_SAMPLES, jsonMapper(PageSample.LIST_REFERENCE), HostStatisticsBuilder::emptyDescriptionSamples),
            BuilderMapper.of(F.PREVAILING_TITLES, jsonMapper(ContentAttrSample.LIST_REFERENCE), HostStatisticsBuilder::prevailingTitles),
            BuilderMapper.of(F.PREVAILING_DESCRIPTIONS, jsonMapper(ContentAttrSample.LIST_REFERENCE), HostStatisticsBuilder::prevailingDescriptions),
            BuilderMapper.of(F.DUPLICATE_PAGE_SAMPLES, jsonMapper(PageSample.LIST_REFERENCE), HostStatisticsBuilder::duplicatePageSamples)
    ).collect(Collectors.toUnmodifiableMap(BuilderMapper::getName, Function.identity()));

    private static <Field> BiFunction<CHRow, String, Field> jsonMapper(TypeReference<Field> reference) {
        return (r, n) -> JsonMapping.readValue(r.getString(n), reference);
    }

    @Qualifier("legacyMdbTableStorage")
    private final TableProvider tableProvider;
    @Qualifier("legacyMdbClickhouseServer")
    private final ClickhouseServer clickhouseServer;

    @Override
    public ClickhouseServer getClickhouseServer() {
        return clickhouseServer;
    }

    public HostStatistics getHostStatisticsWithoutSamples(WebmasterHostId hostId) {
        return getHostStatisticsInternal(hostId, FIELDS_WITHOUT_SAMPLES);
    }

    public HostStatistics getHostStatisticsWithSamples(WebmasterHostId hostId) {
        return getHostStatisticsInternal(hostId, ALL_FIELDS);
    }

    private HostStatistics getHostStatisticsInternal(WebmasterHostId hostId, Collection<String> fields) {
        ClickhouseTableInfo table = tableProvider.getTable(TableType.HOST_STATISTICS);
        var st = QueryBuilder.select(fields).from(table.getTableName()).where(QueryBuilder.eq(F.HOST_ID, hostId.toStringId()));
        return getClickhouseServer().queryOne(table.chContext(getClickhouseServer(), hostId), st.toString(), chRow -> {
            var builder = HostStatistics.builder();
            for (String field : fields) {
                MAPPERS.get(field).apply(chRow, builder);
            }
            return builder.build();
        }).orElse(null);
    }

    public interface F {
        String HOST_ID = "host_id";
        String DOCS = "docs";
        String DOCS_ON_SEARCH = "docs_on_search";
        String HTML_DOCS = "html_docs";
        String GOOD_DOCS_ON_SEARCH = "good_docs_on_search";
        String EMPTY_TITLES = "empty_titles";
        String EMPTY_DESCRIPTIONS = "empty_descriptions";
        String SLOW_PAGES = "slow_pages";
        String PAGES_WITH_FETCH_TIME = "pages_with_fetch_time";
        String TOTAL_FETCH_TIME = "total_fetch_time";
        String UNIQUE_TITLES = "unique_titles";
        String UNIQUE_DESCRIPTIONS = "unique_descriptions";
        String DUPLICATE_TITLES = "duplicate_titles";
        String DUPLICATE_DESCRIPTIONS = "duplicate_descriptions";
        String MORDA_SAMPLE = "morda_sample";
        String SLOW_PAGE_SAMPLES = "slow_page_samples";
        String EMPTY_TITLE_SAMPLES = "empty_title_samples";
        String EMPTY_DESCRIPTION_SAMPLES = "empty_description_samples";
        String DUPLICATE_PAGE_SAMPLES = "duplicate_page_samples";
        String PREVAILING_TITLES = "prevailing_titles";
        String PREVAILING_DESCRIPTIONS = "prevailing_descriptions";
    }
}
