package ru.yandex.webmaster3.storage.searchquery.dao;

import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import lombok.Setter;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.Days;
import org.joda.time.LocalDate;

import ru.yandex.webmaster3.core.data.WebmasterHostId;
import ru.yandex.webmaster3.core.searchquery.IndicatorUsage;
import ru.yandex.webmaster3.core.searchquery.OrderDirection;
import ru.yandex.webmaster3.core.searchquery.QueryId;
import ru.yandex.webmaster3.core.searchquery.QueryIndicator;
import ru.yandex.webmaster3.storage.clickhouse.LocalClickhouseTableProvider;
import ru.yandex.webmaster3.storage.clickhouse.TableProvider;
import ru.yandex.webmaster3.storage.clickhouse.TableType;
import ru.yandex.webmaster3.storage.clickhouse.table.Top3000ValuesTable;
import ru.yandex.webmaster3.storage.searchquery.DeviceType;
import ru.yandex.webmaster3.storage.searchquery.QueryStat;
import ru.yandex.webmaster3.storage.searchquery.RegionInclusion;
import ru.yandex.webmaster3.storage.util.clickhouse2.ClickhouseException;

/**
 * @author aherman
 */
public class Top3000ValuesCHDao extends AbstractQueriesStatsCHDao {

    @Setter
    private TableProvider tableStorage;
    @Setter
    private String partSuffix;

    protected LocalClickhouseTableProvider getTableProvider() {
        return new Top3000ValuesTable(tableStorage.getTable(TableType.TOP_3000__VALUES), partSuffix);
    }

    public int countQueries(WebmasterHostId hostId, LocalDate dateFrom, LocalDate dateTo,
                            RegionInclusion regionInclusion, Set<Integer> regionIds, DeviceType deviceType) throws ClickhouseException {
        LocalClickhouseTableProvider table = getTableProvider();
        String regionFilter = queryStatisticsFiltersService.getRegionFilter(regionInclusion, regionIds);
        if (!regionFilter.isEmpty()) {
            regionFilter = "AND " + regionFilter;
        }
        String q =
                "SELECT count() AS c FROM ("
                        + "SELECT query_id FROM " + table.getTableName(getClickhouseServer(), hostId)
                        + " PREWHERE host_id = '" + hostId.toStringId() + "'"
                        + " AND date >= '" + dateFrom.toString() + "'"
                        + " AND date <= '" + dateTo.toString() + "'"
                        + " " + regionFilter
                        + " AND " + deviceType.getQueryFilter()
                        + " GROUP BY query_id"
                        + ")";
        return getClickhouseServer().queryOne(table.chContext(getClickhouseServer(), hostId), q, row -> row.getLongUnsafe("c")).orElse(0L).intValue();
    }

    public List<QueryId> getQueryIds(WebmasterHostId hostId,
                                     LocalDate dateFrom, LocalDate dateTo,
                                     List<QueryId> excludeQueries,
                                     RegionInclusion regionInclusion, Set<Integer> regionIds,
                                     DeviceType deviceType,
                                     QueryIndicator orderBy, OrderDirection direction,
                                     int limitFrom, int limit) {
        LocalClickhouseTableProvider table = getTableProvider();
        String orderColumns = "";
        String orderClause = "";
        if (!orderBy.isUsable(IndicatorUsage.IN_ORDER)) {
            orderBy = QueryIndicator.TOTAL_SHOWS_COUNT;
        }
        if (orderBy == QueryIndicator.NAME) {
            orderBy = QueryIndicator.TOTAL_SHOWS_COUNT;
        }
        String currentRangeCondition = "date >= '" + dateFrom.toString() + "'"
                + " AND date <= '" + dateTo.toString() + "' ";
        orderColumns = getOrderColumns(orderBy, currentRangeCondition);
        String orderColumnName = getOrderColumnName(orderBy);
        if (!StringUtils.isEmpty(orderColumnName) && !StringUtils.isEmpty(orderColumns)) {
            if (isPosition(orderBy)) {
                direction = direction.reverse();
            }
            orderClause = "ORDER BY " + orderColumnName;
            orderColumns = " , " + orderColumns;
            if (direction == OrderDirection.ASC) {
                orderClause += " ASC";
            } else {
                orderClause += " DESC";
            }

            orderClause += ", query_id"; // для предсказуемости сортировки при одинаковых значених orderColumnName
        }

        LocalDate whereDateFrom = orderBy.isDynamics()
                ? dateFrom.minusDays(Days.daysBetween(dateFrom, dateTo).getDays() + 1)
                : dateFrom;
        String regionFilter = queryStatisticsFiltersService.getRegionFilter(regionInclusion, regionIds);
        if (!regionFilter.isEmpty()) {
            regionFilter = "AND " + regionFilter;
        }
        String excludeQueriesString;
        if (excludeQueries.isEmpty()) {
            excludeQueriesString = "";
        } else {
            excludeQueriesString = " AND NOT query_id IN(" +
                    excludeQueries.stream().map(l -> Long.toString(l.getQueryId())).collect(Collectors.joining(",")) + ")";
        }
        String q = "SELECT query_id " + orderColumns
                + " FROM " + table.getTableName(getClickhouseServer(), hostId)
                + " WHERE host_id = '" + hostId.toStringId() + "'"
                + " AND date >= '" + whereDateFrom.toString() + "'"
                + " AND date <= '" + dateTo.toString() + "'"
                + " " + excludeQueriesString
                + " " + regionFilter
                + " AND " + deviceType.getQueryFilter()
                + " GROUP BY query_id"
                + " " + orderClause
                + " LIMIT " + limitFrom + ", " + limit;
        return getClickhouseServer().queryAll(table.chContext(getClickhouseServer(), hostId), q, row -> new QueryId(row.getLongUnsafe("query_id")));
    }

    public List<QueryStat> getQueryStat(WebmasterHostId hostId,
                                        List<QueryIndicator> indicators,
                                        LocalDate dateFrom, LocalDate dateTo,
                                        RegionInclusion regionInclusion, Set<Integer> regionIds,
                                        List<QueryId> queryIds, DeviceType deviceType) {
        LocalClickhouseTableProvider table = getTableProvider();
        return getQueryStat(table.getTableName(getClickhouseServer(), hostId), table.getShard(getClickhouseServer(), hostId),
                hostId, dateFrom, dateTo, regionInclusion, regionIds, queryIds, indicators, deviceType);
    }


}
