package com.friday.tooldriver.service.rpa.supplier.smartrpa;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import com.friday.base.common.annotation.MethodLog;
import com.friday.base.common.response.DubboResult;
import com.friday.operation.api.distributor.dto.StandardRpaInput;
import com.friday.operation.api.keepalive.constant.smartrpa.RobotState;
import com.friday.operation.api.keepalive.dto.smartrpa.ActionConfirmReq;
import com.friday.operation.api.keepalive.dto.smartrpa.ExecActionDto;
import com.friday.operation.api.keepalive.dto.smartrpa.HeartbeatReq;
import com.friday.operation.api.keepalive.dto.smartrpa.HeartbeatResp;
import com.friday.operation.api.tooldriver.service.ISmartRPAHeartbeatService;
import com.friday.tooldriver.dto.smartrpa.SmartJobDto;
import com.friday.tooldriver.dto.smartrpa.SmartRobotDto;
import com.friday.tooldriver.po.smartrpa.JobPo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

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

import static com.friday.operation.api.keepalive.constant.smartrpa.ExecAction.*;
import static com.friday.operation.api.keepalive.constant.smartrpa.JobState.Pending;
import static com.friday.tooldriver.constants.smartrpa.SmartRpaErrorCode.INVALID_JOB_INPUT_ARGS;
import static com.friday.tooldriver.constants.smartrpa.SmartRpaErrorCode.ROBOT_MULTI_EXEC_ACTION;

/**
 * @Author YaoYao
 * @Date Created In 11:45 2022/12/14
 * @Description SmartRPA心跳交互管理
 * @Modified By
 */
@Service
@Slf4j
public class HeartbeatManager implements ISmartRPAHeartbeatService {
    @Autowired
    private RobotManager robotManager;
    @Autowired
    private JobExecManager jobExecManager;

    @Value("${job.action.confirm.default.timeout}")
    private Integer jobConfirmDefaultTimeout;
    @Value("${job.action.confirm.start.timeout}")
    private Integer jobConfirmStartTimeout;
    @Value("${heartbeat.default.timeout}")
    private Integer jobRunningTimeout;

    public DubboResult<HeartbeatResp> heartbeat(HeartbeatReq req){
        HeartbeatResp resp = HeartbeatResp.builder().actions(Lists.newArrayList()).build();
        if(Objects.isNull(req) || !req.reqValid()){
            log.error("心跳请求数据无效：" + JSON.toJSONString(req));
            return DubboResult.newSuccess(resp);
        }
        try {
            syncRobotState(req.getMachineState(), req.getRobotKey());
            if(!req.jobStateReqValid()){
                log.error("任务状态同步数据无效：" + JSON.toJSONString(req));
            } else if(StringUtils.isNotEmpty(req.getJobId())){
                syncJobState(req.getJobState(), req.getJobId());
            }
            if(Objects.nonNull(req.getActionConfirm())){
                if(!req.getActionConfirm().reqValid()) {
                    log.error("执行动作确认数据无效：" + JSON.toJSONString(req));
                } else {
                    actionConfirm(req);
                }
            }
        } catch (Exception e){
            log.error("心跳同步状态异常{}", JSON.toJSONString(req), e);
        }
        try {
            List<ExecActionDto> actions = collectInstructs(req);
            resp.setActions(actions);
        } catch (Exception e){
            log.error("心跳获取指令异常{}", JSON.toJSONString(req), e);
        }
        return DubboResult.newSuccess(resp);
    }

    @Override
    public DubboResult<StandardRpaInput> getJobDetail(String jobId) {
        DubboResult<SmartJobDto> jobResult = jobExecManager.queryJobByKey(Integer.parseInt(jobId));
        if(!jobResult.success()){
            return DubboResult.newFailure(jobResult.getCode(), jobResult.getMessage());
        }
        if(StringUtils.isEmpty(jobResult.getObject().getInputArguments())){
            return DubboResult.newFailure(INVALID_JOB_INPUT_ARGS.getCode(), INVALID_JOB_INPUT_ARGS.getMessage());
        }
        StandardRpaInput rpaInput = JSON.parseObject(jobResult.getObject().getInputArguments(), StandardRpaInput.class);
        return DubboResult.newSuccess(rpaInput);
    }

    private void syncRobotState(String state, String robotKey){
        robotManager.syncRobotState(state, robotKey);
    }

    private void syncJobState(String state, String jobId){
        jobExecManager.syncJobState(state, jobId);
    }

    private void actionConfirm(HeartbeatReq req){
        ActionConfirmReq confirm = req.getActionConfirm();
        log.info("鹊桥执行动作确认心跳入参：{}", JSON.toJSONString(req));
        if(Start.getAction().equals(confirm.getAction())){
            jobExecManager.startJobConfirm(confirm);
        } else if(Terminate.getAction().equals(confirm.getAction())){
            jobExecManager.stopJobConfirm(confirm);
        } else if(Pause.getAction().equals(confirm.getAction())){
            jobExecManager.pauseJobConfirm(confirm);
        } else if(Resume.getAction().equals(confirm.getAction())){
            jobExecManager.resumeJobConfirm(confirm);
        } else if(UpdateJobState.getAction().equals(confirm.getAction())){
            jobExecManager.syncJobState(req.getActionConfirm());
        } else if(SetMachineState.getAction().equals(confirm.getAction())){
            robotManager.setRobotStateConfirm(req.getRobotKey(), confirm.getMachineState());
        } else if(EnableLicense.getAction().equals(confirm.getAction())){
            robotManager.distributeLicenseConfirm(req.getRobotKey(), true);
        } else if(DisableLicense.getAction().equals(confirm.getAction())){
            robotManager.distributeLicenseConfirm(req.getRobotKey(), false);
        }
    }

    private List<ExecActionDto> collectInstructs(HeartbeatReq req){
        List<ExecActionDto> instructs = Lists.newArrayList();
        instructs.addAll(collectJobToExecute(req));
        instructs.addAll(collectJobToSyncState(req));
        instructs.add(collectRobotToUpdateLicense(req));
        instructs.add(collectRobotToSetState(req));
        instructs.removeAll(Collections.singleton(null));
        return instructs;
    }

    private List<ExecActionDto> collectJobToExecute(HeartbeatReq req){
        List<ExecActionDto> jobs = jobExecManager.getJobExecActions(req.getRobotKey());
        if(jobs.size() > 1){
            List<String> actions = jobs.stream().map(ExecActionDto::getAction).collect(Collectors.toList());
            log.error(ROBOT_MULTI_EXEC_ACTION.toString(req.getRobotKey(), actions.toString()));
            jobs = Lists.newArrayList(jobs.get(0));
        }
        return jobs;
    }

    private List<ExecActionDto> collectJobToSyncState(HeartbeatReq req){
        return jobExecManager.getJobStateSyncActions(req.getRobotKey());
    }

    private ExecActionDto collectRobotToUpdateLicense(HeartbeatReq req){
        return robotManager.getLicenseAction(req.getRobotKey());
    }

    private ExecActionDto collectRobotToSetState(HeartbeatReq req){
        return robotManager.getRobotStateSetAction(req.getRobotKey());
    }

    //处理近一周的待处理指令
    public void doActionOvertime(boolean all, String robotKey){
        if(all){
            List<String> robotKeys = robotManager.getAllRobotKeys();
            for(String key : robotKeys){
                doActionOvertime(key);
            }
        } else {
            doActionOvertime(robotKey);
        }
    }

    public void doActionOvertime(String robotKey){
        //运行超时任务
        List<JobPo> jobsRunningOvertime = jobExecManager.selectRunningJobs(robotKey);
        for(JobPo job : jobsRunningOvertime){
            if(job.getStartTime().plusDays(jobRunningTimeout).isBefore(LocalDateTime.now())){
                jobExecManager.jobActionOvertimeConfirm(job.getId(), job.getSyncState());
                robotManager.updateRobotUnresponsiveTime(robotKey, false);
            }
        }
        //运行指令
        List<JobPo> jobsToConfirm = jobExecManager.selectJobsToExecute(robotKey);
        for(JobPo job : jobsToConfirm){
            int timeout = Pending.getState().equals(job.getState()) ? jobConfirmStartTimeout : jobConfirmDefaultTimeout;
            //指令处理超时
            if(job.getStateLastUpdateTime().plusSeconds(timeout).isBefore(LocalDateTime.now())){
                jobExecManager.jobActionOvertimeConfirm(job.getId(), job.getSyncState());
                robotManager.updateRobotUnresponsiveTime(robotKey, false);
            }
        }
        //同步任务状态指令
        List<JobPo> jobsToSyncState = jobExecManager.selectJobsToSyncState(robotKey);
        for(JobPo job : jobsToSyncState){
            if(job.getLastSyncReqTime().plusSeconds(jobConfirmDefaultTimeout).isBefore(LocalDateTime.now())){
                jobExecManager.jobActionOvertimeConfirm(job.getId(), job.getSyncState());
                robotManager.updateRobotUnresponsiveTime(robotKey, false);
            }
        }
        //许可收发指令/设置机器状态指令
        DubboResult<SmartRobotDto> smartRobotResult = robotManager.queryRobotByKey(robotKey);
        if(smartRobotResult.success() && smartRobotResult.getObject().licenseOrRobotStateToChange()){
            SmartRobotDto robot = smartRobotResult.getObject();
            if(robot.getRobotStateLastUpdateTime().plusSeconds(jobConfirmDefaultTimeout).isBefore(LocalDateTime.now())){
                robotManager.robotActionOvertimeConfirm(robot.getMachineCode(), robot.getMachineLoginUser(), robot.getRobotState(), robot.getRobotStateToChange());
                robotManager.updateRobotUnresponsiveTime(robotKey, false);
            }
        }
        //连续N次确认超时置状态Unresponsive
        if(robotManager.robotUnresponsive(robotKey)){
            robotManager.setRobotState(robotKey, RobotState.Unresponsive.getState());
            log.error("机器{}无响应", robotKey);
            robotManager.updateRobotUnresponsiveTime(robotKey, true);
        }
    }

    //处理心跳丢失
    public void doDisconnect(){
        //T时间内未收到心跳即断连，需告警运维。
        //心跳重新建立时需判断上一个运行任务是否为终态，否则下发更新任务状态指令
    }

}
