package com.friday.distributor.statemachine;

import com.friday.distributor.bean.Job;
import com.friday.distributor.bean.Task;
import com.friday.distributor.constant.Events;
import com.friday.distributor.constant.States;
import com.friday.operation.api.distributor.constant.TaieJobState;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.persist.StateMachinePersister;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

import static com.friday.distributor.constant.Events.JobEvent.*;
import static com.friday.distributor.constant.States.JobState.*;

/**
 * @Author YaoYao
 * @Date Created In 16:23 2023/3/10
 * @Description
 * @Modified By
 */
@Slf4j
@Service
public class TaskStateMachineHelper {
    @Autowired
    private BeanFactory beanFactory;
    @Autowired
    private DistributorStateMachineBuilder stateMachineBuilder;
    @Resource(name = "taskStateMachineRedisPersister")
    private StateMachinePersister<States.TaskState, Events.TaskEvent, String> taskStateMachinePersister;
    @Resource(name = "jobStateMachineRedisPersister")
    private StateMachinePersister<States.JobState, Events.JobEvent, String> jobStateMachinePersister;

    public void dealOnJobState(TaieJobState taieJobState, Job job){
        switch (taieJobState){
            case Running:
                if (job.runningOverTime()) {
                    job.setState(RUNNING_TIMEOUT.name());
                    this.sendJobEvent(RUNNING_TIMEOUT_JOB, job);
                }
                break;
            case Success:
                job.setState(SUCCESS.name());
                this.sendJobEvent(COMPLETE_JOB, job);
                break;
            case Faulted:
            case Fail:
                if(job.getRetryTime() < job.getMaxRetryTime()){
                    job.setRetryTime(job.getRetryTime() + 1);
                    job.setExecJobId(null);
                } else {
                    job.setState(FAIL.name());
                    this.sendJobEvent(FAIL_JOB, job);
                }
                break;
            case Terminated:
                job.setState(TERMINATE.name());
                this.sendJobEvent(TERMINATE_JOB, job);
                break;
            default:
        }

    }

    //任务状态机事件转换
    public boolean sendTaskEvent(Events.TaskEvent taskEvent, Task task){
        Message<Events.TaskEvent> taskMsg = MessageBuilder
                .withPayload(taskEvent)
                .setHeader("task", task)
                .build();
        return this.sendTaskEvent(taskMsg, task);
    }

    //子任务状态机事件转换
    public boolean sendJobEvent(Events.JobEvent jobEvent, Job job){
        Message<Events.JobEvent> jobMsg = MessageBuilder
                .withPayload(jobEvent)
                .setHeader("job", job)
                .build();
        return this.sendJobEvent(jobMsg, job);
    }

    private synchronized boolean sendTaskEvent(Message<Events.TaskEvent> message, Task task) {
        boolean result = false;
        try {
            StateMachine<States.TaskState, Events.TaskEvent> taskStateMachine = stateMachineBuilder.buildTaskStateMachine(beanFactory);
            if(Events.TaskEvent.START_TASK.equals(message.getPayload())) {
                taskStateMachine.start();
            } else {
                taskStateMachinePersister.restore(taskStateMachine, task.getUniqueKey());
            }
            task.setStateMachine(taskStateMachine);
            result = taskStateMachine.sendEvent(message);
            String persistId = task.getUniqueKey();
            taskStateMachinePersister.persist(taskStateMachine, persistId);
        } catch (Exception e) {
            log.error("Task状态机持久化失败", e);
        } finally {
            task.getStateMachine().stop();
        }
        return result;
    }

    private synchronized boolean sendJobEvent(Message<Events.JobEvent> message, Job job) {
        boolean result = false;
        try {
            StateMachine<States.JobState, Events.JobEvent> jobStateMachine = stateMachineBuilder.buildJobStateMachine(beanFactory);
            if(!job.isStateMachineStart()) {
                jobStateMachine.start();
                job.setStateMachineStart(true);
            } else {
                jobStateMachinePersister.restore(jobStateMachine, job.getUniqueKey());
            }
            job.setStateMachine(jobStateMachine);
            result = jobStateMachine.sendEvent(message);
            String persistId = job.getUniqueKey();
            jobStateMachinePersister.persist(jobStateMachine, persistId);
        } catch (Exception e) {
            log.error("Job状态机持久化失败", e);
        } finally {
            job.getStateMachine().stop();
        }
        return result;
    }

    public StateMachine<States.TaskState, Events.TaskEvent> restoreTaskStateMachine(String taskUniKey) throws Exception {
        StateMachine<States.TaskState, Events.TaskEvent> taskStateMachine = stateMachineBuilder.buildTaskStateMachine(beanFactory);
        taskStateMachinePersister.restore(taskStateMachine, taskUniKey);
        return taskStateMachine;
    }

    public StateMachine<States.JobState, Events.JobEvent> restoreJobStateMachine(String jobUniKey) throws Exception {
        StateMachine<States.JobState, Events.JobEvent> jobStateMachine = stateMachineBuilder.buildJobStateMachine(beanFactory);
        jobStateMachinePersister.restore(jobStateMachine, jobUniKey);
        return jobStateMachine;
    }
}
