package com.friday.distributor.service;

import com.alibaba.dubbo.common.utils.CollectionUtils;
import com.alibaba.fastjson.JSON;
import com.friday.blank.api.constants.MachineStateEnum;
import com.friday.blank.api.dto.taskType.TaskTypeMachineDto;
import com.friday.distributor.constant.Events;
import com.friday.distributor.constant.States;
import com.friday.distributor.exception.DistributorRuntimeException;
import com.friday.distributor.scheduler.ExecScheduler;
import com.friday.distributor.statemachine.TaskStateMachineHelper;
import com.google.common.collect.Lists;
import com.friday.base.common.response.DubboResult;
import com.friday.distributor.bean.Job;
import com.friday.distributor.bean.Task;
import com.friday.distributor.common.PlatformInteractionService;
import com.friday.distributor.frame.GeneralStrategyContext;
import com.friday.distributor.queue.TaskPersistHelper;
import com.friday.distributor.queue.TaskQueueManager;
import com.friday.operation.api.distributor.dto.TaskIdentifier;
import com.friday.operation.api.distributor.dto.TaskResultDto;
import com.friday.operation.api.distributor.dto.TaskResultFile;
import com.friday.operation.api.distributor.dto.TaskStartResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @Description 任务执行相关服务类
 */
@Slf4j
@Service
public class DistributorExecuteService {
    @Autowired
    private GeneralStrategyContext generalStrategy;
    @Autowired
    private TaskQueueManager taskQueueManager;
    @Autowired
    private TaskStateMachineHelper taskStateMachineHelper;
    @Autowired
    private TaskPersistHelper taskPersistHelper;
    @Autowired
    private PlatformInteractionService platformInteractionService;

    public TaskStartResult asyncStartTaieTask(Task task){
        task.setState(States.TaskState.EXECUTING.name());
        taskQueueManager.addTask(task, task.getQueueKey());
        taskStateMachineHelper.sendTaskEvent(Events.TaskEvent.START_TASK, task);
        if(task.isDirect()){
            for(Job job : task.getJobs()){
                this.asyncStartTaieJob(job);
            }
        }
        TaskIdentifier taskIdentifier = TaskIdentifier.builder()
                .taskId(task.getTaskId())
                .build();
        taskIdentifier.setInvokeIds(task.getJobs().stream().map(Job::getInvokeId).collect(Collectors.toList()));
        return TaskStartResult.builder().taskIdentifier(taskIdentifier).build();
    }

    public void asyncStartTaieJob(Job job){
        try {
            if(!job.unStart()){
                return;
            }
            job.setScheduling(true);
            taskQueueManager.updateJob(job, job.getQueueKey());
            ExecScheduler execScheduler = taskQueueManager.getQueueExecScheduler(job.getQueueKey());
            DubboResult<TaskTypeMachineDto> execRobotResult = execScheduler.distributeExecRobot(job, "DefaultStrategy");
            if(!execRobotResult.success()){
                log.info("任务{}-{}未获取执行器资源，原因：{}", job.getTaskType(), job.getInvokeId(), execRobotResult.getMessage());
                doAsyncStartTaieJobFail(job, execRobotResult.getMessage());
                return;
            }
            job.setExecRobot(execRobotResult.getObject());
            DubboResult<String> startResult = generalStrategy.asyncStartTaieJob(job);
            if(!startResult.success()){
                log.error("任务{}-{}未成功启动，原因：{}", job.getTaskType(), job.getInvokeId(), startResult.getMessage());
                this.doAsyncStartTaieJobFail(job, startResult.getMessage());
                return;
            }
            platformInteractionService.syncRobotState(job.getTaieTenant(), job.getExecRobot().getMachineName(), MachineStateEnum.NORMAL_OCCUPY.getKey());
            String execJobId = startResult.getObject();
            this.doAsyncStartTaieJobSuccessfully(job, execJobId);
        } catch (Exception e){
            log.error("启动子任务异常：taskType = {}, invokeId = {}", job.getTaskType(), job.getInvokeId(), e);
        }
    }

    private void doAsyncStartTaieJobSuccessfully(Job job, String execJobId){
        job.setExecJobId(execJobId);
        job.setJobStartTime(LocalDateTime.now());
        job.setScheduling(false);
        job.setRemark("启动成功 at " + LocalDateTime.now());
        taskStateMachineHelper.sendJobEvent(Events.JobEvent.START_JOB, job);
        taskQueueManager.updateJob(job, job.getQueueKey());
    }

    private void doAsyncStartTaieJobFail(Job job, String msg){
        if (job.startOverTime()) {
            job.setState(States.JobState.START_TIMEOUT.name());
            taskStateMachineHelper.sendJobEvent(Events.JobEvent.START_TIMEOUT_JOB, job);
        }
        job.setScheduling(false);
        job.setRemark(msg + "at " + LocalDateTime.now());
        taskQueueManager.updateJob(job, job.getQueueKey());
        taskPersistHelper.insertOrUpdateJobRecord(job);
    }

    public TaskStartResult syncStartTaieTask(Task task){
        TaskResultDto result = new TaskResultDto();
        Map<String, Object> resultData = new HashMap<>();
        List<TaskResultFile> files = Lists.newArrayList();
        for(Job job : task.getJobs()){
            DubboResult<TaskResultDto> syncStartJobResult = this.syncStartTaieJob(job);
            if(!syncStartJobResult.success()){
                task.setState(States.TaskState.EXECUTED.name());
                taskPersistHelper.insertOrUpdateTaskRecord(task);
                throw new DistributorRuntimeException("同步启动子任务失败:" + syncStartJobResult.getMessage());
            }
            TaskResultDto jobResult = syncStartJobResult.getObject();
            if(Objects.nonNull(jobResult.getResultData())){
                resultData.putAll(jobResult.getResultData());
            }
            if(CollectionUtils.isNotEmpty(jobResult.getFiles())){
                files.addAll(jobResult.getFiles());
            }
        }
        result.setResultData(resultData);
        result.setFiles(files);
        task.setState(States.TaskState.EXECUTED.name());
        taskPersistHelper.insertOrUpdateTaskRecord(task);
        TaskIdentifier taskIdentifier = TaskIdentifier.builder()
                .taskId(task.getTaskId())
                .build();
        taskIdentifier.setInvokeIds(task.getJobs().stream().map(Job::getInvokeId).collect(Collectors.toList()));
        return TaskStartResult.builder()
                .taskIdentifier(taskIdentifier)
                .result(result)
                .build();
    }

    private DubboResult<TaskResultDto> syncStartTaieJob(Job job){
        ExecScheduler execScheduler = taskQueueManager.getQueueExecScheduler(job.getQueueKey());
        DubboResult<TaskTypeMachineDto> execRobotResult = execScheduler.distributeExecRobot(job, "DefaultStrategy");
        if(!execRobotResult.success()){
            return DubboResult.newFailure(DubboResult.SERVER_EXCEPTION, "执行器调度失败：" + execRobotResult.getMessage());
        }
        fillingJobExecRobot(job, execRobotResult.getObject());
        job.setExecRobot(execRobotResult.getObject());
        job.setJobStartTime(LocalDateTime.now());
        DubboResult<TaskResultDto> syncStartResult = generalStrategy.syncStartTaieJob(job);
        if(syncStartResult.success()){
            job.setState(States.JobState.SUCCESS.name());
            job.setJobOutput(JSON.toJSONString(syncStartResult.getObject()));
        } else {
            job.setState(States.JobState.FAIL.name());
            job.setFailMsg(syncStartResult.getMessage());
        }
        job.setJobEndTime(LocalDateTime.now());
        taskPersistHelper.insertOrUpdateJobRecord(job);
        return syncStartResult;
    }

    public DubboResult stopTaieJob(Job job){
        if(job.running()){
            DubboResult result = generalStrategy.stopTaieJob(job);
            if(!result.success()){
                job.setRemark("终止任务失败:" + result.getMessage() + " at " + LocalDateTime.now());
                taskPersistHelper.insertOrUpdateJobRecord(job);
                return result;
            }
        }
        job.setState(States.JobState.TERMINATE.name());
        job.setRemark("终止任务成功at " + LocalDateTime.now());
        taskStateMachineHelper.sendJobEvent(Events.JobEvent.TERMINATE_JOB, job);
        taskQueueManager.updateJob(job, job.getQueueKey());
        return DubboResult.newSuccess();
    }

    private void fillingJobExecRobot(Job job, TaskTypeMachineDto machine) {
    	//填充job的当前执行工位
    	job.setExecRobot(machine);
        //填充job开始时间
        job.setJobStartTime(LocalDateTime.now());
    	//发送给rpa/ai/program执行时的入参，填充对应的工位参数
    	job.getJobInput().getBaseParam().setRobotIp(machine.getIp());
        job.getJobInput().getBaseParam().setRobotUser(machine.getUser());
        job.getJobInput().getBaseParam().setExecuteMethod(machine.getToolMethod());
    }
}
