package ru.yandex.webmaster3.streamer.robot;

import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;

import lombok.Getter;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;

import ru.yandex.kikimr.persqueue.consumer.StreamListener;

/**
 * @author kravchenko99
 * @date 8/4/21
 */

@Slf4j(topic = "CU")
public class ClickhouseUploader<S, E> {
    private static final int CAPACITY = 1000;
    private Thread uploadThread;
    private final ArrayBlockingQueue<UploadRecord<S, E>> buffer = new ArrayBlockingQueue<>(CAPACITY);

    private final String name;
    private final Consumer<List<UploadRecord<S, E>>> consumer;
    private final int maxThreshold;
    private final int minThreshold;
    private final int timeThreshold;


    public ClickhouseUploader(String name,
                              Consumer<List<UploadRecord<S, E>>> consumer,
                              int maxThreshold,
                              int minThreshold,
                              int timeThreshold) {
        this.name = name;
        this.consumer = consumer;
        this.maxThreshold = maxThreshold;
        this.minThreshold = minThreshold;
        this.timeThreshold = timeThreshold;
        uploadThread = new Thread(this::upload);
        uploadThread.start();

    }

    @FunctionalInterface
    public interface UploadRecordCommitter {
        void commit();
    }

    @Getter
    public static class UploadRecord<S, E> {
        private final UploadRecordCommitter committer;
        private final List<S> records;
        private final List<E> examples;

        public UploadRecord(UploadRecordCommitter committer, List<S> records, List<E> examples) {
            this.committer = committer;
            this.records = records;
            this.examples = examples;
        }

        public int count() {
            return records.size();
        }
    }

    public void putRecord(UploadRecord<S, E> r) throws InterruptedException {
        buffer.put(r);
    }

    @Scheduled(fixedDelay = 20000, initialDelay = 20000)
    private void checkUploadThread() {
        log.info("{}: check ClickhouseUploader is alive - {}", name, uploadThread.isAlive());
        if (!uploadThread.isAlive() && !Thread.interrupted()) {
            uploadThread = new Thread(this::upload);
            uploadThread.start();
        }
    }

    @SneakyThrows
    private void upload() {
        Instant instant = Instant.now();
        int cnt = 0;
        List<UploadRecord<S, E>> recordsForUpload = new ArrayList<>();
        while (!Thread.interrupted()) {
            while (cnt < maxThreshold && buffer.size() > 0) {
                UploadRecord<S, E> take = buffer.take();
                cnt += take.count();
                recordsForUpload.add(take);
            }
            if (cnt < minThreshold && instant.plusSeconds(timeThreshold).isAfter(Instant.now())) {
                TimeUnit.SECONDS.sleep(1);
                continue;
            }

            try {
                consumer.accept(recordsForUpload);
            } catch (Exception e) {
                log.error("{}: while save records error was occurred - {}", name, e.getMessage(), e);
            }
            cnt = 0;
            recordsForUpload = new ArrayList<>();
            instant = Instant.now();
        }
    }
}
