package com.friday.distributor.scheduler.execScheduler;

import com.alibaba.dubbo.common.utils.CollectionUtils;
import com.friday.base.common.response.DubboResult;
import com.friday.blank.api.dto.taskType.TaskTypeMachineDto;
import com.friday.distributor.annotation.AuditInfo;
import com.friday.distributor.bean.Job;
import com.friday.distributor.common.RpaExecHelper;
import com.friday.distributor.common.PlatformInteractionService;
import com.friday.distributor.queue.TaskQueueManager;
import com.friday.distributor.scheduler.ExecScheduler;
import com.friday.operation.api.tooldriver.dto.common.DriverBase;
import com.friday.operation.api.tooldriver.dto.rpa.LicenseDto;
import com.friday.operation.api.tooldriver.dto.rpa.RobotDto;
import com.friday.operation.api.tooldriver.service.ICapabilityDriver;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Random;
import java.util.stream.Collectors;

import static com.friday.operation.api.tooldriver.constant.RpaRobotStateEnum.Available;
import static com.friday.operation.api.tooldriver.constant.RpaRobotStateEnum.Busy;
import static com.friday.blank.api.constants.MachineStateEnum.*;


/**
 * @Author YaoYao
 * @Date Created In 11:40 2023/3/1
 * @Description 后台默认的任务调度器
 * @Modified By
 */

@Slf4j
@Component("DefaultUnattendedRpaExecScheduler")
public class DefaultUnattendedRpaExecScheduler extends ExecScheduler {
    @Autowired
    private ICapabilityDriver rpaDriver;
    @Autowired
    private PlatformInteractionService platformInteractionService;
    @Autowired
    private TaskQueueManager taskQueueManager;
    @Autowired
    private RpaExecHelper rpaExecHelper;

    @Value("${default.rpa.unattended.licenseType}")
    private String unattendedLicenseType;

    @Override
    public DubboResult<TaskTypeMachineDto> distributeExecRobot(Job job, String strategy) {
        try {
            //GivenRobots中taie工位空闲 givenRobots是启动时带的or同时启动拆的or没有限制 总之就是不为空
            if(CollectionUtils.isEmpty(job.getGivenRobots())){
                return DubboResult.newFailure(DubboResult.SERVER_EXCEPTION, "未指定可运行的工位");
            }
//            List<TaskTypeMachineDto> availableTaieRobots = this.availableTaieRobot(job);
            List<TaskTypeMachineDto> availableTaieRobots = job.getTaskExecConfig().getMachines();
            if(CollectionUtils.isEmpty(availableTaieRobots)){
                return DubboResult.newFailure(DubboResult.SERVER_EXCEPTION, "无空闲TAIE工位");
            }
            //不影响未来更高优先级任务(暂不做)
            TaskTypeMachineDto candidate = this.firstCandidate(availableTaieRobots);
            //TAIE工位空闲但实际RPA忙碌:置非法占用
            String rpaRobotState = rpaExecHelper.getRobotInfo(candidate).getState();
            if(Busy.getState().equals(rpaRobotState)){
                platformInteractionService.syncRobotState(job.getTaieTenant(), candidate.getMachineName(), ILLEGAL_OCCUPY.getKey());
                return DubboResult.newFailure(DubboResult.SERVER_EXCEPTION, "RPA工位忙碌:" + candidate.getRobotName());
            }
            if(!Available.getState().equals(rpaRobotState)){
                return DubboResult.newFailure(DubboResult.SERVER_EXCEPTION, "RPA工位不可用:" + candidate.getRobotName());
            }
            if(candidate.getIsInTrain()){
                DubboResult assignLicenseResult = this.assignLicense(job, candidate);
                if(!assignLicenseResult.success()){
                    return DubboResult.newFailure(assignLicenseResult.getCode(), assignLicenseResult.getMessage());
                }
            }
            return DubboResult.newSuccess(candidate);
        } catch (Exception e){
            log.error("执行器调度-{}异常", this.getClass().getSimpleName(), e);
            return DubboResult.newFailure(DubboResult.SERVER_EXCEPTION, "执行器调度异常:" + this.getClass().getSimpleName());
        }
    }

    @Override
    public DubboResult freeExecRobot(Job job, String strategy) {
        try {
            if(Objects.nonNull(job.getExecRobot()) && job.getExecRobot().getIsInTrain()){
                return this.freeLicense(job);
            }
            return DubboResult.newSuccess();
        } catch (Exception e){
            log.error("执行器资源释放-{}异常", this.getClass().getSimpleName(), e);
            return DubboResult.newFailure(DubboResult.SERVER_EXCEPTION, "执行器资源释放:" + this.getClass().getSimpleName());
        }
    }

    private List<TaskTypeMachineDto> availableTaieRobot(Job job){
        List<TaskTypeMachineDto> givenRobots = job.getGivenRobots();
        List<String> availableTaieRobot = platformInteractionService.queryPlatformRobotsByState(job.getTaieTenant(), AVAILABLE.getKey());
        return givenRobots.stream().filter(r -> availableTaieRobot.contains(r.getMachineName())).collect(Collectors.toList());
    }
    
    private TaskTypeMachineDto firstCandidate(List<TaskTypeMachineDto> availableTaieRobots){
        List<TaskTypeMachineDto> licenseEnabledMachines = availableTaieRobots.stream().filter(m -> !m.getIsInTrain()).collect(Collectors.toList());
        if(CollectionUtils.isNotEmpty(licenseEnabledMachines)){
            return this.random(licenseEnabledMachines);
        }
        return this.random(availableTaieRobots);
    }

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



    @AuditInfo(desc = "后台火车表发放许可")
    public DubboResult assignLicense(Job job, TaskTypeMachineDto candidate){
        DriverBase driverBase = DriverBase.builder()
                .tenantKey(job.getTaskExecConfig().getProcess().getOcTenantKey())
                .build();
        RobotDto robot = rpaExecHelper.getRobotInfo(candidate);
        if(robot.getLicensed()){
            return DubboResult.newSuccess();
        }
        //许可数量足够
        DubboResult<Integer> licenseNumResult = rpaDriver.availableLicenseNum(driverBase, unattendedLicenseType);
        if(!licenseNumResult.success()){
            return DubboResult.newFailure(DubboResult.SERVER_EXCEPTION, "查询可用许可数量失败:" + licenseNumResult.getMessage());
        }
        if(licenseNumResult.getObject() == 0){
            return DubboResult.newFailure(DubboResult.SERVER_EXCEPTION, "许可数量不足");
        }
        //赋许可
        LicenseDto unattendedLicense = LicenseDto.builder()
                .robotKey(candidate.getRobotKey())
                .licenseType(unattendedLicenseType)
                .build();
        DubboResult enableLicenseResult = rpaDriver.enableLicense(driverBase, unattendedLicense);
        if(!enableLicenseResult.success()){
            return DubboResult.newFailure(enableLicenseResult.getCode(), enableLicenseResult.getMessage());
        }
        return DubboResult.newSuccess();
    }

    @AuditInfo(desc = "后台火车表回收许可")
    public DubboResult freeLicense(Job job){
        TaskTypeMachineDto execRobot = job.getExecRobot();
        List<Job> runningJobs = taskQueueManager.getRunningJobs(job.getQueueKey());
        Optional<Job> runningJobOnSameRobot =runningJobs.stream().filter(j -> j.getExecRobot().getRobotName().equals(execRobot.getRobotName())).findAny();
        if(runningJobOnSameRobot.isPresent()){
            log.error("当前子任务结束后有新任务在同一台机器{}上运行，不释放许可!", execRobot.getRobotName());
            return DubboResult.newFailure(DubboResult.SERVER_EXCEPTION, "当前子任务结束后有新任务在同一台机器上运行，不释放许可");
        }
        String rpaRobotState = rpaExecHelper.getRobotInfo(execRobot).getState();
        if(Busy.getState().equals(rpaRobotState)){
            log.error("RPA机器人{}正在运行中，不释放许可!", execRobot.getRobotName());
            return DubboResult.newFailure(DubboResult.SERVER_EXCEPTION, "RPA机器人正在运行中，不释放许可");
        }
        //释放许可
        DriverBase driverBase = DriverBase.builder()
                .tenantKey(job.getTaskExecConfig().getProcess().getOcTenantKey())
                .build();
        LicenseDto unattendedLicense = LicenseDto.builder()
                .robotKey(execRobot.getRobotKey())
                .licenseType(unattendedLicenseType)
                .build();
        return rpaDriver.disableLicense(driverBase, unattendedLicense);
    }
}
