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

import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collector;
import java.util.stream.Collectors;

import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import org.jetbrains.annotations.Nullable;
import org.joda.time.Duration;
import org.springframework.beans.factory.annotation.Autowired;

import ru.yandex.webmaster3.core.data.WebmasterHostId;
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.CHPrimitiveType;
import ru.yandex.webmaster3.storage.util.clickhouse2.CHRow;
import ru.yandex.webmaster3.storage.util.clickhouse2.CHTable;
import ru.yandex.webmaster3.storage.util.clickhouse2.ClickhouseQueryContext;
import ru.yandex.webmaster3.storage.util.clickhouse2.query.QueryBuilder;
import ru.yandex.webmaster3.storage.util.clickhouse2.query.Statement;

@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class MainMirrorsCHDao extends AbstractClickhouseDao {

    private static final String PREFIX_TABLE_NAME = "main_mirrors_";
    private static final String PREFIX_TABLE_NAME_V2 = "main_mirrors2_";
    private static final String PREFIX_TABLE_NAME_ALL_MIRRORS = "all_main_mirrors_";
    private static final String DATABASE = DB_WEBMASTER3;
    public static final CHTable TABLE = CHTable.builder()
            .database(DATABASE)
            .name(PREFIX_TABLE_NAME + "%s")
            .sharded(false)
            .partitionBy("toYYYYMM(date)")
            .keyField(F.DATE, CHPrimitiveType.Date)
            .keyField(F.HOST, CHPrimitiveType.String)
            .field(F.MAIN_HOST, CHPrimitiveType.String)
            .build();

    public static final CHTable TABLE_V2 = CHTable.builder()
            .database(DATABASE)
            .name(PREFIX_TABLE_NAME_V2 + "%s")
            .sharded(false)
            .partitionBy("toYYYYMM(date)")
            .keyField(F.DATE, CHPrimitiveType.Date)
            .keyField(F.HOST, CHPrimitiveType.String)
            .field(F.MAIN_HOST, CHPrimitiveType.String)
            .build();

    public static final CHTable TABLE_ALL_MIRRORS = CHTable.builder()
            .database(DATABASE)
            .name(PREFIX_TABLE_NAME_ALL_MIRRORS + "%s")
            .partitionBy("toYYYYMM(date)")
            .keyField(F.DATE, CHPrimitiveType.Date)
            .keyField(F.HOST, CHPrimitiveType.String)
            .field(F.MAIN_HOST, CHPrimitiveType.String)
            .build();
    public static final TableType MIRRORS_TABLE = TableType.MIRRORS2;
    @Setter
    private TableProvider tableStorage;

    @Setter
    @Getter
    private boolean isMdb;

    public String getTableVersion() {
        return tableStorage.getTable(MIRRORS_TABLE).getLocalTableName();
    }

    private final static Collector<CHRow, ?, Map<WebmasterHostId, WebmasterHostId>> COLLECTOR =
            Collectors.toMap(
                    x -> getNullableHostId(x, F.HOST),
                    x -> getNullableHostId(x, F.MAIN_HOST),
                    (oldValue, newValue) -> newValue
            );

    private final static Duration TIMEOUT = Duration.standardSeconds(90);

    public static String getDatabase() {
        return DATABASE;
    }

    public static String getPrefix() {
        return PREFIX_TABLE_NAME;
    }

    public Map<WebmasterHostId, WebmasterHostId> getMainMirrors(Set<WebmasterHostId> hosts) {
        if (hosts.isEmpty()) {
            return Collections.emptyMap();
        }
        final ClickhouseTableInfo table = tableStorage.getTable(MIRRORS_TABLE);
        final Statement st = QueryBuilder.select(F.HOST, F.MAIN_HOST).from(table.getLocalTableName())
                .prewhere(QueryBuilder.in(F.HOST, hosts));
        return collectAll(st, COLLECTOR);
    }

    public Map<WebmasterHostId, WebmasterHostId> collectAll(String tableVersion) {

        final Statement select = QueryBuilder.select(F.HOST, F.MAIN_HOST).from(tableVersion);
        return getClickhouseServer().collectAll(ClickhouseQueryContext.useDefaults()
                        .setTimeout(TIMEOUT).setCredentials(getClickhouseServer().getServiceUserCredentials()),
                select.toString(), COLLECTOR);
    }

    @Nullable
    public WebmasterHostId getMainMirror(WebmasterHostId host) {
        final ClickhouseTableInfo table = tableStorage.getTable(MIRRORS_TABLE);
        final Statement st = QueryBuilder.select(F.MAIN_HOST).from(table.getLocalTableName())
                .where(QueryBuilder.eq(F.HOST, host.toStringId()));
        return getClickhouseServer().queryOne(st.toString(), r -> r.getHostId(F.MAIN_HOST)).orElse(null);
    }

    // методы выше отвечают из таблицы отфильтрованной по wmc хостам
    // поэтому в некоторых случаях неглавное зеркало может прийти как главное
    // данный метод возвращает 100% главное или нет, как на поиске
    public Map<WebmasterHostId, WebmasterHostId> getMainMirrorsFromAllMirrors(Collection<WebmasterHostId> hosts) {
        if (hosts.isEmpty()) {
            return Collections.emptyMap();
        }
        final ClickhouseTableInfo table = tableStorage.getTable(TableType.ALL_MIRRORS);
        final Statement st = QueryBuilder.select(F.HOST, F.MAIN_HOST).from(table.getLocalTableName())
                .prewhere(QueryBuilder.in(F.HOST, hosts));
        ClickhouseQueryContext.Builder chQCtx = ClickhouseQueryContext.useDefaults()
                .setHost(getClickhouseServer().pickAliveHostOrFail(0));
        Map<WebmasterHostId, WebmasterHostId> mainMirrors =
                getClickhouseServer().collectAll(chQCtx, st.toString(), COLLECTOR);
        hosts.forEach(host -> mainMirrors.putIfAbsent(host, host));
        return mainMirrors;
    }

    public interface F {
        String DATE = "date";
        String HOST = "host";
        String MAIN_HOST = "main_host";
    }
}
