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

import java.util.List;
import java.util.UUID;

import com.datastax.driver.core.utils.UUIDs;
import org.apache.commons.lang3.tuple.Pair;
import org.joda.time.DateTime;
import org.springframework.stereotype.Repository;

import ru.yandex.webmaster3.core.util.enums.EnumResolver;
import ru.yandex.webmaster3.core.util.functional.Bijection;
import ru.yandex.webmaster3.storage.notifications.NotificationProgress;
import ru.yandex.webmaster3.storage.notifications.NotificationZKHelper;
import ru.yandex.webmaster3.storage.util.ydb.AbstractYDao;
import ru.yandex.webmaster3.storage.util.ydb.querybuilder.typesafe.DataMapper;
import ru.yandex.webmaster3.storage.util.ydb.querybuilder.typesafe.Field;
import ru.yandex.webmaster3.storage.util.ydb.querybuilder.typesafe.FieldMapper;
import ru.yandex.webmaster3.storage.util.ydb.querybuilder.typesafe.Fields;

/**
 * Created by Oleg Bazdyrev on 12/01/2022.
 */
@Repository
public class CommonNotificationProgressYDao extends AbstractYDao {

    protected CommonNotificationProgressYDao() {
        super(PREFIX_NOTIFICATION, "common_notification_progress");
    }

    public boolean haveProgressRecord(CommonNotificationType type, UUID notificationId) {
        return select(F.NOTIFICATION_ID)
                .where(F.TYPE.eq(type))
                .and(F.CREATED.eq(new DateTime(UUIDs.unixTimestamp(notificationId))))
                .and(F.NOTIFICATION_ID.eq(notificationId)).queryOne() != null;
    }

    public Pair<NotificationProgress, Integer> getRecordIfExists(CommonNotificationType type, UUID notificationId) {
        return select(PROGRESS_AND_VERSION)
                .where(F.TYPE.eq(type))
                .and(F.CREATED.eq(new DateTime(UUIDs.unixTimestamp(notificationId))))
                .and(F.NOTIFICATION_ID.eq(notificationId)).queryOne();
    }

    public void updateRecord(CommonNotificationType type, UUID notificationId, NotificationProgress progress, int version) {
        update().with(F.PROGRESS.set(progress)).and(F.VERSION.set(version))
                .where(F.TYPE.eq(type))
                .and(F.CREATED.eq(new DateTime(UUIDs.unixTimestamp(notificationId))))
                .and(F.NOTIFICATION_ID.eq(notificationId))
                .execute();
    }

    public void createRecord(CommonNotificationType type, UUID notificationId, NotificationProgress progress) {
        upsert(F.TYPE.value(type),
                F.CREATED.value(new DateTime(UUIDs.unixTimestamp(notificationId))),
                F.NOTIFICATION_ID.value(notificationId),
                F.PROGRESS.value(progress),
                F.VERSION.value(0)
        ).execute();
    }

    public void deleteRecord(CommonNotificationType type, UUID notificationId) {
        delete().where(F.TYPE.eq(type))
                .and(F.CREATED.eq(new DateTime(UUIDs.unixTimestamp(notificationId))))
                .and(F.NOTIFICATION_ID.eq(notificationId))
                .execute();
    }

    public List<UUID> listRecords(CommonNotificationType type) {
        return select(F.NOTIFICATION_ID).where(F.TYPE.eq(type))
                .queryForList(FieldMapper.create(F.CREATED, uuid -> new DateTime(UUIDs.unixTimestamp(uuid))));
    }

    private static final DataMapper<Pair<NotificationProgress, Integer>> PROGRESS_AND_VERSION = DataMapper.create(F.PROGRESS, F.VERSION, Pair::of);

    private interface F {
        Field<CommonNotificationType> TYPE = Fields.stringEnumField("type", CommonNotificationType.R);
        Field<DateTime> CREATED = Fields.jodaDateTimeField("created");
        Field<UUID> NOTIFICATION_ID = Fields.uuidField("notification_id");
        Field<NotificationProgress> PROGRESS = Fields.byteArrayField("progress").map(new Bijection<>() {
            @Override
            public NotificationProgress leftToRight(byte[] bytes) {
                return NotificationZKHelper.getNotificationProgress(bytes);
            }

            @Override
            public byte[] rightToLeft(NotificationProgress notificationProgress) {
                return NotificationZKHelper.dumpNotificationProgress(notificationProgress);
            }
        });
        Field<Integer> VERSION = Fields.intField("version");
    }

    public enum CommonNotificationType {
        IMPORTANT_URLS_CHANGES,
        MIRRORS_CHANGES,
        DISPLAY_NAMES_CHANGES,
        ;

        public static final EnumResolver<CommonNotificationType> R = EnumResolver.er(CommonNotificationType.class);

    }
}
