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

import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;

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

import ru.yandex.webmaster3.core.notification.LanguageEnum;
import ru.yandex.webmaster3.storage.importanturls.data.ImportantUrlsChange;
import ru.yandex.webmaster3.storage.mirrors.dao.AbstractNotificationsChangesCHDao;
import ru.yandex.webmaster3.storage.user.message.content.MessageContent;
import ru.yandex.webmaster3.storage.user.notification.NotificationType;
import ru.yandex.webmaster3.storage.util.JsonDBMapping;
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.query.OrderBy;

/**
 * Created by Oleg Bazdyrev on 2019-06-06.
 */
public class ImportantUrlsChangesCHDao extends AbstractNotificationsChangesCHDao {

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

    @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 NOTIFICATION_TYPE = "notification_type";
        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";
        String CHANGES = "changes";
    }

    @Value
    public static class Message extends AbstractMessage {
        List<ImportantUrlsRecordPair> changes;

        @JsonCreator
        public Message(@NonNull CHRow row) {
            super(
                    row.getHostId(F.HOST_ID),
                    NotificationType.valueOf(row.getString(F.NOTIFICATION_TYPE)),
                    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)
            );
            try {
                this.changes = JsonDBMapping.OM.readValue(row.getString(F.CHANGES), ImportantUrlsRecordPair.LIST_TYPE_REFERENCE);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }

        public MessageContent getContent() {
            switch (notificationType) {
                case URL_TITLE_CHANGE:
                    return new ImportantUrlsChange.TitleChangeAllHostPages(hostId,
                            changes.stream().map(ImportantUrlsRecordPair::titleChange)
                                    .collect(Collectors.toList()));
                case URL_INDEXING_LAST_ACCESS_CHANGE:
                    return new ImportantUrlsChange.IndexingLastAccessChangeAllHostPages(hostId,
                            changes.stream().map(ImportantUrlsRecordPair::indexingLastAccessChange)
                                    .collect(Collectors.toList()));
                case URL_INDEXING_STATUS_CHANGE:
                    return new ImportantUrlsChange.HttpCodeChangeAllHostPages(hostId,
                            changes.stream().map(ImportantUrlsRecordPair::httpCodeChange)
                                    .collect(Collectors.toList()));
                case URL_SEARCH_LAST_ACCESS_CHANGE:
                    return new ImportantUrlsChange.SearchLastAccessChangeAllHostPages(hostId,
                            changes.stream().map(ImportantUrlsRecordPair::searchLastAccessChange)
                                    .collect(Collectors.toList()));
                case URL_SEARCH_STATUS_CHANGE:
                    return new ImportantUrlsChange.SearchUrlStatusChangeAllHostPages(hostId,
                            changes.stream().map(ImportantUrlsRecordPair::searchUrlStatusChange)
                                    .collect(Collectors.toList()));
                case URL_DESCRIPTION_CHANGE:
                    return new ImportantUrlsChange.DescriptionChangeAllHostPages(hostId,
                            changes.stream().map(ImportantUrlsRecordPair::descriptionChange)
                                    .collect(Collectors.toList()));
                case URL_REL_CANONICAL_TARGET_CHANGE:
                    return new ImportantUrlsChange.RelCanonicalTargetChangeAllHostPages(hostId,
                            changes.stream().map(ImportantUrlsRecordPair::relCanonicalTargetChange)
                                    .collect(Collectors.toList()));
                default:
                    throw new IllegalStateException();
            }
        }
    }

}
