package com.friday.distributor.statemachine;

import com.friday.distributor.bean.Job;
import com.friday.distributor.common.PlatformInteractionService;
import com.friday.distributor.constant.Events.JobEvent;
import com.friday.distributor.constant.States.JobState;
import com.friday.distributor.exception.DistributorRuntimeException;
import com.friday.distributor.common.ExecutorMethodInvokeHelper;
import com.friday.distributor.queue.TaskPersistHelper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.Message;
import org.springframework.statemachine.annotation.OnTransition;
import org.springframework.statemachine.annotation.WithStateMachine;
import org.springframework.statemachine.listener.StateMachineListenerAdapter;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;

import static com.friday.distributor.constant.States.JobState.*;
import static com.friday.operation.api.distributor.constant.JobStateChangeEnum.*;
import static com.friday.blank.api.constants.MachineStateEnum.AVAILABLE;

/**
 * @Author YaoYao
 * @Date Created In 13:46 2023/2/27
 * @Description 子任务状态机监听器
 * @Modified By
 */
@Component
@WithStateMachine(id = "TaieDistributorJobMachine")
@Slf4j
public class JobEventListener extends StateMachineListenerAdapter<JobState, JobEvent> {
    @Autowired
    private TaskPersistHelper taskPersistHelper;
    @Autowired
    private PlatformInteractionService platformInteractionService;
    @Autowired
    private ExecutorMethodInvokeHelper invoker;

    @OnTransition(target = "RUNNING")
    public void startJob(Message<JobEvent> message){
        Job job = (Job) message.getHeaders().get("job");
        if(job == null){
            log.error("目标子任务缺失");
            throw new DistributorRuntimeException("状态机更新状态异常，缺失上下文");
        }
        log.info("开始启动子任务{}...", job.getInvokeId());
        job.setState(RUNNING.name());
        taskPersistHelper.insertOrUpdateJobRecord(job);
        invoker.onJobStateChange(job, UNSTART2RUNNING);
    }

    @OnTransition(target = "START_TIMEOUT")
    public void startTimeOutJob(Message<JobEvent> message){
        Job job = (Job) message.getHeaders().get("job");
        if(job == null){
            log.error("目标子任务缺失");
            throw new DistributorRuntimeException("状态机更新状态异常，缺失上下文");
        }
        log.info("子任务启动超时{}...", job.getInvokeId());
        job.setState(START_TIMEOUT.name());
        taskPersistHelper.insertOrUpdateJobRecord(job);
        invoker.onJobStateChange(job, UNSTART2START_TIMEOUT);
    }


    @OnTransition(source = "RUNNING", target = "RUNNING")
    public void updateJob(Message<JobEvent> message){
        Job job = (Job) message.getHeaders().get("job");
        if(job == null){
            log.error("目标子任务缺失");
            throw new DistributorRuntimeException("状态机更新状态异常，缺失上下文");
        }
        log.info("开始更新子任务{}...", job.getInvokeId());
        job.setState(RUNNING.name());
        taskPersistHelper.insertOrUpdateJobRecord(job);
    }

    @OnTransition(source = "RUNNING", target = "SUCCESS")
    public void completeJob(Message<JobEvent> message){
        Job job = (Job) message.getHeaders().get("job");
        if(job == null){
            log.error("目标子任务缺失");
            throw new DistributorRuntimeException("状态机更新状态异常，缺失上下文");
        }
        log.info("子任务完成{}...", job.getInvokeId());
        platformInteractionService.syncRobotState(job.getTaieTenant(), job.getExecRobot().getMachineName(), AVAILABLE.getKey());
        job.setJobEndTime(LocalDateTime.now());
        job.setState(SUCCESS.name());
        taskPersistHelper.insertOrUpdateJobRecord(job);
        invoker.onJobStateChange(job, RUNNING2SUCCESS);
    }

    @OnTransition(source = "RUNNING", target = "FAIL")
    public void failJob(Message<JobEvent> message){
        Job job = (Job) message.getHeaders().get("job");
        if(job == null){
            log.error("目标子任务缺失");
            throw new DistributorRuntimeException("状态机更新状态异常，缺失上下文");
        }
        log.info("子任务失败{}...", job.getInvokeId());
        platformInteractionService.syncRobotState(job.getTaieTenant(), job.getExecRobot().getMachineName(), AVAILABLE.getKey());
        job.setJobEndTime(LocalDateTime.now());
        job.setState(FAIL.name());
        taskPersistHelper.insertOrUpdateJobRecord(job);
        invoker.onJobStateChange(job, RUNNING2FAIL);
    }

    @OnTransition(source = "RUNNING", target = "TERMINATE")
    public void terminateJob(Message<JobEvent> message){
        terminateJobWhenRunning(message, "RUNNING");
    }

    @OnTransition(source = "RUNNING_TIMEOUT", target = "TERMINATE")
    public void terminateJobWhenRunningTimeout(Message<JobEvent> message){
        terminateJobWhenRunning(message, "RUNNING_TIMEOUT");
    }

    private void terminateJobWhenRunning(Message<JobEvent> message, String state) {
        Job job = (Job) message.getHeaders().get("job");
        if(job == null){
            log.error("目标子任务缺失");
            throw new DistributorRuntimeException("状态机更新状态异常，缺失上下文");
        }
        log.info("{}子任务终止{}...", state, job.getInvokeId());
        platformInteractionService.syncRobotState(job.getTaieTenant(), job.getExecRobot().getMachineName(), AVAILABLE.getKey());
        job.setJobEndTime(LocalDateTime.now());
        job.setState(TERMINATE.name());
        taskPersistHelper.insertOrUpdateJobRecord(job);
        invoker.onJobStateChange(job, RUNNING2TERMINATE);
    }

    @OnTransition(source = "UNSTART", target = "TERMINATE")
    public void terminateJobWhenUnstart(Message<JobEvent> message){
        Job job = (Job) message.getHeaders().get("job");
        if(job == null){
            log.error("目标子任务缺失");
            throw new DistributorRuntimeException("状态机更新状态异常，缺失上下文");
        }
        log.info("未开始的子任务终止{}...", job.getInvokeId());
        job.setState(TERMINATE.name());
        taskPersistHelper.insertOrUpdateJobRecord(job);
    }

    @OnTransition(source = "RUNNING", target = "RUNNING_TIMEOUT")
    public void runningTimeOutJob(Message<JobEvent> message){
        Job job = (Job) message.getHeaders().get("job");
        if(job == null){
            log.error("目标子任务缺失");
            throw new DistributorRuntimeException("状态机更新状态异常，缺失上下文");
        }
        log.info("子任务运行超时{}...", job.getInvokeId());
        job.setState(RUNNING_TIMEOUT.name());
        taskPersistHelper.insertOrUpdateJobRecord(job);
    }

}
