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

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

import com.fasterxml.jackson.annotation.JsonCreator;
import com.google.common.collect.ImmutableList;
import lombok.Getter;
import lombok.NonNull;
import org.apache.commons.lang3.tuple.Pair;
import org.joda.time.Duration;

import ru.yandex.webmaster3.core.data.WebmasterHostId;
import ru.yandex.webmaster3.core.notification.LanguageEnum;
import ru.yandex.webmaster3.storage.user.message.content.MessageContent;
import ru.yandex.webmaster3.storage.user.notification.NotificationType;
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.ClickhouseException;
import ru.yandex.webmaster3.storage.util.clickhouse2.ClickhouseQueryContext;
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.Statement;

/**
 * Created by kravchenko99 on 2020-02-18.
 */

// нужно для отправки сообщений про изменение зеркал
public class MirrorsChangesCHDao extends AbstractNotificationsChangesCHDao {

    public static final CHTable TABLE = CHTable.builder()
            .database(DB_WEBMASTER3_NOTIFICATIONS)
            .name("mirrors_changes_%s")
            .partitionBy("toYYYYMM(" + F.DATE + ")")
            .keyField(F.DATE, CHPrimitiveType.Date)
            .keyField(F.HOST_ID, CHPrimitiveType.String)
            .field(F.OLD_MAIN_MIRROR, CHPrimitiveType.String)
            .field(F.NEW_MAIN_MIRROR, CHPrimitiveType.String)
            .field(F.USER_ID, CHPrimitiveType.UInt64)
            .field(F.FIO, CHPrimitiveType.String)
            .field(F.EMAIL, CHPrimitiveType.String)
            .field(F.LANGUAGE, CHPrimitiveType.String)
            .field(F.CHANNEL_EMAIL, CHPrimitiveType.UInt8)
            .field(F.LOGIN, CHPrimitiveType.String)
            .field(F.CHANNEL_SERVICE, CHPrimitiveType.UInt8)
            .build();
    private static final List<Pair<Object, OrderBy.Direction>> ORDER_BY = ImmutableList.of(
            Pair.of(F.HOST_ID, OrderBy.Direction.ASC),
            Pair.of(F.USER_ID, OrderBy.Direction.ASC)
    );

    public List<Pair<WebmasterHostId, WebmasterHostId>> getAllHostsIdWithNewMirrors(String tableId) throws ClickhouseException {
        ClickhouseQueryContext.Builder context = ClickhouseQueryContext.useDefaults()
                .setHost(getClickhouseServer().pickAliveHostOrFail(SHARD));
        String tableName = getTable().replicatedMergeTreeTableName(0, tableId);

        Statement statement = QueryBuilder
                .select(F.HOST_ID, F.NEW_MAIN_MIRROR)
                .from(getTable().getDatabase(), tableName);

        return getClickhouseServer().collectAll(context, statement.toString(),
                Collectors.mapping(row -> Pair.of(row.getHostId(F.HOST_ID), row.getHostId(F.NEW_MAIN_MIRROR)),
                        Collectors.toList()));
    }

    public Map<WebmasterHostId, WebmasterHostId> getHostsForAutoVerification(String tableId) throws ClickhouseException {
        ClickhouseQueryContext.Builder context = ClickhouseQueryContext.useDefaults()
                .setTimeout(Duration.standardMinutes(2))
                .setHost(getClickhouseServer().pickAliveHostOrFail(SHARD));

        String tableName = getTable().replicatedMergeTreeTableName(0, tableId);

        String query = String.format("SELECT DISTINCT host_id, new_main_mirror " +
                "FROM %s.%s WHERE host_id!=new_main_mirror", getTable().getDatabase(), tableName);

        return getClickhouseServer().collectAll(context, query,
                Collectors.toMap(row -> row.getHostId(F.HOST_ID), row -> row.getHostId(F.NEW_MAIN_MIRROR)));
    }

    @Override
    public CHTable getTable() {
        return TABLE;
    }

    @Override
    public List<Pair<Object, OrderBy.Direction>> getOrderBy() {
        return ORDER_BY;
    }

    @Override
    protected AbstractMessage getMapper(CHRow row) {
        return new Message(row);
    }

    public interface F {
        String DATE = "date";
        String HOST_ID = "host_id";
        String OLD_MAIN_MIRROR = "old_main_mirror";
        String NEW_MAIN_MIRROR = "new_main_mirror";
        String USER_ID = "user_id";
        String LOGIN = "login";
        String FIO = "fio";
        String EMAIL = "email";
        String LANGUAGE = "language";
        String CHANNEL_EMAIL = "channel_email";
        String CHANNEL_SERVICE = "channel_service";
    }

    @Getter
    public static class Message extends AbstractMessage {
        private final WebmasterHostId oldMainMirror;
        private final WebmasterHostId newMainMirror;

        @JsonCreator
        public Message(@NonNull CHRow row) {
            super(
                    row.getHostId(F.HOST_ID),
                    NotificationType.MAIN_MIRROR_UPDATE,
                    row.getLongUnsafe(F.USER_ID),
                    row.getInt(F.CHANNEL_EMAIL) > 0,
                    row.getInt(F.CHANNEL_SERVICE) > 0,
                    row.getString(F.EMAIL),
                    row.getString(F.FIO),
                    LanguageEnum.fromString(row.getString(F.LANGUAGE)),
                    row.getString(F.LOGIN)
            );
            this.oldMainMirror = row.getHostId(F.OLD_MAIN_MIRROR);
            this.newMainMirror = row.getHostId(F.NEW_MAIN_MIRROR);
        }

        public MessageContent getContent() {
            return new MessageContent.MainMirrorChanged(hostId, oldMainMirror, newMainMirror);
        }
    }
}
