package com.friday.tooldriver.service.rpa.driverimpl;

import com.friday.base.file.service.FileCommonService;
import com.friday.tooldriver.service.rpa.supplier.smartrpa.JobExecManager;
import com.friday.tooldriver.service.rpa.supplier.smartrpa.RobotManager;
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.keepalive.constant.taieoc.ProcessFileTypeEnum;
import com.friday.operation.api.keepalive.constant.taieoc.TaieOcConstants;
import com.friday.operation.api.keepalive.dto.smartrpa.OcProcessCommitFilterDto;
import com.friday.operation.api.keepalive.dto.smartrpa.OcProcessReleaseDto;
import com.friday.operation.api.keepalive.dto.smartrpa.OcProcessReleaseFilterDto;
import com.friday.operation.api.tooldriver.dto.common.DriverBase;
import com.friday.operation.api.tooldriver.dto.rpa.*;
import com.friday.tooldriver.constants.DriverSystemCode;
import com.friday.tooldriver.constants.TaieOcManageCode;
import com.friday.tooldriver.dto.smartrpa.SmartJobDto;
import com.friday.tooldriver.dto.smartrpa.SmartRobotDto;
import com.friday.tooldriver.mapper.taieoc.OcProcessCommitMapper;
import com.friday.tooldriver.mapper.taieoc.OcProcessReleaseMapper;
import com.friday.tooldriver.mapper.taieoc.RobotGroupRelationMapper;
import com.friday.tooldriver.po.taieoc.OcProcessCommitPo;
import com.friday.tooldriver.po.taieoc.OcProcessReleasePo;
import com.friday.tooldriver.service.rpa.smartRPA.SmartRPAGroupManage;
import com.friday.tooldriver.service.strategy.ICapabilityDriverService;
import com.friday.tooldriver.service.rpa.smartRPA.ProcessReleaseManage;
import com.friday.tooldriver.utils.NumberUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.TreeSet;
import java.util.stream.Collectors;


/**
 * @Author YaoYao
 * @Date Created In 18:52 2022/12/1
 * @Description 神码RPA驱动能力实现服务类
 * @Modified By
 */
@Service("SmartRPADriver")
@Slf4j
public class SmartRPADriverServiceImpl implements ICapabilityDriverService  {

    @Autowired
    private RobotManager robotManager;
    @Autowired
    private JobExecManager jobExecManager;

    @Autowired
    private SmartRPAGroupManage groupManage;

    @Autowired
    private ProcessReleaseManage processReleaseManage;

    @Autowired
    private OcProcessCommitMapper ocProcessCommitMapper;

    @Autowired
    private OcProcessReleaseMapper processReleaseMapper;

    @Autowired
    private RobotGroupRelationMapper robotGroupRelationMapper;

    @Autowired
    private FileCommonService fileService;

    @Override
    public DubboResult<TenantDto> createTenant(DriverBase driverBase, TenantDto tenant) {
        return null;
    }

    @Override
    public DubboResult<ArrayList<TenantDto>> queryTenants(DriverBase driverBase, TenantSelectFilter filter) {
        return null;
    }

    @Override
    public DubboResult<TenantDto> queryTenantByKey(DriverBase driverBase, String key) {
        return null;
    }

    @Override
    public DubboResult deleteTenantByKey(DriverBase driverBase, String key) {
        return null;
    }

    @Override
    public DubboResult updateTenantByKey(DriverBase driverBase, String key) {
        return null;
    }

    @Override
    public DubboResult activateTenantByKey(DriverBase driverBase, String key) {
        return null;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public DubboResult<GroupDto> createGroup(DriverBase driverBase, GroupDto group) {
        try{
            return groupManage.addGroup(driverBase, group);
        }catch (Exception e){
            log.error(TaieOcManageCode.FAIL.toString(), e);
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return DubboResult.newFailure(TaieOcManageCode.FAIL.getCode(), TaieOcManageCode.FAIL.getMessage());
        }
    }


    @Override
    public DubboResult<ArrayList<GroupDto>> queryGroups(DriverBase driverBase, GroupFilter filter) {
        return groupManage.queryGroupInfo(driverBase, filter);
    }

    @Override
    public DubboResult<GroupDto> queryGroupByKey(DriverBase driverBase, String key) {
        return groupManage.queryGroupByKey(key);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public DubboResult deleteGroupByKey(DriverBase driverBase, String key) {
        processReleaseManage.deleteGroupByKey(key);
        robotGroupRelationMapper.deleteByGroupId(null, Long.parseLong(key));
        this.groupManage.deleteGroupByKey(key);
        return DubboResult.newSuccess();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public DubboResult<CodePackageDto> uploadPackage(DriverBase driverBase, CodePackageDto pkg) {
        try{
            if(StringUtils.isAnyEmpty(pkg.getName(), pkg.getFilePath(), pkg.getFileName())){
                return DubboResult.newFailure(TaieOcManageCode.PARAM_VERIFY_ERROR.getCode(), TaieOcManageCode.PARAM_VERIFY_ERROR.getMessage());
            }
            pkg.setFileType(StringUtils.isEmpty(pkg.getFileType())?ProcessFileTypeEnum.EXECUTE.getKey():pkg.getFileType());
            OcProcessCommitPo ocProcessCommitPo = this.processReleaseManage.obtainMaxVersion(driverBase.getTenantKey(), pkg.getName(), pkg.getFileType());
            String oldVersion = TaieOcConstants.EMP_STRING;
            if(!ObjectUtils.isEmpty(ocProcessCommitPo)){
                oldVersion = ocProcessCommitPo.getUploadVersion();
            }
            // 生成新的版本号
            String versionNew = NumberUtils.generateVersion(oldVersion, StringUtils.isEmpty(pkg.getVersionType())?TaieOcConstants.SMALL_TYPE: pkg.getVersionType());
            // TODO git合并接口
            // 新版本信息落库
            this.processReleaseManage.uploadPackage(driverBase.getTenantKey(), versionNew, pkg);
            pkg.setKey(pkg.getName() + TaieOcConstants.EN_COLON + versionNew);
            return DubboResult.newSuccess(pkg);
        }catch (Exception e){
            log.error(TaieOcManageCode.INTERNAL_ERROR.toString(), e);
            return DubboResult.newFailure(TaieOcManageCode.INTERNAL_ERROR.getCode(), TaieOcManageCode.INTERNAL_ERROR.getMessage());
        }

    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public DubboResult deletePackageByKey(DriverBase driverBase, String key) {
        if(ObjectUtils.isEmpty(driverBase.getTenantKey()) || !key.contains(TaieOcConstants.EN_COLON)){
            return DubboResult.newFailure(TaieOcManageCode.PARAM_VERIFY_ERROR.getCode(), TaieOcManageCode.PARAM_VERIFY_ERROR.getMessage());
        }
        // 判断代码包是否为当前活动版本
        String[] split = key.split(TaieOcConstants.EN_COLON);
        List<OcProcessReleaseDto> ocProcessReleaseDtos = processReleaseManage.queryProcessReleaseInfo(driverBase.getTenantKey(), split[0], split[1]);
        if (!CollectionUtils.isEmpty(ocProcessReleaseDtos)) {
            log.info(TaieOcManageCode.ACTIVITY_STATE_ERROR.toString());
            return DubboResult.newFailure(TaieOcManageCode.ACTIVITY_STATE_ERROR.getCode(), TaieOcManageCode.ACTIVITY_STATE_ERROR.getMessage());
        }
        processReleaseManage.deletePackage(driverBase.getTenantKey(), split[0], split[1]);
        return DubboResult.newSuccess(TaieOcManageCode.SUCCESS.getMessage());
    }

    @Override
    @MethodLog(desc = "获取smartRPA代码包信息")
    public DubboResult<ArrayList<CodePackageDto>> queryPackages(DriverBase driverBase, CodePackageFilter filter) {
        List<OcProcessCommitPo> processCommitPos = ocProcessCommitMapper.selectByParam(OcProcessCommitFilterDto.builder()
                .fileType(filter.getFileType())
                .uploadVersion(filter.getVersion())
                .processName(filter.getName())
                .ocTenantId(Long.parseLong(driverBase.getTenantKey()))
                .build()
        );
        ArrayList<CodePackageDto> packageDto = processCommitPos.stream().map(OcProcessCommitPo::createCodePackageDto).collect(Collectors.collectingAndThen(Collectors.toCollection(()->new TreeSet<>(Comparator.comparing(x->x.getName() +x.getVersion()))), ArrayList::new));
        return DubboResult.newSuccess(packageDto);
    }

    @Override
    public DubboResult<CodePackageDto> queryPackageByKey(DriverBase driverBase, String key) {
        List<OcProcessCommitPo> processCommitPos = ocProcessCommitMapper.selectByParam(OcProcessCommitFilterDto.builder()
                        .ocTenantId(Long.parseLong(driverBase.getTenantKey()))
                .uploadVersion(key.split(TaieOcConstants.EN_COLON)[1])
                .processName(key.split(TaieOcConstants.EN_COLON)[0])
                .build());
        if(CollectionUtils.isEmpty(processCommitPos)){
            return DubboResult.newFailure(DriverSystemCode.CODE_FILE_NOT_EXIST.getCode(), DriverSystemCode.CODE_FILE_NOT_EXIST.getMessage());
        }
        return DubboResult.newSuccess(processCommitPos.get(0).createCodePackageDto());
    }

    @Override
    public DubboResult downloadPackageByKey(DriverBase driverBase, String key, String fileName) {
        try{
            if(StringUtils.isEmpty(key)){
                return DubboResult.newFailure(DriverSystemCode.INVALID_KEY_ERROR.getCode(), DriverSystemCode.INVALID_KEY_ERROR.getMessage());
            }
            List<OcProcessCommitPo> processCommitPos = ocProcessCommitMapper.selectByParam(OcProcessCommitFilterDto.builder()
                            .ocTenantId(Long.parseLong(driverBase.getTenantKey()))
                            .processName(key.substring(0, key.indexOf(TaieOcConstants.EN_COLON)))
                            .uploadVersion(key.substring(key.indexOf(TaieOcConstants.EN_COLON) + 1))
                    .build());
            if(CollectionUtils.isEmpty(processCommitPos)){
                return DubboResult.newFailure(DriverSystemCode.INVALID_KEY_ERROR.getCode(), DriverSystemCode.INVALID_KEY_ERROR.getMessage());
            }
            OcProcessCommitPo ocProcessCommitPo = processCommitPos.get(0);
            String filePath = this.fileService.getFilePath(ocProcessCommitPo.getFileKey());
            if(ObjectUtils.isEmpty(filePath)){
                return DubboResult.newSuccess(FileInfoDto.builder().fileName(ocProcessCommitPo.getFileName())
                        .path(ocProcessCommitPo.getFileKey()).build());
            }
            return DubboResult.newSuccess(FileInfoDto.builder().fileName(filePath.substring(filePath.lastIndexOf(TaieOcConstants.EN_SLASH) + 1))
                    .path(filePath).build());
        }catch (Exception e){
            log.error(DriverSystemCode.CODE_FILE_DOWNLOAD_ERROR.toString(), e);
            return DubboResult.newFailure(DriverSystemCode.CODE_FILE_DOWNLOAD_ERROR.getCode(), DriverSystemCode.CODE_FILE_DOWNLOAD_ERROR.getMessage());
        }

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public DubboResult<ReleaseDto> releaseProcess(DriverBase driverBase, ReleaseDto release) {
        if(ObjectUtils.isEmpty(driverBase.getGroupKey())){
            return DubboResult.newFailure(TaieOcManageCode.DRIVER_GROUP_EMPTY.getCode(), TaieOcManageCode.DRIVER_GROUP_EMPTY.getMessage());
        }
        List<OcProcessCommitPo> ocProcessCommitPos = ocProcessCommitMapper.selectByParam(OcProcessCommitFilterDto.builder()
                .uploadVersion(release.getProcessVersion())
                .processName(release.getName())
                .ocTenantId(Long.parseLong(driverBase.getTenantKey()))
                .fileType(ProcessFileTypeEnum.EXECUTE.getKey())
                .build()
        );
        if(CollectionUtils.isEmpty(ocProcessCommitPos)){
            log.info(TaieOcManageCode.PACKAGE_NOT_EXIST.toString());
            return DubboResult.newFailure(TaieOcManageCode.PACKAGE_NOT_EXIST.getCode(), TaieOcManageCode.PACKAGE_NOT_EXIST.getMessage());
        }
        // 查询发布记录, 发布过则在原发布基础更新
        List<OcProcessReleaseDto> ocProcessReleaseDtos = processReleaseMapper.selectProcessRelease(OcProcessReleaseFilterDto.builder()
                .ocGroupId(Long.parseLong(driverBase.getGroupKey())).build());
        OcProcessReleasePo ocProcessReleasePo;
        if(CollectionUtils.isEmpty(ocProcessReleaseDtos)){
            // 发布新版本
            ocProcessReleasePo = OcProcessReleasePo.builder().ocGroupId(Long.parseLong(driverBase.getGroupKey())).processCommitId(ocProcessCommitPos.get(0).getId()).build();
            processReleaseMapper.insert(ocProcessReleasePo);
        }else{
            ocProcessReleasePo = OcProcessReleasePo.builder().processCommitId(ocProcessCommitPos.get(0).getId()).id(ocProcessReleaseDtos.get(0).getReleaseId()).build();
            processReleaseMapper.update(ocProcessReleasePo);
        }
        release.setKey(String.valueOf(ocProcessReleasePo.getId()));
        return DubboResult.newSuccess(release);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public DubboResult deleteProcessReleaseByKey(DriverBase driverBase, String key) {
        processReleaseMapper.update(OcProcessReleasePo.builder()
                .id(Long.parseLong(key))
                .isValid(false)
                .build()
        );
        return null;
    }

    @Override
    public DubboResult<ArrayList<ReleaseDto>> queryProcessReleases(DriverBase driverBase, ReleaseFilter filter) {
        List<OcProcessReleaseDto> processCommitPos = processReleaseMapper.selectProcessRelease(OcProcessReleaseFilterDto.builder()
                .ocTenantId(StringUtils.isEmpty(driverBase.getTenantKey())?null:Long.parseLong(driverBase.getTenantKey()))
                .processName(filter.getName())
                .version(filter.getProcessVersion())
                .ocGroupId(StringUtils.isEmpty(driverBase.getGroupKey())?null:Long.parseLong(driverBase.getGroupKey()))
                .build());
        ArrayList<ReleaseDto> releaseDto = processCommitPos.stream()
                .map(e -> ReleaseDto.builder()
                        .key(String.valueOf(e.getReleaseId()))
                        .latestVersion(e.getProcessVersion().equals(ocProcessCommitMapper.selectMaxVersion(
                                OcProcessCommitFilterDto.builder()
                                        .ocTenantId(Long.parseLong(driverBase.getTenantKey()))
                                        .processName(e.getProcessName())
                                        .fileType(ProcessFileTypeEnum.EXECUTE.getKey())
                                        .build()).getUploadVersion()))
                        .processVersion(e.getProcessVersion())
                        .name(e.getProcessName())
                        .build()).collect(Collectors.toCollection(ArrayList::new));
        return DubboResult.newSuccess(releaseDto);
    }

    @Override
    public DubboResult<ReleaseDto> queryProcessReleaseByKey(DriverBase driverBase, String key) {
        List<OcProcessReleaseDto> ocProcessReleaseDtos = processReleaseMapper.selectProcessRelease(OcProcessReleaseFilterDto.builder().releaseId(Long.parseLong(key)).build());
        if(!CollectionUtils.isEmpty(ocProcessReleaseDtos)){
            return DubboResult.newSuccess(ocProcessReleaseDtos.get(0).tranToReleaseDto());
        }
        return DubboResult.newFailure(DubboResult.SERVER_EXCEPTION, "未查询到指定流程");
    }

    @Override
    public DubboResult<RobotDto> createRobot(DriverBase driverBase, RobotDto robot) {
        SmartRobotDto smartRobotDto = SmartRobotDto.builder()
                .machineCode(robot.getMachineCode())
                .machineLoginUser(robot.getUserName())
                .ocTenantId(robot.getOcTenantId())
                .robotKey(robot.getMachineCode() + robot.getUserName())
                .authState(robot.getAuthState())
                .build();
        DubboResult<SmartRobotDto> result = robotManager.createRobot(smartRobotDto);
        if(!result.success()){
            return DubboResult.newFailure(result.getCode(), result.getMessage());
        }
        SmartRobotDto smartRobot = result.getObject();
        return DubboResult.newSuccess(smartRobot.transferToRobotDto());
    }

    @Override
    public DubboResult deleteRobotByKey(DriverBase driverBase, String key) {
        return robotManager.deleteRobotByKey(key);
    }

    @Override
    public DubboResult<ArrayList<RobotDto>> queryRobots(DriverBase driverBase, RobotFilter filter) {
        //暂无场景
        return DubboResult.newSuccess(Lists.newArrayList());
    }

    @Override
    public DubboResult<RobotDto> queryRobotByKey(DriverBase driverBase, String key) {
        DubboResult<SmartRobotDto> result = robotManager.queryRobotByKey(key);
        if(!result.success()){
            return DubboResult.newFailure(result.getCode(), result.getMessage());
        }
        SmartRobotDto smartRobot = result.getObject();
        return DubboResult.newSuccess(smartRobot.transferToRobotDto());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public DubboResult assignRobotToGroup(DriverBase driverBase, String robotKey, String groupKey) {
        return robotManager.assignRobotToGroup(robotKey, groupKey);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public DubboResult removeRobotFromGroup(DriverBase driverBase, String robotKey, String groupKey) {
        return robotManager.removeRobotFromGroup(robotKey, groupKey);
    }

    @Override
    public DubboResult enableLicense(DriverBase driverBase, LicenseDto license) {
        return robotManager.distributeLicense(license.getRobotKey(), true);
    }

    @Override
    public DubboResult disableLicense(DriverBase driverBase, LicenseDto license) {
        return robotManager.distributeLicense(license.getRobotKey(), false);
    }

    @Override
    public DubboResult<Integer> availableLicenseNum(DriverBase driverBase, String licenseType) {
        return DubboResult.newSuccess(Integer.MAX_VALUE);
    }

    @Override
    public DubboResult<JobDto> startJob(DriverBase driverBase, StartJobDto start) {
        if(StringUtils.isAnyEmpty(start.getRobotKey(), start.getProcessReleaseKey())){
            return DubboResult.newFailure(DubboResult.SERVER_EXCEPTION, "无效的启动入参");
        }
        DubboResult<RobotDto> robotResult = this.queryRobotByKey(driverBase, start.getRobotKey());
        if(!robotResult.success()){
            return DubboResult.newFailure(DubboResult.SERVER_EXCEPTION, "未找到指定机器人:" + start.getRobotKey());
        }
        DubboResult<ReleaseDto> releaseResult = this.queryProcessReleaseByKey(driverBase, start.getProcessReleaseKey());
        if(!releaseResult.success()){
            return DubboResult.newFailure(DubboResult.SERVER_EXCEPTION, "未找到指定流程:" + start.getProcessReleaseKey());
        }
        SmartJobDto smartJob = SmartJobDto.builder()
                .source(start.getSource())
                .robotKey(start.getRobotKey())
                .processReleaseKey(start.getProcessReleaseKey())
                .inputArguments(start.getInputArguments())
                .build();
        DubboResult<SmartJobDto> startResult = jobExecManager.startJob(smartJob);
        if(!startResult.success()){
            return DubboResult.newFailure(startResult.getCode(), startResult.getMessage());
        }
        return DubboResult.newSuccess(startResult.getObject().transferToJobDto());
    }

    @Override
    public DubboResult<JobDto> restartJob(DriverBase driverBase, String jobId) {
        DubboResult<SmartJobDto>  jobResult = jobExecManager.restartJobByKey(Integer.parseInt(jobId));
        if(!jobResult.success()){
            return DubboResult.newFailure(jobResult.getCode(), jobResult.getMessage());
        }
        return DubboResult.newSuccess(jobResult.getObject().transferToJobDto());
    }

    @Override
    public DubboResult pauseJob(DriverBase driverBase, String jobId) {
        return jobExecManager.pauseJob(Integer.parseInt(jobId));
    }

    @Override
    public DubboResult resumeJob(DriverBase driverBase, String jobId) {
        return jobExecManager.resumeJob(Integer.parseInt(jobId));
    }

    @Override
    public DubboResult stopJob(DriverBase driverBase, String jobId) {
        return jobExecManager.stopJob(Integer.parseInt(jobId));
    }

    @Override
    public DubboResult stopJobs(DriverBase driverBase, List<String> jobIdList) {
        return DubboResult.newSuccess();
    }

    @Override
    public DubboResult<ArrayList<JobDto>> queryJobs(DriverBase driverBase, JobFilter filter) {
        return DubboResult.newSuccess(Lists.newArrayList());
    }

    @Override
    public DubboResult<JobDto> queryJobByKey(DriverBase driverBase, String jobId) {
        DubboResult<SmartJobDto> jobResult = jobExecManager.queryJobByKey(Integer.parseInt(jobId));
        if(!jobResult.success()){
            return DubboResult.newFailure(jobResult.getCode(), jobResult.getMessage());
        }
        return DubboResult.newSuccess(jobResult.getObject().transferToJobDto());
    }
}
