package com.friday.distributor.common;

import com.alibaba.fastjson.JSON;
import com.friday.blank.api.dto.taskType.TaskTypeDetailDto;
import com.friday.blank.api.dto.taskType.TaskTypeMachineDto;
import com.friday.distributor.queue.TaskPersistHelper;
import com.friday.base.common.annotation.MethodLog;
import com.friday.base.common.response.DubboResult;
import com.friday.distributor.bean.Job;
import com.friday.distributor.bean.Task;
import com.friday.distributor.po.TaskInvoke;
import com.friday.operation.api.distributor.bean.BaseJob;
import com.friday.operation.api.distributor.constant.JobStateChangeEnum;
import com.friday.operation.api.distributor.constant.TaskStateChangeEnum;
import com.friday.operation.api.distributor.dto.*;
import com.friday.operation.api.executor.constant.BizStrategyMethod;
import com.friday.operation.api.executor.dto.module.InvokeDto;
import com.friday.operation.api.executor.service.IDynamicModuleService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import static com.friday.operation.api.executor.constant.BizStrategyMethod.*;

/**
 * @Author YaoYao
 * @Date Created In 15:09 2023/3/21
 * @Description
 * @Modified By
 */
@Slf4j
@Service
public class ExecutorMethodInvokeHelper {
    @Autowired(required = false)
    private IDynamicModuleService dynamicModuleService;
    @Autowired
    private TaskExecConfigService execConfigService;
    @Autowired
    private TaskPersistHelper persistHelper;


    public DubboResult checkParam(StartTaskReq startReq){
        TaskTypeDetailDto detail = execConfigService.getTaskExecConfig(startReq.getTaieTenant(), startReq.getTaskType());
        String serviceVersion = detail.getRelatedProcess().getVersion();
        String executorIP = random(detail.getMachines()).getIp();
        return this.invokeExecutorMethod(startReq.getTaieTenant(), startReq.getTaskType(), serviceVersion, executorIP, CHECK_PARAM, startReq);
    }

    public DubboResult<ArrayList<BaseJob>> createJobs(Task task){
        String serviceVersion = task.getTaskExecConfig().getRelatedProcess().getVersion();
        String executorIP = random(task.getTaskExecConfig().getMachines()).getIp();
        return this.invokeExecutorMethod(task.getTaieTenant(), task.getTaskType(), serviceVersion, executorIP, CREATE_JOB, task.transferToBaseTask());
    }

    public DubboResult executeJob(Job job, StandardJobInput input){
        try {
            TaskTypeDetailDto detail = job.getTaskExecConfig();
            String serviceVersion = detail.getProcess().getVersion();
            String executorIP = random(detail.getMachines()).getIp();
            return this.invokeExecutorMethod(job.getTaieTenant(), job.getTaskType(), serviceVersion, executorIP, EXECUTE_JOB, input);
        } catch (Exception e){
            log.error("任务invokeId = {}执行异常", job.getInvokeId(), e);
            return DubboResult.newFailure(DubboResult.SERVER_EXCEPTION, "任务执行异常:" + e.getMessage());
        }
    }

    public DubboResult onJobStateChange(Job job, JobStateChangeEnum stateChange){
        TaskTypeDetailDto detail = job.getTaskExecConfig();
        String serviceVersion = detail.getRelatedProcess().getVersion();
        String executorIP = random(detail.getMachines()).getIp();
        TaieJobStateChangeEvent event = TaieJobStateChangeEvent.builder()
                .invokeId(job.getInvokeId())
                .stateChange(stateChange)
                .build();
        return this.invokeExecutorMethod(job.getTaieTenant(), job.getTaskType(), serviceVersion, executorIP, ON_JOB_STATE_CHANGE, event);
    }

    public DubboResult onTaskStateChange(Task task, TaskStateChangeEnum stateChange){
        TaskTypeDetailDto detail = task.getTaskExecConfig();
        String serviceVersion = detail.getRelatedProcess().getVersion();
        String executorIP = random(detail.getMachines()).getIp();
        TaieTaskStateChangeEvent event = TaieTaskStateChangeEvent.builder()
                .taskId(task.getTaskId())
                .stateChange(stateChange)
                .build();
        return this.invokeExecutorMethod(task.getTaieTenant(), task.getTaskType(), serviceVersion, executorIP, ON_TASK_STATE_CHANGE, event);
    }

    public DubboResult onJobStateChange(Integer invokeId, JobStateChangeEnum stateChange){
        TaieJobDto job = persistHelper.getJobFrameData(invokeId);
        TaskTypeDetailDto detail = execConfigService.getTaskExecConfig(job.getTaieTenant(), job.getTaskType());
        String serviceVersion = detail.getRelatedProcess().getVersion();
        String executorIP = random(detail.getMachines()).getIp();
        TaieJobStateChangeEvent event = TaieJobStateChangeEvent.builder()
                .invokeId(job.getInvokeId())
                .stateChange(stateChange)
                .build();
        return this.invokeExecutorMethod(job.getTaieTenant(), job.getTaskType(), serviceVersion, executorIP, ON_JOB_STATE_CHANGE, event);
    }

    public DubboResult onTaskStateChange(String taskId, TaskStateChangeEnum stateChange){
        TaieTaskDto task = persistHelper.getTaskFrameData(taskId);
        TaskTypeDetailDto detail = execConfigService.getTaskExecConfig(task.getTaieTenant(), task.getTaskType());
        String serviceVersion = detail.getRelatedProcess().getVersion();
        String executorIP = random(detail.getMachines()).getIp();
        TaieTaskStateChangeEvent event = TaieTaskStateChangeEvent.builder()
                .taskId(task.getTaskId())
                .stateChange(stateChange)
                .build();
        return this.invokeExecutorMethod(task.getTaieTenant(), task.getTaskType(), serviceVersion, executorIP, ON_TASK_STATE_CHANGE, event);
    }

    public DubboResult<TaskResultDto> getTaskResult(TaskResultFilter filter){
        try {
            TaskInvoke invoke = persistHelper.getTaskInvokeByTaskId(filter.getTaskId());
            TaskTypeDetailDto detail = execConfigService.getTaskExecConfig(invoke.getTaieTenant(), invoke.getTaskType());
            String serviceVersion = detail.getRelatedProcess().getVersion();
            String executorIP = random(detail.getMachines()).getIp();
            return this.invokeExecutorMethod(invoke.getTaieTenant(), invoke.getTaskType(), serviceVersion, executorIP, GET_TASK_RESULT, filter);
        } catch (Exception e){
            log.error("任务taskId = {}获取执行结果异常", filter.getTaskId(), e);
            return DubboResult.newFailure(DubboResult.SERVER_EXCEPTION, "获取执行结果异常:" + e.getMessage());
        }
    }

    public DubboResult getData(DataRequest request){
        try {
            TaskTypeDetailDto detail = execConfigService.getTaskExecConfig(request.getTaieTenant(), request.getTaskType());
            String serviceVersion = detail.getRelatedProcess().getVersion();
            String executorIP = random(detail.getMachines()).getIp();
            return this.invokeExecutorMethod(request.getTaieTenant(), request.getTaskType(), serviceVersion, executorIP, GET_DATA, request);
        } catch (Exception e){
            log.error("数据GET交互异常，入参：{}", JSON.toJSONString(request), e);
            return DubboResult.newFailure(DubboResult.SERVER_EXCEPTION, "数据GET交互异常:" + e.getMessage());
        }
    }

    public DubboResult syncData(DataRequest request){
        try {
            TaskTypeDetailDto detail = execConfigService.getTaskExecConfig(request.getTaieTenant(), request.getTaskType());
            String serviceVersion = detail.getRelatedProcess().getVersion();
            String executorIP = random(detail.getMachines()).getIp();
            return this.invokeExecutorMethod(request.getTaieTenant(), request.getTaskType(), serviceVersion, executorIP, SYNC_DATA, request);
        } catch (Exception e){
            log.error("数据POST交互异常，入参：{}", JSON.toJSONString(request), e);
            return DubboResult.newFailure(DubboResult.SERVER_EXCEPTION, "数据POST交互异常:" + e.getMessage());
        }
    }

    public DubboResult handleFile(HandleFileDto file){
        try {
            TaskTypeDetailDto detail = execConfigService.getTaskExecConfig(file.getTaieTenant(), file.getTaskType());
            String serviceVersion = detail.getRelatedProcess().getVersion();
            String executorIP = random(detail.getMachines()).getIp();
            return this.invokeExecutorMethod(file.getTaieTenant(), file.getTaskType(), serviceVersion, executorIP, HANDLE_FILE, file);
        } catch (Exception e){
            log.error("文件处理异常，入参：{}", JSON.toJSONString(file), e);
            return DubboResult.newFailure(DubboResult.SERVER_EXCEPTION, "文件处理异常:" + e.getMessage());
        }
    }

    private TaskTypeMachineDto random(List<TaskTypeMachineDto> machines){
        Random r = new Random();
        int index = r.nextInt(machines.size());
        return machines.get(index);
    }

    @MethodLog
    public DubboResult invokeExecutorMethod(String taieTenant, String taskType, String version, String executorIP, BizStrategyMethod method, Object inputArg){
        try {
            BizConfigDto bizConfig = execConfigService.getBizConfig(taieTenant, taskType);
            InvokeDto invoke = InvokeDto.builder()
                    .moduleName(bizConfig.getModule())
                    .moduleVersion(version)
                    .handlerName(bizConfig.getHandler())
                    .methodName(method.getMethodName())
                    .inputArg(inputArg)
                    .build();
            return dynamicModuleService.invoke(invoke);
        } catch (Exception e){
            log.error("执行器方法调用异常", e);
            return DubboResult.newFailure(DubboResult.SERVER_EXCEPTION, "执行器方法调用异常:" + e.getMessage());
        }
    }


}
