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

import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;

import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.tuple.Pair;
import org.joda.time.LocalDate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import ru.yandex.webmaster3.core.iks.data.Sqi;
import ru.yandex.webmaster3.core.util.W3Collectors;
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.iks.util.IksTableUtil;
import ru.yandex.webmaster3.storage.util.clickhouse2.AbstractClickhouseDao;
import ru.yandex.webmaster3.storage.util.clickhouse2.CHPrimitiveType;
import ru.yandex.webmaster3.storage.util.clickhouse2.CHTable;
import ru.yandex.webmaster3.storage.util.clickhouse2.ClickhouseServer;
import ru.yandex.webmaster3.storage.util.clickhouse2.MdbClickhouseServer;
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;

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

    public static final String HISTORY_TABLE_NAME = "iks_history";
    // историю шардируем, последнее значение - нет
    public static final CHTable HISTORY_TABLE = CHTable.builder()
            .database(AbstractClickhouseDao.DB_WEBMASTER3)
            .name("tmp_" + HISTORY_TABLE_NAME + "_%s")
            .sharded(true)
            .partitionBy("toYear(date)")
            .keyField(F.OWNER, CHPrimitiveType.String)
            .keyField(F.DATE, CHPrimitiveType.Date)
            .field(F.MAIN_MIRROR, CHPrimitiveType.String)
            .field(F.IKS, CHPrimitiveType.Int64)
            .field(F.PREV_IKS, CHPrimitiveType.Int64)
            .field(F.TITLE, CHPrimitiveType.String)
            .build();

    // историю шардируем, последнее значение - нет
    public static final CHTable LAST_STATE_TABLE = CHTable.builder()
            .database(AbstractClickhouseDao.DB_WEBMASTER3)
            .name("iks_%s")
            .sharded(false)
            .partitionBy("")
            .keyField(F.OWNER, CHPrimitiveType.String)
            .field(F.DATE, CHPrimitiveType.Date)
            .field(F.MAIN_MIRROR, CHPrimitiveType.String)
            .field(F.IKS, CHPrimitiveType.Int64)
            .field(F.PREV_IKS, CHPrimitiveType.Int64)
            .field(F.TITLE, CHPrimitiveType.String)
            .build();

    private final TableProvider legacyMdbTableStorage;
    private final MdbClickhouseServer legacyMdbClickhouseServer;

    @Override
    protected ClickhouseServer getClickhouseServer() {
        return legacyMdbClickhouseServer;
    }

    public Map<String, List<Pair<LocalDate, Long>>> getIksHistory(Collection<String> owners, LocalDate dateFrom, LocalDate dateTo) {
        Map<Integer, List<String>> ownersByShard = owners.stream().collect(Collectors.groupingBy(this::getShard));

        return ownersByShard.entrySet().stream()
                .map(p -> getIksHistoryByShard(p.getValue(), dateFrom, dateTo, p.getKey()))
                .map(Map::entrySet)
                .flatMap(Collection::stream)
                .collect(W3Collectors.toHashMap());
    }

    private Map<String, List<Pair<LocalDate, Long>>> getIksHistoryByShard(Collection<String> owners, LocalDate dateFrom, LocalDate dateTo, int shard) {
        Where st = QueryBuilder.select(F.OWNER, F.DATE, F.IKS)
                .from(IksTableUtil.DATABASE, HISTORY_TABLE_NAME)
                .where(QueryBuilder.in(F.OWNER, owners));

        if (dateFrom != null) {
            st = st.and(QueryBuilder.gte(F.DATE, dateFrom));
        }

        if (dateTo != null) {
            st = st.and(QueryBuilder.lte(F.DATE, dateTo));
        }

        return getClickhouseServer().collectAll(withShard(shard),
                st.orderBy(F.DATE, OrderBy.Direction.ASC).toString(),
                Collectors.groupingBy(chRow -> chRow.getString(F.OWNER),
                        Collectors.mapping(
                                chRow -> Pair.of(chRow.getLocalDate(F.DATE), chRow.getLongUnsafe(F.IKS)),
                                Collectors.toList()
                        )
                )
        );
    }

    public Optional<Sqi> getLastIks(String owner) {
        return Optional.ofNullable(getLastIks(Collections.singleton(owner)).get(owner));
    }

    public Map<String, Sqi> getLastIks(Collection<String> owners) {
        ClickhouseTableInfo table = legacyMdbTableStorage.getTable(TableType.IKS);

        var st = QueryBuilder.select(F.DATE, F.OWNER, F.IKS, F.PREV_IKS, F.MAIN_MIRROR, F.TITLE)
                .from(table.getLocalTableName())
                .where(QueryBuilder.in(F.OWNER, owners));

        return getClickhouseServer().queryAll(st.toString(),
                r -> new Sqi(r.getLocalDate(F.DATE), r.getString(F.OWNER), r.getString(F.MAIN_MIRROR),
                        (int) r.getLongUnsafe(F.IKS), (int) r.getLongUnsafe(F.PREV_IKS), r.getString(F.TITLE)))
                .stream().collect(Collectors.toMap(Sqi::getOwner, Function.identity()));
    }

    private interface F {
        String OWNER = "owner";
        String DATE = "date";
        String MAIN_MIRROR = "main_mirror";
        String IKS = "iks";
        String PREV_IKS = "prev_iks";
        String TITLE = "title";

    }
}
