package com.unibuy.smartdevice;

import com.unibuy.smartdevice.exception.ErrorCode;
import com.unibuy.smartdevice.exception.Logs;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public abstract class SchedulerAbstract implements Runnable {
    public class SchedulerThreadFactory implements ThreadFactory {
        private final AtomicInteger threadNumber = new AtomicInteger(1);
        private final String namePrefix;

        public SchedulerThreadFactory(String className) {
            namePrefix = className + "-Thread-";
        }

        @Override
        public Thread newThread(Runnable r) {
            Thread thread = new Thread(r, namePrefix + threadNumber.getAndIncrement());
            thread.setDaemon(true); // 設定為守護執行緒
            return thread;
        }
    }

    private volatile ScheduledExecutorService scheduler;
    private final Logs logs;
    private volatile boolean isRun;
    private boolean isLoop;
    private String description;

    public boolean isRun() {
        return isRun;
    }

    public void setRun(boolean run) {
        this.isRun = run;
    }

    public Logs getLogs() {
        return logs;
    }

    public boolean isLoop() {
        return isLoop;
    }

    private void setLoop(boolean loop) {
        isLoop = loop;
    }

    public String getDescription() {
        return description;
    }

    public ScheduledExecutorService getScheduler() {
        return scheduler;
    }

    public SchedulerAbstract() {
        this.logs = new Logs(setCls());
        this.isRun = true;
        this.description = description();
    }

    @Override
    public void run() {
        try {
            if (isRun()) {
                execute(); // 執行具體邏輯

                if (isLoop()) {
                    logs.info("loop run: " + getDescription());
                } else {
                    logs.info("one run: " + getDescription());
                    shutdown();
                }
            } else {
                shutdown();
            }
        } catch (Exception e) {
            logs.warning(e);
        }
    }

    private synchronized void initScheduler() {
        if (scheduler != null && !scheduler.isShutdown()) {
            scheduler.shutdown();
            try {
                if (!scheduler.awaitTermination(1, TimeUnit.SECONDS)) {
                    scheduler.shutdownNow();
                    scheduler = null;
                }
            } catch (InterruptedException e) {
                scheduler.shutdownNow();
                scheduler = null;
            }
        }

//      int maxThreads = Runtime.getRuntime().availableProcessors() * 2;
//      scheduler = Executors.newScheduledThreadPool(maxThreads, new SchedulerThreadFactory(description()));

        scheduler = Executors.newSingleThreadScheduledExecutor(new SchedulerThreadFactory(description()));
    }

    private void perform(long delay, TimeUnit timeUnit) {
        initScheduler();
        setLoop(false);
        setRun(true);
        scheduler.schedule(this, delay, timeUnit);
    }

    private void perform(long delay, long period, TimeUnit timeUnit) {
        initScheduler();
        setLoop(true);
        setRun(true);
        scheduler.scheduleWithFixedDelay(this, delay, period, timeUnit);
    }

    private void perform() {
        initScheduler();
        setLoop(false);
        setRun(true);
        scheduler.execute(this);
    }

    public void start() {
        if (isShutdown()) {
            perform();
        } else {
            logs.debug("Scheduler is already running, cannot start again." + description());
        }
    }

    public void start(long delay, TimeUnit timeUnit) {
        if (isShutdown()) {
            perform(delay, timeUnit);
        } else {
            logs.debug("Scheduler is already running, cannot start again." + description());
        }
    }

    public void start(long delay, long period, TimeUnit timeUnit) {
        if (isShutdown()) {
            perform(delay, period, timeUnit);
        } else {
            logs.debug("Scheduler is already running, cannot start again." + description());
        }
    }

    // 判斷排程器是否已關閉
    public boolean isShutdown() {
        return scheduler == null || scheduler.isShutdown();
    }

    public void shutdown() {
        // 使用本地變數快照避免 volatile 欄位在多執行緒間被 initScheduler() 設為 null 導致 NPE
        ScheduledExecutorService s = scheduler;
        if (s != null && !s.isShutdown()) {
            s.shutdown();
            try {
                if (!s.awaitTermination(1, TimeUnit.SECONDS)) {
                    s.shutdownNow();
                }
            } catch (InterruptedException e) {
                s.shutdownNow();
            } finally {
                scheduler = null;
            }
        }
    }

    // 重啟排程器
    public void resetScheduler() {
        if (scheduler != null && !scheduler.isShutdown()) {
            scheduler.shutdownNow(); // 停止並重啟排程器
        }
        initScheduler();
    }

    protected abstract Class<?> setCls();
    protected abstract String description();
    protected abstract void execute();
//    protected abstract void exception(ErrorCode errorCode, String message);
}
