package ru.yandex.webmaster3.storage.util.yt.lock;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import com.fasterxml.jackson.databind.node.LongNode;
import com.google.common.annotations.VisibleForTesting;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import ru.yandex.webmaster3.storage.util.yt.YtCypressService;
import ru.yandex.webmaster3.storage.util.yt.YtCypressServiceImpl;
import ru.yandex.webmaster3.storage.util.yt.YtException;
import ru.yandex.webmaster3.storage.util.yt.YtPath;
import ru.yandex.webmaster3.storage.util.yt.YtService;

/**
 * ishalaru
 * 12.11.2020
 **/
@Slf4j
@Service
@RequiredArgsConstructor(onConstructor_ = {@Autowired})
public class EphemeralNodeStateChecker {
    private static final String EXPIRATION_TIME = "expiration_time";
    private static final String EXPIRATION_TIME_ATTRIBUTE = '@' + EXPIRATION_TIME;
    private final YtService ytService;
    private List<YtPath> nodes = new ArrayList<>();
    private YtCypressService cypressService;
    @Value("${yt.expiration.wait.timestamp:60000}")
    private long expirationWaitTimestamp;

    public void init() {
        cypressService = new YtCypressServiceImpl(ytService, null);
    }


    public void add(YtPath path) {
        synchronized (nodes) {
            update(path);
            nodes.add(path);
        }
    }

    public boolean update(YtPath node) {
        try {
            if (cypressService.exists(node)) {
                cypressService.set(YtPath.path(node, EXPIRATION_TIME_ATTRIBUTE), LongNode.valueOf(DateTime.now().getMillis() + expirationWaitTimestamp * 2));
                return true;
            }
        } catch (YtException exp) {
            log.error(exp.getMessage(), exp);
        }
        return false;

    }

    @Scheduled(initialDelayString = "10000", fixedDelayString = "${yt.expiration.wait.timestamp:10000}")
    public void check() {
        synchronized (nodes) {
            final Iterator<YtPath> iterator = nodes.iterator();
            while (iterator.hasNext()) {
                final YtPath next = iterator.next();
                if (!update(next)) {
                    iterator.remove();
                }
            }
        }
    }

    @VisibleForTesting
    public void setExpirationWaitTimestamp(long expirationWaitTimestamp) {
        this.expirationWaitTimestamp = expirationWaitTimestamp;
    }
}
