package com.github.microservice.core.util.queue;

import com.github.microservice.core.util.os.SystemUtil;
import lombok.*;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.Assert;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

@Slf4j
public class ExecuteQueueUtil {

    private static ScheduledExecutorService executorService = Executors.newScheduledThreadPool(SystemUtil.getCpuCoreCount() * 2);


    static {
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            executorService.shutdownNow();
        }));
    }

    @SneakyThrows
    public static <T> T execute(int maxTryCount, SleepTime sleepTime, Handle<T> handle) {
        Assert.state(maxTryCount > 0, "最大执行次数必须大于0");
        Assert.notNull(sleepTime, "休眠时间不能为空");
        Assert.notNull(handle, "处理器不能为空");

        final long startTime = System.currentTimeMillis();
        final CountDownLatch countDownLatch = new CountDownLatch(1);
        final Item<T> item = new Item<T>() {{
            setMaxTryCount(maxTryCount);
            setStartTime(startTime);
            setSleepTime(sleepTime);
            setCountDownLatch(countDownLatch);
            setHandle(handle);
        }};
        sleepExecute(1, item);
        countDownLatch.await();

        return item.getResult() != null ? item.getResult().getData() : null;
    }

    // 延迟执行
    private static <T> void sleepExecute(int index, Item<T> item) {
        executorService.schedule(() -> {
            try {
                final RunTime runTime = new RunTime() {{
                    setIndex(index);
                    setStartTime(item.getStartTime());
                }};
                //记录结果
                item.setResult(null);
                item.setResult(item.getHandle().execute(runTime));
            } catch (Exception e) {
                e.printStackTrace();
                log.error(e.getMessage());
            }


            if (item.getResult() != null) {
                // 异步则退出阻塞
                if (item.getResult().isAsync()) {
                    countDown(item.getCountDownLatch());
                }

                // 完成则退出阻塞
                if (item.getResult().isDone()) {
                    countDown(item.getCountDownLatch());
                    // 结束，不在执行循环
                    return;
                }
            }

            if (index < item.getMaxTryCount()) {
                sleepExecute(index + 1, item);
            } else {
                countDown(item.getCountDownLatch());
            }
        }, item.getSleepTime().get(index), TimeUnit.MILLISECONDS);
    }

    private static void countDown(CountDownLatch countDownLatch) {
        if (countDownLatch.getCount() > 0) {
            countDownLatch.countDown();
        }
    }

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public static class Item<T> {
        private int maxTryCount;
        private long startTime;
        private SleepTime sleepTime;
        private Handle<T> handle;
        private CountDownLatch countDownLatch;

        //结果集
        private Result<T> result;
    }


    @Data
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    @Accessors(chain = true)
    public static class Result<T> {
        // 返回的数据
        private T data;

        // 是否异步
        private boolean async;

        //是否结束
        private boolean done;

    }


    // 休眠时间
    public interface SleepTime {
        long get(int index);
    }


    // 处理器
    public interface Handle<T> {
        Result<T> execute(RunTime runTime);
    }

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public static class RunTime {
        private int index;
        private long startTime;
    }


//    @SneakyThrows
//    public static void main(String[] args) {
//
//        String a = execute(6, (index) -> {
//            return Map.of(1, 1000L, 2, 1000L, 3, 1000L, 4, 2000L).getOrDefault(index, 3000L);
//        }, (runTime) -> {
//            long time = System.currentTimeMillis() - runTime.getStartTime();
//            var ret = new Result<String>();
//            System.out.println("------------" + Thread.currentThread());
//            System.out.println(runTime.getIndex() + "," + time);
//
//            //异常
//            if (runTime.getIndex() >= 3) {
//                throw new RuntimeException("模拟异常");
//            }
//
////            if (runTime.getIndex() >= 3) {
////                return new Result<String>().setData("放弃").setDone(true);
////            }
//
//            // 时间异步
////            if (time > 3000) {
////                ret.setData("异步线程继续").setAsync(true);
////            }
//
//            // 次数模拟完成
//            if (runTime.getIndex() >= 3) {
//                return new Result<String>().setData("完成").setDone(true);
//            }
//
//
//            return ret;
//        });
//
//
//        System.out.println("结果: " + a);
//
//    }


}
