package com.friday.distributor.queue;

import com.alibaba.fastjson.JSON;
import com.fasterxml.uuid.Generators;
import com.friday.blank.api.dto.taskType.TaskTypeDetailDto;
import com.friday.blank.api.dto.taskType.TaskTypeMachineDto;
import com.friday.distributor.constant.States;
import com.friday.distributor.exception.DistributorRuntimeException;
import com.google.common.collect.Lists;
import com.friday.distributor.bean.Job;
import com.friday.distributor.bean.Task;
import com.friday.distributor.common.TaskExecConfigService;
import com.friday.operation.api.distributor.bean.BaseJob;
import com.friday.operation.api.distributor.dto.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

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

import static com.friday.operation.api.distributor.constant.RobotExecType.Unattended;
import static com.friday.blank.api.constants.BusinessTypeEnum.RPA;
import static com.friday.blank.api.constants.RobotRelationEnum.SYNCHRONOUS;

/**
 * @Author YaoYao
 * @Date Created In 10:47 2023/03/07
 * @Description 任务初始化
 * @Modified By
 */

@Service
@Slf4j
public class TaskFactory {
    @Autowired
    private TaskExecConfigService taskExecConfigService;

    public Task initTask(StartTaskReq startReq) {
        TaskTypeDetailDto detail = taskExecConfigService.getTaskExecConfig(startReq.getTaieTenant(), startReq.getTaskType());
        //指定同异步
        if(Objects.nonNull(startReq.getSync())){
            detail.getSchedule().setIsSync(startReq.getSync());
        }
        //指定同时启动
        if(Objects.nonNull(startReq.getSimultaneous())){
            String robotRelation = detail.getSchedule().getRobotRelation();
            detail.getSchedule().setRobotRelation(startReq.getSimultaneous() ? SYNCHRONOUS.getKey() : robotRelation);
        }
        if(Unattended.getType().equals(detail.getSchedule().getExecuteMethod()) && !CollectionUtils.isEmpty(startReq.getGivenRobots())){
            List<TaskTypeMachineDto> filterGivenRobots = this.filterGivenRobots(detail.getMachines(), startReq.getGivenRobots());
            if(CollectionUtils.isEmpty(filterGivenRobots)){
                throw new DistributorRuntimeException("指定的后台运行机器未在管理端绑定此任务");
            }
            detail.setMachines(filterGivenRobots);
        }
        if(RPA.getKey().equals(detail.getProcess().getBusinessType()) && detail.getSchedule().getIsSync()){
            throw new DistributorRuntimeException("RPA类型的任务不支持同步启动");
        }
        BizConfigDto config = taskExecConfigService.getBizConfig(startReq.getTaieTenant(), startReq.getTaskType());
        Task task = new Task();
        task.setTaskId(Generators.timeBasedGenerator().generate().toString());
        task.setTaieTenant(startReq.getTaieTenant());
        task.setTaskType(startReq.getTaskType());
        task.setTaskVersion(detail.getProcess().getVersion());
        task.setState(States.TaskState.UNEXECUTED.name());
        task.setBizParam(startReq.getBizParam());
        task.setConfigParam(startReq.getConfigParam());
        task.setSysCode(startReq.getSysType());
        task.setOperator(startReq.getOperator());
        task.setTaskExecConfig(detail);
        task.setBizType(detail.getProcess().getBusinessType());
        task.setQueueKey(config.getQueueKey());
        task.setTaskInput(JSON.toJSONString(startReq));
        task.setDirect(startReq.isDirect());
        return task;
    }

    private List<TaskTypeMachineDto> filterGivenRobots(List<TaskTypeMachineDto> bindRobots, List<TaskExecRobot> givenRobots){
        List<String> givenTaieRobotNames = givenRobots.stream().map(TaskExecRobot::getTaieRobotName).collect(Collectors.toList());
        return bindRobots.stream().filter(r -> givenTaieRobotNames.contains(r.getMachineName())).collect(Collectors.toList());
    }

    public List<Job> initJobs(Task task, List<BaseJob> baseJobs){
        List<Job> jobList = Lists.newArrayList();
        baseJobs.forEach(baseJob -> {
            Job job = new Job();
            job.setTaieTenant(task.getTaieTenant());
            job.setTaskType(task.getTaskType());
            job.setBizType(task.getBizType());
            job.setQueueKey(task.getQueueKey());
            job.setTaskId(task.getTaskId());
            job.setState(States.JobState.UNSTART.name());
            job.setCreateTime(LocalDateTime.now());
            job.setTaskExecConfig(task.getTaskExecConfig());
            job.setBizParam(baseJob.getBizParam());
            job.setJobInput(this.createJobInput(task, job));
            jobList.add(job);
        });
        return this.initJobAssignedRobots(task.getTaskExecConfig(), jobList);
    }


    //为每个子任务指定运行工位
    private List<Job> initJobAssignedRobots(TaskTypeDetailDto detail, List<Job> createdJobs) {
        boolean simultaneous = SYNCHRONOUS.getKey().equals(detail.getSchedule().getRobotRelation());
        List<TaskTypeMachineDto> assignedRobots = detail.getMachines();
        List<Job> jobsAssignedRobots = Lists.newArrayList();
        if(simultaneous) {
            for (TaskTypeMachineDto assignedRobot : assignedRobots) {
                for (Job createdJob : createdJobs) {
                    createdJob.setGivenRobots(Lists.newArrayList(assignedRobot));
                }
                jobsAssignedRobots.addAll(createdJobs);
            }
        } else {
            jobsAssignedRobots = createdJobs;
            for (Job job : jobsAssignedRobots) {
                job.setGivenRobots(assignedRobots);
            }
        }
        return jobsAssignedRobots;
    }

    //TODO 后续接入日志模块，将从数据库查询的配置信息改为从新平台查询
    private StandardJobInput createJobInput(Task task, Job job){
        TaskTypeDetailDto detail = job.getTaskExecConfig();
        StandardJobBaseParam baseParam = StandardJobBaseParam.builder()
                .taieTenant(task.getTaieTenant())
                .taskType(task.getTaskType())
                .taskId(task.getTaskId())
                .invokeId(job.getInvokeId())
                .empNo(detail.getSchedule().getEmpId())
                .processName(detail.getProcess().getProcessName())
                .processVersion(detail.getProcess().getVersion())
                .operator(task.getOperator())
                .sysCode(task.getSysCode())
                .missionName(detail.getProcess().getMissionName())
                .startType(task.getStartType())
                .build();
        return StandardJobInput.builder()
                .baseParam(baseParam)
                .configParam(task.getConfigParam())
                .bizParam(job.getBizParam())
                .build();
    }



}
