package com.ecep.contract.util;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.ecep.contract.Message;
import com.ecep.contract.SpringApp;
import com.ecep.contract.TaskStatus;
import com.ecep.contract.ui.Tasker;

import lombok.Getter;
import lombok.Setter;

/**
 * 被监控的任务包装类
 */
public class MonitoredTask<T> implements Runnable {
    private static final Logger logger = LoggerFactory.getLogger(MonitoredTask.class);
    private final Tasker<T> delegate;
    private CompletableFuture<T> future;
    @Getter
    private final String taskId;
    @Getter
    private final long startTime;
    @Getter
    private long endTime = 0;
    @Setter
    private Long timeoutSeconds;
    private ScheduledFuture<?> timeoutExecutor;
    @Getter
    private String title;
    private List<Message> messages = new ArrayList<>();
    @Getter
    private TaskStatus state = null;
    @Getter
    private Throwable exception = null;
    private Runnable succeededProperty = null;
    private Runnable failedProperty = null;
    private Runnable cancelledHandler = null;
    private AtomicBoolean timeoutHandled = new AtomicBoolean(false);

    public MonitoredTask(Tasker<T> delegate, String taskId) {
        this.delegate = delegate;
        this.taskId = taskId;
        this.startTime = System.currentTimeMillis();
        delegate.setMessageHandler(this::messageHandler);
        delegate.setTitleHandler(this::titleHandler);
    }

    private boolean messageHandler(Message message) {
        messages.add(message);
        return true;
    }

    private boolean titleHandler(String title) {
        this.title = title;
        return true;
    }

    public List<Message> getMessages() {
        return messages;
    }

    @Override
    public void run() {
        if (logger.isDebugEnabled()) {
            logger.debug("任务 {} 开始执行", taskId);
        }
        state = TaskStatus.RUNNING;
        future = CompletableFuture.supplyAsync(this::callDelegate);
        future.whenComplete((result, ex) -> {
            endTime = System.currentTimeMillis();
            if (ex != null) {
                exception = ex;
                state = TaskStatus.FAILED;
                if (logger.isDebugEnabled()) {
                    logger.debug("任务 {} 执行异常", taskId, ex);
                }
                if (failedProperty != null) {
                    failedProperty.run();
                }
                return;
            }
            state = TaskStatus.SUCCEEDED;
            if (logger.isDebugEnabled()) {
                logger.debug("任务 {} 执行完成", taskId);
            }
            if (succeededProperty != null) {
                succeededProperty.run();
            }

        });
    }

    private T callDelegate() {
        // 设置超时监控
        if (timeoutSeconds != null) {
            setupTimeoutMonitor();
        }
        try {
            return delegate.call();
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            // 取消超时监控
            if (timeoutExecutor != null) {
                timeoutExecutor.cancel(true);
            }
        }
    }

    /**
     * 设置超时监控
     */
    private void setupTimeoutMonitor() {
        timeoutExecutor = getExecutorService().schedule(() -> {
            if (isRunning() && timeoutHandled.compareAndSet(false, true)) {
                updateMessage("任务执行超时，正在取消...");
                state = TaskStatus.TIMED_OUT;
                future.cancel(true);
            }
        }, timeoutSeconds, TimeUnit.SECONDS);
    }

    public boolean isRunning() {
        return state == TaskStatus.RUNNING;
    }

    public void submit() {
        getExecutorService().submit(this);
    }

    public ScheduledFuture<?> schedule(long delay, TimeUnit unit) {
        ScheduledFuture<?> schedule = getExecutorService().schedule(this, delay, unit);
        state = TaskStatus.DELAYED;
        return schedule;
    }

    private void updateMessage(String string) {
        this.messageHandler(Message.info(string));
    }

    public boolean cancel() {
        if (future != null && future.cancel(true)) {
            delegate.setCancelled(true);
            state = TaskStatus.CANCELLED;
            if (cancelledHandler != null) {
                cancelledHandler.run();
            }
            return true;
        }
        return false;
    }

    /**
     * 获取任务执行耗时
     */
    public long getExecutionTime() {
        if (endTime == 0) {
            return System.currentTimeMillis() - startTime;
        }
        return endTime - startTime;
    }

    public void setTimeout(Long timeoutSeconds2) {
        this.timeoutSeconds = timeoutSeconds2;
    }

    ScheduledExecutorService getExecutorService() {
        return SpringApp.getBean(ScheduledExecutorService.class);
    }

    public void setOnSucceeded(Runnable runnable) {
        succeededProperty = runnable;
    }

    public void setOnCancelled(Runnable runnable) {
        cancelledHandler = runnable;
    }

    public void setOnFailed(Runnable runnable) {
        failedProperty = runnable;
    }

}