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

import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;

import lombok.Setter;
import org.joda.time.LocalDate;

import ru.yandex.webmaster3.core.data.WebmasterHostId;
import ru.yandex.webmaster3.core.searchquery.QueryGroupId;
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.searchquery.DayStat;
import ru.yandex.webmaster3.storage.searchquery.DeviceType;
import ru.yandex.webmaster3.storage.searchquery.GroupStat;
import ru.yandex.webmaster3.storage.searchquery.RegionInclusion;
import ru.yandex.webmaster3.storage.util.clickhouse2.AbstractClickhouseDao;
import ru.yandex.webmaster3.storage.util.clickhouse2.ClickhouseException;
import ru.yandex.webmaster3.storage.util.clickhouse2.ClickhouseQueryContext;

/**
 * @author aherman
 */
public class GroupStatistics2CHDao extends AbstractClickhouseDao {

    @Setter
    private TableProvider tableStorage;
    @Setter
    private QueryStatisticsFiltersService queryStatisticsFiltersService;

    protected LocalClickhouseTableProvider getTableProvider() {
        return tableStorage.getTable(TableType.GROUP_STATISTICS);
    }

    @SuppressWarnings("Duplicates")
    public List<GroupStat> getQueryStat(WebmasterHostId hostId,
                                        List<QueryIndicator> indicators,
                                        LocalDate dateFrom, LocalDate dateTo,
                                        RegionInclusion regionInclusion, Set<Integer> regionIds,
                                        Collection<QueryGroupId> groupIds, DeviceType deviceType)
            throws ClickhouseException {
        LocalClickhouseTableProvider table = getTableProvider();
        if (groupIds.isEmpty()) {
            return Collections.emptyList();
        }
        String idsStr = groupIds.stream().map(l -> "'" + l.getGroupId().toString() + "'").collect(Collectors.joining(","));
        String regionFilter = queryStatisticsFiltersService.getRegionFilter(regionInclusion, regionIds);
        if (!regionFilter.isEmpty()) {
            regionFilter = "AND " + regionFilter;
        }
        StringBuilder qBuilder = new StringBuilder();
        qBuilder.append("SELECT "
                + "date"
                + ", group_id"
        );
        Set<String> columns = new HashSet<>();
        addQueryColumns(columns, indicators);
        for (String column : columns) {
            switch (column) {
                case F.ST:
                    qBuilder.append(", sum(shows_1 + shows_2_3 + shows_4_10 + shows_11_50) as " + F.ST);
                    break;
                case F.CT:
                    qBuilder.append(", sum(clicks_1 + clicks_2_3 + clicks_4_10 + clicks_11_50) as " + F.CT);
                    break;
                case F.S1:
                    qBuilder.append(", sum(shows_1) as " + F.S1);
                    break;
                case F.C1:
                    qBuilder.append(", sum(clicks_1) as " + F.C1);
                    break;
                case F.S2_3:
                    qBuilder.append(", sum(shows_2_3) as " + F.S2_3);
                    break;
                case F.C2_3:
                    qBuilder.append(", sum(clicks_2_3) as " + F.C2_3);
                    break;
                case F.S4_10:
                    qBuilder.append(", sum(shows_4_10) as " + F.S4_10);
                    break;
                case F.C4_10:
                    qBuilder.append(", sum(clicks_4_10) as " + F.C4_10);
                    break;
                case F.S11_50:
                    qBuilder.append(", sum(shows_11_50) as " + F.S11_50);
                    break;
                case F.C11_50:
                    qBuilder.append(", sum(clicks_11_50) as " + F.C11_50);
                    break;
                case F.APS2_3:
                    qBuilder.append(", sum(agr_pos_shows_2_3) as " + F.APS2_3);
                    break;
                case F.APC2_3:
                    qBuilder.append(", sum(agr_pos_clicks_2_3) as " + F.APC2_3);
                    break;
                case F.APS4_10:
                    qBuilder.append(", sum(agr_pos_shows_4_10) as " + F.APS4_10);
                    break;
                case F.APC4_10:
                    qBuilder.append(", sum(agr_pos_clicks_4_10) as " + F.APC4_10);
                    break;
                case F.APS11_50:
                    qBuilder.append(", sum(agr_pos_shows_11_50) as " + F.APS11_50);
                    break;
                case F.APC11_50:
                    qBuilder.append(", sum(agr_pos_clicks_11_50) as " + F.APC11_50);
                    break;
                case F.SRP:
                    qBuilder.append(", sum(serps_total) as " + F.SRP);
                    break;
            }
        }
        qBuilder.append(
                " FROM " + table.getTableName()
                        + " PREWHERE host_id = '" + hostId.toStringId() + "'"
                        + " AND date >= '" + dateFrom.toString() + "'"
                        + " AND date <= '" + dateTo.toString() + "'"
                        + " " + regionFilter
                        + " AND group_id IN (" + idsStr + ")"
                        + " AND " + deviceType.getQueryFilter()
                        + " GROUP BY date, group_id"
        );

        ClickhouseQueryContext.Builder ctx = table.chContext(getClickhouseServer(), hostId);

        List<GroupStat> result = getClickhouseServer().queryAll(ctx, qBuilder.toString(), row -> {
            LocalDate date = row.getLocalDate("date");
            String groupId = row.getString("group_id");

            long totalShows = 0;
            long totalClicks = 0;
            long shows_1 = 0;
            long clicks_1 = 0;
            long shows_2_3 = 0;
            long clicks_2_3 = 0;
            long shows_4_10 = 0;
            long clicks_4_10 = 0;
            long shows_11_50 = 0;
            long clicks_11_50 = 0;
            long agr_shows_2_3 = 0;
            long agr_clicks_2_3 = 0;
            long agr_shows_4_10 = 0;
            long agr_clicks_4_10 = 0;
            long agr_shows_11_50 = 0;
            long agr_clicks_11_50 = 0;
            long totalSerps = 0;

            for (String column : columns) {
                switch (column) {
                    case F.ST:
                        totalShows = row.getLongUnsafe(F.ST);
                        break;
                    case F.CT:
                        totalClicks = row.getLongUnsafe(F.CT);
                        break;
                    case F.S1:
                        shows_1 = row.getLongUnsafe(F.S1);
                        break;
                    case F.C1:
                        clicks_1 = row.getLongUnsafe(F.C1);
                        break;
                    case F.S2_3:
                        shows_2_3 = row.getLongUnsafe(F.S2_3);
                        break;
                    case F.C2_3:
                        clicks_2_3 = row.getLongUnsafe(F.C2_3);
                        break;
                    case F.S4_10:
                        shows_4_10 = row.getLongUnsafe(F.S4_10);
                        break;
                    case F.C4_10:
                        clicks_4_10 = row.getLongUnsafe(F.C4_10);
                        break;
                    case F.S11_50:
                        shows_11_50 = row.getLongUnsafe(F.S11_50);
                        break;
                    case F.C11_50:
                        clicks_11_50 = row.getLongUnsafe(F.C11_50);
                        break;

                    case F.APS2_3:
                        agr_shows_2_3 = row.getLongUnsafe(F.APS2_3);
                        break;
                    case F.APC2_3:
                        agr_clicks_2_3 = row.getLongUnsafe(F.APC2_3);
                        break;
                    case F.APS4_10:
                        agr_shows_4_10 = row.getLongUnsafe(F.APS4_10);
                        break;
                    case F.APC4_10:
                        agr_clicks_4_10 = row.getLongUnsafe(F.APC4_10);
                        break;
                    case F.APS11_50:
                        agr_shows_11_50 = row.getLongUnsafe(F.APS11_50);
                        break;
                    case F.APC11_50:
                        agr_clicks_11_50 = row.getLongUnsafe(F.APC11_50);
                        break;

                    case F.SRP:
                        totalSerps = row.getLongUnsafe(F.SRP);
                        break;
                }
            }

            return new GroupStat(date, new QueryGroupId(hostId, UUID.fromString(groupId)),
                    totalShows, totalClicks,
                    shows_1, clicks_1,
                    shows_2_3, clicks_2_3,
                    shows_4_10, clicks_4_10,
                    shows_11_50, clicks_11_50,
                    agr_shows_2_3, agr_clicks_2_3,
                    agr_shows_4_10, agr_clicks_4_10,
                    agr_shows_11_50, agr_clicks_11_50,
                    totalSerps
            );
        });
        result.sort(Comparator.comparing(DayStat::getDate));
        return result;
    }

    @SuppressWarnings("Duplicates")
    private void addQueryColumns(Set<String> columnSet, List<QueryIndicator> indicators) {
        for (QueryIndicator indicator : indicators) {
            switch (indicator) {
                case SHOWS_1_50:
                case TOTAL_SHOWS_COUNT:
                    columnSet.add(F.ST);
                    break;

                case CLICKS_1_50:
                case TOTAL_CLICKS_COUNT:
                    columnSet.add(F.CT);
                    break;

                case TOTAL_CTR:
                    columnSet.add(F.ST);
                    columnSet.add(F.CT);
                    break;

                case TOTAL_SERPS_COUNT:
                    columnSet.add(F.SRP);
                    break;

                case SHOWS_COUNT_1:
                    columnSet.add(F.S1);
                    break;

                case CLICKS_COUNT_1:
                    columnSet.add(F.C1);
                    break;

                case SHOWS_COUNT_2_3:
                    columnSet.add(F.S2_3);
                    break;

                case CLICKS_COUNT_2_3:
                    columnSet.add(F.C2_3);
                    break;

                case SHOWS_COUNT_4_10:
                    columnSet.add(F.S4_10);
                    break;

                case CLICKS_COUNT_4_10:
                    columnSet.add(F.C4_10);
                    break;

                case SHOWS_COUNT_11_50:
                    columnSet.add(F.S11_50);
                    break;

                case CLICKS_COUNT_11_50:
                    columnSet.add(F.C11_50);
                    break;

                case CTR_1:
                    columnSet.add(F.S1);
                    columnSet.add(F.C1);
                    break;

                case CTR_2_3:
                    columnSet.add(F.S2_3);
                    columnSet.add(F.C2_3);
                    break;

                case CTR_4_10:
                    columnSet.add(F.S4_10);
                    columnSet.add(F.C4_10);
                    break;

                case CTR_11_50:
                    columnSet.add(F.S11_50);
                    columnSet.add(F.C11_50);
                    break;

                case AVERAGE_SHOW_POSITION:
                    columnSet.add(F.ST);
                    columnSet.add(F.S1);
                    columnSet.add(F.APS2_3);
                    columnSet.add(F.APS4_10);
                    columnSet.add(F.APS11_50);
                    break;

                case AVERAGE_CLICK_POSITION:
                    columnSet.add(F.CT);
                    columnSet.add(F.C1);
                    columnSet.add(F.APC2_3);
                    columnSet.add(F.APC4_10);
                    columnSet.add(F.APC11_50);
                    break;

                case AVERAGE_SHOW_POSITION_2_3:
                    columnSet.add(F.S2_3);
                    columnSet.add(F.APS2_3);
                    break;

                case AVERAGE_CLICK_POSITION_2_3:
                    columnSet.add(F.C2_3);
                    columnSet.add(F.APC2_3);
                    break;

                case AVERAGE_SHOW_POSITION_4_10:
                    columnSet.add(F.S4_10);
                    columnSet.add(F.APS4_10);
                    break;

                case AVERAGE_CLICK_POSITION_4_10:
                    columnSet.add(F.C4_10);
                    columnSet.add(F.APC4_10);
                    break;

                case AVERAGE_SHOW_POSITION_11_50:
                    columnSet.add(F.S11_50);
                    columnSet.add(F.APS11_50);
                    break;

                case AVERAGE_CLICK_POSITION_11_50:
                    columnSet.add(F.C11_50);
                    columnSet.add(F.APC11_50);
                    break;
            }
        }
    }

    protected static class F {
        private static final String ST = "ST";
        private static final String CT = "CT";
        private static final String S1 = "S1";
        private static final String C1 = "C1";
        private static final String S2_3 = "S2_3";
        private static final String C2_3 = "C2_3";
        private static final String S4_10 = "S4_10";
        private static final String C4_10 = "C4_10";
        private static final String S11_50 = "S11_50";
        private static final String C11_50 = "C11_50";
        private static final String APS2_3 = "APS2_3";
        private static final String APC2_3 = "APC2_3";
        private static final String APS4_10 = "APS4_10";
        private static final String APC4_10 = "APC4_10";
        private static final String APS11_50 = "APS11_50";
        private static final String APC11_50 = "APC11_50";

        private static final String SRP = "SRP";
    }

}
