package ru.yandex.webmaster3.storage.indexing2.samples.dao;

import java.util.Arrays;
import java.util.List;

import org.apache.commons.lang3.tuple.Pair;
import org.joda.time.DateTime;

import ru.yandex.webmaster3.core.data.HttpCodeInfo;
import ru.yandex.webmaster3.core.data.WebmasterHostId;
import ru.yandex.webmaster3.core.util.FNVHash;
import ru.yandex.webmaster3.storage.indexing2.samples.data.IndexedUrlSample;
import ru.yandex.webmaster3.storage.util.clickhouse2.AbstractClickhouseDao;
import ru.yandex.webmaster3.storage.util.clickhouse2.ClickhouseQueryContext;
import ru.yandex.webmaster3.storage.util.clickhouse2.condition.Condition;
import ru.yandex.webmaster3.storage.util.clickhouse2.query.OrderBy;
import ru.yandex.webmaster3.storage.util.clickhouse2.query.QueryBuilder;
import ru.yandex.webmaster3.storage.util.clickhouse2.query.Where;
import ru.yandex.webmaster3.storage.util.clickhouse2.query.cases.Case;
import ru.yandex.wmtools.common.util.http.YandexHttpStatus;

/**
 * Created by leonidrom on 22/02/2017.
 */
public class IndexedUrlSampleCHDao extends AbstractClickhouseDao {
    private static final String TABLE_MERGED = "indexed_url_samples_merged";
    public static final int PART_COUNT = 8;

    public List<YandexHttpStatus> getDistinctCodes(WebmasterHostId hostId, Condition filters) {

        Where where = QueryBuilder.selectDistinct(F.HTTP_CODE)
                .from(DB_WEBMASTER3_INDEXING, TABLE_MERGED)
                .where(QueryBuilder.eq(F.HOST_ID, hostId.toString()));

        where = filter(where, filters);
        return getClickhouseServer().queryAll(chContext(hostId),
                where.orderBy(F.HTTP_CODE, OrderBy.Direction.ASC),
                row -> YandexHttpStatus.parseCode(row.getInt(F.HTTP_CODE)));
    }

    public long getSamplesCount(WebmasterHostId hostId, Condition filters) {
        Where where = QueryBuilder.select("count(*)")
                .from(DB_WEBMASTER3_INDEXING, TABLE_MERGED)
                .where(QueryBuilder.eq(F.HOST_ID, hostId.toString()));
        where = filter(where, filters);
        return getClickhouseServer().queryOne(chContext(hostId), where, row -> row.getLongUnsafe(0)).orElse(0L);
    }

    public List<IndexedUrlSample> getSamples(WebmasterHostId hostId, Condition filters,
                                             IndexedUrlSamplesOrderField orderBy, OrderBy.Direction orderDirection,
                                             int limitFrom, int limitSize) {

        Where where = QueryBuilder.select(F.LAST_ACCESS, F.PATH, F.HTTP_CODE)
                .from(DB_WEBMASTER3_INDEXING, TABLE_MERGED)
                .where(QueryBuilder.eq(F.HOST_ID, hostId.toString()));
        where = filter(where, filters);
        return getClickhouseServer().queryAll(chContext(hostId),
                where.orderBy(Arrays.asList(
                        Pair.of(getOrderByField(orderBy), orderDirection),
                        Pair.of(F.PATH, OrderBy.Direction.ASC)
                )).limit(limitFrom, limitSize),
                row -> new IndexedUrlSample(
                        row.getString(F.PATH),
                        HttpCodeInfo.fromHttpStatus(YandexHttpStatus.parseCode(row.getInt(F.HTTP_CODE))),
                        new DateTime(row.getLong(F.LAST_ACCESS))
                )
        );
    }

    public List<IndexedUrlSample> getSamples(WebmasterHostId hostId, String path) {
        Where where = QueryBuilder.select(F.LAST_ACCESS, F.PATH, F.HTTP_CODE)
                .from(DB_WEBMASTER3_INDEXING, TABLE_MERGED)
                .where(QueryBuilder.eq(F.HOST_ID, hostId.toString()))
                .and(QueryBuilder.eq(F.PATH, path));

        return getClickhouseServer().queryAll(chContext(hostId), where,
                row -> new IndexedUrlSample(
                        row.getString(F.PATH),
                        HttpCodeInfo.fromHttpStatus(YandexHttpStatus.parseCode(row.getInt(F.HTTP_CODE))),
                        new DateTime(row.getLong(F.LAST_ACCESS))
                )
        );
    }

    private Where filter(Where st, Condition filters) {
        if (filters == null) {
            return st;
        }
        String filterStr = filters.toQuery();
        return st.and(new Case() {
            @Override
            public String toString() {
                return filterStr;
            }
        });
    }

    private static String getOrderByField(IndexedUrlSamplesOrderField orderField) {
        switch (orderField) {
            case LAST_ACCESS:
                return F.LAST_ACCESS;
            default:
                throw new RuntimeException("Unknown order field " + orderField);
        }
    }

    private ClickhouseQueryContext.Builder chContext(WebmasterHostId hostId) {
        int shardsCount = getClickhouseServer().getShardsCount();
        int shard = (int) FNVHash.hash64Mod(hostId.toString(), shardsCount * PART_COUNT) / PART_COUNT;
        return withShard(shard);
    }

    static class F {
        static final String HOST_ID = "host_id";
        static final String LAST_ACCESS = "last_access";
        static final String PATH = "path";
        static final String HTTP_CODE = "http_code";
    }
}
