package ru.yandex.webmaster3.storage.user.message;

import com.datastax.driver.core.utils.UUIDs;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ru.yandex.webmaster3.core.data.WebmasterHostId;
import ru.yandex.webmaster3.core.notification.UTMLabels;
import ru.yandex.webmaster3.storage.user.message.content.MessageContent;

import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.UUID;

/**
 * @author avhaliullin
 */
public class UserMessageInfo {
    private static final Logger log = LoggerFactory.getLogger(UserMessageInfo.class);
    private final MessageId id;
    private final long userId;
    private final UUID userUUID;
    private final WebmasterHostId hostName;
    private final boolean read;
    private final boolean critical;
    private final DateTime time;
    private final MessageContent content;
    private boolean deleted = false;

    private UserMessageInfo(MessageId messageId, long userId, UUID userUUID, WebmasterHostId hostId, boolean read,
                            boolean critical, DateTime time, MessageContent content, boolean deleted) {
        this.id = messageId;
        this.userId = userId;
        this.userUUID = userUUID;
        this.hostName = hostId;
        this.read = read;
        this.critical = critical;
        this.time = time == null ? new DateTime(UUIDs.unixTimestamp(messageId.getEventUuid())) : time;
        this.content = content;
        this.deleted = deleted;
    }


    private UserMessageInfo(MessageId messageId, long userId, WebmasterHostId hostId, boolean read, boolean critical,
                            DateTime time, MessageContent content) {
        this.id = messageId;
        this.userId = userId;
        this.userUUID = null;
        this.hostName = hostId;
        this.read = read;
        this.critical = critical;
        this.time = time == null ? new DateTime(UUIDs.unixTimestamp(messageId.getEventUuid())) : time;
        this.content = content;
    }

    private UserMessageInfo(MessageId messageId, long userId, WebmasterHostId hostId, boolean read, boolean critical,
                            DateTime time, MessageContent content, boolean deleted) {
        this(messageId, userId, hostId, read, critical, time, content);
        this.deleted = deleted;
    }

    private UserMessageInfo(MessageId messageId, UUID userUUID, WebmasterHostId hostId, boolean read, boolean critical,
                            DateTime time, MessageContent content) {
        this.id = messageId;
        this.userId = 0;
        this.userUUID = userUUID;
        this.hostName = hostId;
        this.read = read;
        this.critical = critical;
        this.time = time == null ? new DateTime(UUIDs.unixTimestamp(messageId.getEventUuid())) : time;
        this.content = content;
    }

    private UserMessageInfo(MessageId messageId, UUID userUUID, WebmasterHostId hostId, boolean read, boolean critical,
                            DateTime time, MessageContent content, boolean deleted) {
        this(messageId, userUUID, hostId, read, critical, time, content);
        this.deleted = deleted;
    }

    public long getUserId() {
        return userId;
    }

    public UUID getUserUUID() {
        return userUUID;
    }


    public WebmasterHostId getHostId() {
        return hostName;
    }

    public boolean isRead() {
        return read;
    }

    public MessageId getId() {
        return id;
    }

    public DateTime getTime() {
        return time;
    }

    public MessageContent getContent() {
        return content;
    }

    public boolean isCritical() {
        return critical;
    }

    public boolean isDeleted() {
        return deleted;
    }

    public UTMLabels getUTMLabels() {
        return MessageUTMUtils.getLabelsForMessage(content, time);
    }

    private static MessageDigest getMessageDigest() {
        try {
            MessageDigest e = MessageDigest.getInstance("MD5");
            e.reset();
            return e;
        } catch (NoSuchAlgorithmException var1) {
            throw new RuntimeException(var1);
        }
    }

    private static long messageHash(String data, WebmasterHostId hostId) {
        if (hostId != null) {
            data = data + hostId.toStringId();
        }
        MessageDigest digest = getMessageDigest();

        digest.update(data.getBytes(StandardCharsets.UTF_8));

        BigInteger number = new BigInteger(1, digest.digest());
        return number.longValue();
    }

    public UserMessageInfo withOtherContent(MessageContent content) {
        return new UserMessageInfo(id, userId, userUUID, hostName, read, critical, time, content, deleted);
    }

    public static UserMessageInfo createWithMessageId(MessageId messageId, long userId, WebmasterHostId hostId,
                                                      boolean read, boolean critical, DateTime time, MessageContent content) {
        return new UserMessageInfo(messageId, userId, hostId, read, critical, time, content);
    }

    public static UserMessageInfo createWithMessageId(MessageId messageId, UUID userUUID, WebmasterHostId hostId,
                                                      boolean read, boolean critical, DateTime time, MessageContent content) {
        return new UserMessageInfo(messageId, userUUID, hostId, read, critical, time, content);
    }

    public static UserMessageInfo createWithEventUUID(UUID eventUuid, long userId, WebmasterHostId hostId,
                                                      boolean critical, MessageContent content) {
        return new UserMessageInfo(new MessageId(eventUuid, messageHash(content.hashString(), hostId)), userId, hostId, false, critical, DateTime.now(), content);
    }

    public static UserMessageInfo createWithEventUUID(UUID eventUuid, UUID userUUID, WebmasterHostId hostId,
                                                      boolean critical, MessageContent content) {
        return new UserMessageInfo(new MessageId(eventUuid, messageHash(content.hashString(), hostId)), userUUID, hostId, false, critical, DateTime.now(), content);
    }

    // Для удаленного сообщения важны только поля из ключа, дата и флаг deleted
    public static UserMessageInfo createDeleted(MessageId messageId, long userId, UUID userUUID) {
        return new UserMessageInfo(messageId, userId, userUUID, null, false, false, DateTime.now(), null, true);
    }

    public static UserMessageInfo createWithRead(UserMessageInfo message, boolean read) {
        if (message.isDeleted()) {
            log.error("Logic of delete messages is broken!");
        }
        return new UserMessageInfo(message.getId(), message.getUserId(), message.getUserUUID(), message.getHostId(), read, message.isCritical(), message.getTime(), message.getContent(), message.isDeleted());
    }

    public UserMessageInfo withUUID(UUID userUUID) {
        return new UserMessageInfo(id, userId, userUUID, hostName, read, critical, time, content, deleted);
    }
}
