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

import com.google.common.collect.Lists;
import com.friday.base.common.response.DubboResult;
import com.friday.operation.api.executor.dto.executor.ExecutorDto;
import com.friday.operation.api.executor.dto.executor.ExecutorFilter;
import com.friday.operation.api.executor.dto.executor.RelatedTaskRelease;
import com.friday.operation.api.executor.dto.executor.TaskRelease;
import com.friday.operation.api.executor.dto.module.InvokeDto;
import com.friday.operation.api.executor.service.IDynamicModuleService;
import com.friday.operation.api.tooldriver.dto.common.DriverBase;
import com.friday.operation.api.tooldriver.dto.rpa.*;
import com.friday.tooldriver.dto.common.GroupSelectFilter;
import com.friday.tooldriver.dto.common.PackageSelectFilter;
import com.friday.tooldriver.exception.DriverBusinessException;
import com.friday.tooldriver.mapper.executor.ExecutorMapper;
import com.friday.tooldriver.mapper.taieoc.ProgramCodePackageMapper;
import com.friday.tooldriver.mapper.taieoc.ProgramGroupMapper;
import com.friday.tooldriver.mapper.taieoc.ProgramProcessReleaseMapper;
import com.friday.tooldriver.po.executor.ExecutorPo;
import com.friday.tooldriver.po.taieoc.CodePackagePo;
import com.friday.tooldriver.po.taieoc.GroupPo;
import com.friday.tooldriver.po.taieoc.ReleasePo;
import com.friday.tooldriver.service.executor.ExecutorManager;
import com.friday.tooldriver.service.strategy.ICapabilityDriverService;
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 java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import static com.friday.operation.api.executor.constant.BizStrategyMethod.EXECUTE_JOB;
import static com.friday.operation.api.tooldriver.constant.ProcessTypeEnum.Related;

/**
 * @Author YaoYao
 * @Date Created In 15:56 2023/4/13
 * @Description
 * @Modified By
 */
@Slf4j
@Service("ProgramDefaultDriver")
public class ProgramDefaultDriverServiceImpl implements ICapabilityDriverService {
    @Autowired
    private ProgramGroupMapper programGroupMapper;
    @Autowired(required = false)
    private IDynamicModuleService dynamicModuleService;
    @Autowired
    private ExecutorManager executorManager;
    @Autowired
    private ExecutorMapper executorMapper;
    @Autowired
    private ProgramCodePackageMapper programCodePackageMapper;
    @Autowired
    private ProgramProcessReleaseMapper programProcessReleaseMapper;

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

    @Override
    public DubboResult<ArrayList<TenantDto>> queryTenants(DriverBase driverBase, TenantSelectFilter filter) {
        return DubboResult.newSuccess(Lists.newArrayList(TenantDto.builder()
                .name("程序任务默认中控")
                .key("1")
                .build()));
    }

    @Override
    public DubboResult<TenantDto> queryTenantByKey(DriverBase driverBase, String key) {
        return DubboResult.newSuccess(TenantDto.builder()
                .name("程序任务默认中控")
                .key("1")
                .build());
    }

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

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

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

    @Override
    public DubboResult<GroupDto> createGroup(DriverBase driverBase, GroupDto group) {
        try {
            Integer tenantId = Integer.parseInt(driverBase.getTenantKey());
            List<GroupPo> groups = programGroupMapper.selectGroups(GroupSelectFilter.builder()
                    .ocTenantKey(tenantId)
                    .name(group.getName())
                    .build());
            if(!CollectionUtils.isEmpty(groups)){
                return DubboResult.newFailure(DubboResult.SERVER_EXCEPTION, "组已存在");
            }
            GroupPo groupPo = GroupPo.builder()
                    .ocTenantKey(tenantId)
                    .name(group.getName())
                    .description(group.getDescription())
                    .build();
            programGroupMapper.insertGroup(groupPo);
            group.setKey(String.valueOf(groupPo.getId()));
            return DubboResult.newSuccess(group);
        } catch (Exception e){
            log.error("创建组失败", e);
            return DubboResult.newFailure(DubboResult.SERVER_EXCEPTION, "创建组失败");
        }
    }

    @Override
    public DubboResult<ArrayList<GroupDto>> queryGroups(DriverBase driverBase, GroupFilter filter) {
        try {
            List<GroupPo> groupPos = programGroupMapper.selectGroups(GroupSelectFilter.builder()
                    .ocTenantKey(Integer.parseInt(driverBase.getTenantKey()))
                    .name(filter.getName())
                    .build());
            List<GroupDto> groups = groupPos.stream().map(GroupPo::transToGroupDto).collect(Collectors.toList());
            return DubboResult.newSuccess((ArrayList<GroupDto>) groups);
        } catch (Exception e){
            log.error("查询组失败", e);
            return DubboResult.newFailure(DubboResult.SERVER_EXCEPTION, "查询组失败");
        }
    }

    @Override
    public DubboResult<GroupDto> queryGroupByKey(DriverBase driverBase, String key) {
        try {
            GroupPo groupPo = programGroupMapper.selectGroupById(Integer.parseInt(key));
            if(Objects.isNull(groupPo)){
                return DubboResult.newFailure(DubboResult.SERVER_EXCEPTION, "未查询到组信息");
            }
            return DubboResult.newSuccess(groupPo.transToGroupDto());
        } catch (Exception e){
            log.error("查询单个组失败", e);
            return DubboResult.newFailure(DubboResult.SERVER_EXCEPTION, "查询单个组失败");
        }
    }

    @Override
    public DubboResult deleteGroupByKey(DriverBase driverBase, String key) {
        try {
            programGroupMapper.deleteGroupById(Integer.parseInt(key));
            return DubboResult.newSuccess();
        } catch (Exception e){
            log.error("删除组失败", e);
            return DubboResult.newFailure(DubboResult.SERVER_EXCEPTION, "删除组失败");
        }
    }

    @Override
    public DubboResult<CodePackageDto> uploadPackage(DriverBase driverBase, CodePackageDto pkg) {
        //bff层存入共享盘，此处传入的filePath为绝对路径（含文件名）
        if(StringUtils.isAnyEmpty(driverBase.getTenantKey(), pkg.getName(), pkg.getFilePath())){
            return DubboResult.newFailure(DubboResult.CLIENT_EXCEPTION, "缺少必填入参");
        }
        try {
            Integer ocTenantKey = Integer.parseInt(driverBase.getTenantKey());
            CodePackagePo latestValidPackage = programCodePackageMapper.selectLatestValidPackage(ocTenantKey, pkg.getName());
            String version = NumberUtils.generateVersion("", "small");
            String versionType = "small";
            if(Objects.nonNull(latestValidPackage)){
                if(StringUtils.isNotEmpty(pkg.getVersionType())){
                    versionType = pkg.getVersionType();
                }
                version = NumberUtils.generateVersion(latestValidPackage.getVersion(), versionType);
            }
            CodePackagePo code = CodePackagePo.builder()
                    .ocTenantKey(ocTenantKey)
                    .filePath(pkg.getFilePath())
                    .packageName(pkg.getName())
                    .version(version)
                    .build();
            programCodePackageMapper.insertPackage(code);
            pkg.setKey(String.join(":", pkg.getName(), version));
            pkg.setVersion(version);
            return DubboResult.newSuccess(pkg);
        } catch (Exception e){
            log.error("上传程序代码包失败", e);
            return DubboResult.newFailure(DubboResult.SERVER_EXCEPTION, "上传代码包失败");
        }
    }

    @Override
    public DubboResult deletePackageByKey(DriverBase driverBase, String key) {
        if(!key.contains(":") || key.split(":").length != 2){
            return DubboResult.newFailure(DubboResult.CLIENT_EXCEPTION, "无效Key");
        }
        try {
            Integer ocTenantId = Integer.parseInt(driverBase.getTenantKey());
            String processName = key.split(":")[0];
            String version = key.split(":")[1];
            programCodePackageMapper.deletePackageByKey(ocTenantId, processName, version);
            return DubboResult.newSuccess();
        } catch (Exception e){
            log.error("删除代码包失败", e);
            return DubboResult.newFailure(DubboResult.SERVER_EXCEPTION, "删除代码包失败");
        }
    }

    @Override
    public DubboResult<ArrayList<CodePackageDto>> queryPackages(DriverBase driverBase, CodePackageFilter filter) {
        //目前仅支持根据processName查询版本号
        PackageSelectFilter selector = PackageSelectFilter.builder()
                .ocTenantKey(Integer.parseInt(driverBase.getTenantKey()))
                .packageName(filter.getName())
                .version(filter.getVersion())
                .build();
        List<CodePackagePo> codes = programCodePackageMapper.selectPackages(selector);
        List<CodePackageDto> codePackages = codes.stream().map(CodePackagePo::transToCodePackage).collect(Collectors.toList());
        return DubboResult.newSuccess((ArrayList<CodePackageDto>) codePackages);
    }

    @Override
    public DubboResult<CodePackageDto> queryPackageByKey(DriverBase driverBase, String key) {
        if(!key.contains(":") || key.split(":").length != 2){
            return DubboResult.newFailure(DubboResult.CLIENT_EXCEPTION, "无效Key");
        }
        try {
            Integer ocTenantId = Integer.parseInt(driverBase.getTenantKey());
            String processName = key.split(":")[0];
            String version = key.split(":")[1];
            List<CodePackagePo> codes = programCodePackageMapper.selectPackages(PackageSelectFilter.builder()
                    .ocTenantKey(ocTenantId)
                    .packageName(processName)
                    .version(version)
                    .valid(true)
                    .build());
            if(CollectionUtils.isEmpty(codes)){
                return DubboResult.newFailure(DubboResult.CLIENT_EXCEPTION, "未找到代码包");
            }
            return DubboResult.newSuccess(codes.get(0).transToCodePackage());
        } catch (Exception e){
            log.error("删除代码包失败", e);
            return DubboResult.newFailure(DubboResult.SERVER_EXCEPTION, "删除代码包失败");
        }

    }

    @Override
    public DubboResult downloadPackageByKey(DriverBase driverBase, String key, String fileName) {
        DubboResult<CodePackageDto> codeResult = this.queryPackageByKey(driverBase, key);
        if(!codeResult.success()){
            return codeResult;
        }
        return DubboResult.newSuccess(FileInfoDto.builder().path(codeResult.getObject().getFilePath()).fileName(codeResult.getObject().getFileName()).build());
    }

    @Override
    @Transactional
    public DubboResult<ReleaseDto> releaseProcess(DriverBase driverBase, ReleaseDto release) {
        if(StringUtils.isAnyEmpty(release.getCodePackageName(), release.getProcessVersion())){
            return DubboResult.newFailure(DubboResult.CLIENT_EXCEPTION, "缺少必填入参");
        }
        try {
            CodePackagePo packagePo = programCodePackageMapper.selectPackageByKey(Integer.parseInt(driverBase.getTenantKey()), release.getCodePackageName(), release.getProcessVersion());
            if(Objects.isNull(packagePo)){
                return DubboResult.newFailure(DubboResult.SERVER_EXCEPTION, "未找到对应程序代码包");
            }
            Integer groupId = Integer.parseInt(driverBase.getGroupKey());
            GroupPo groupPo = programGroupMapper.selectGroupById(groupId);
            if(Objects.isNull(groupPo)){
                return DubboResult.newFailure(DubboResult.SERVER_EXCEPTION, "组不存在");
            }
            String releaseName = StringUtils.isEmpty(release.getName()) ? release.getCodePackageName() : release.getName();
            ProgramProcessReleaseDto releaseDto = programProcessReleaseMapper.selectReleaseDto(groupId, releaseName, release.getProcessVersion());
            if(Objects.nonNull(releaseDto)){
                return DubboResult.newFailure(DubboResult.SERVER_EXCEPTION, "组内已存在同名流程");
            }
            ReleasePo releasePo = ReleasePo.builder()
                    .packageId(packagePo.getId())
                    .programGroupId(groupId)
                    .releaseName(releaseName)
                    .processType(release.getProcessType())
                    .build();
            programProcessReleaseMapper.insertRelease(releasePo);
            release.setKey(String.valueOf(releasePo.getId()));
            release.setName(releaseName);
            if(Related.getType().equals(release.getProcessType())){
                RelatedTaskRelease related = RelatedTaskRelease.builder()
                        .groupKey(driverBase.getGroupKey())
                        .module(release.getName())
                        .version(release.getProcessVersion())
                        .build();
                executorManager.releaseRelatedTask(related);
            }
            return DubboResult.newSuccess(release);
        } catch (DriverBusinessException e){
            log.error("发布关联任务失败", e);
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return DubboResult.newFailure(DubboResult.SERVER_EXCEPTION, "发布关联任务失败:" + e.getMessage());
        } catch (Exception e){
            log.error("发布程序任务流程失败", e);
            return DubboResult.newFailure(DubboResult.SERVER_EXCEPTION, "发布程序任务流程失败");
        }
    }

    @Override
    @Transactional
    public DubboResult deleteProcessReleaseByKey(DriverBase driverBase, String key) {
        try {
            Integer releaseId = Integer.parseInt(key);
            ProgramProcessReleaseDto release = programProcessReleaseMapper.selectReleaseDtoById(releaseId);
            if(Objects.isNull(release)){
                return DubboResult.newFailure(DubboResult.SERVER_EXCEPTION, "未找到对应发布流程");
            }
            if(Related.getType().equals(release.getProcessType())){
                executorManager.removeRelateTaskRelease(release.getReleaseName(), release.getProcessVersion());
            }
            programProcessReleaseMapper.deleteReleaseById(releaseId);
            return DubboResult.newSuccess();
        } catch (DriverBusinessException e){
            log.error("移除关联任务失败", e);
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return DubboResult.newFailure(DubboResult.SERVER_EXCEPTION, "移除关联任务失败");
        } catch (Exception e){
            log.error("删除程序发布任务流程失败", e);
            return DubboResult.newFailure(DubboResult.SERVER_EXCEPTION, "删除程序发布任务流程失败");
        }
    }

    @Override
    public DubboResult<ArrayList<ReleaseDto>> queryProcessReleases(DriverBase driverBase, ReleaseFilter filter) {
        return DubboResult.newSuccess(Lists.newArrayList());
    }

    @Override
    public DubboResult<ReleaseDto> queryProcessReleaseByKey(DriverBase driverBase, String key) {
        try {
            Integer releaseId = Integer.parseInt(key);
            ProgramProcessReleaseDto releaseDto = programProcessReleaseMapper.selectReleaseDtoById(releaseId);
            if(Objects.isNull(releaseDto)){
                return DubboResult.newFailure(DubboResult.SERVER_EXCEPTION, "未找到对应发布流程");
            }
            return DubboResult.newSuccess(releaseDto.transferToReleaseDto());
        } catch (Exception e){
            log.error("查询单个程序发布任务流程失败", e);
            return DubboResult.newFailure(DubboResult.SERVER_EXCEPTION, "查询单个程序发布任务流程失败");
        }
    }

    @Override
    public DubboResult<RobotDto> createRobot(DriverBase driverBase, RobotDto robot) {
        try {
            ExecutorDto executorDto = ExecutorDto.builder()
                    .tenantKey(driverBase.getTenantKey())
                    .ip(robot.getIp())
                    .user(robot.getUserName())
                    .build();
            ExecutorDto executor = executorManager.registerExecutor(executorDto);
            robot.setKey(executor.getKey());
            return DubboResult.newSuccess(robot);
        } catch (Exception e){
            log.error("创建程序任务执行器失败", e);
            return DubboResult.newFailure(DubboResult.SERVER_EXCEPTION, "创建程序任务执行器失败");
        }
    }

    @Override
    public DubboResult deleteRobotByKey(DriverBase driverBase, String key) {
        try {
            executorManager.deleteExecutor(Integer.parseInt(key));
            return DubboResult.newSuccess();
        } catch (Exception e){
            log.error("删除程序任务执行器失败", e);
            return DubboResult.newFailure(DubboResult.SERVER_EXCEPTION, "删除程序任务执行器失败");
        }
    }

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

    @Override
    public DubboResult<RobotDto> queryRobotByKey(DriverBase driverBase, String key) {
        try {
            ExecutorFilter filter = new ExecutorFilter();
            filter.setKey(key);
            List<ExecutorDto> executors = executorManager.queryExecutors(filter);
            if(CollectionUtils.isEmpty(executors)){
                return DubboResult.newFailure(DubboResult.SERVER_EXCEPTION, "未找到对应的程序执行器");
            }
            return DubboResult.newSuccess(executors.get(0).transferToRobotDto());
        } catch (Exception e){
            log.error("查询单个程序任务执行器失败", e);
            return DubboResult.newFailure(DubboResult.SERVER_EXCEPTION, "查询单个程序任务执行器失败");
        }
    }

    @Override
    public DubboResult assignRobotToGroup(DriverBase driverBase, String robotKey, String groupKey) {
        try {
            TaskRelease release = this.createRelease(robotKey, groupKey);
            executorManager.releaseTask(release);
            return DubboResult.newSuccess();
        } catch (DriverBusinessException e){
            log.error("将程序执行器加入组失败:{}", e.getMessage());
            return DubboResult.newFailure(DubboResult.SERVER_EXCEPTION, "将程序执行器加入组失败:" + e.getMessage());
        } catch (Exception e){
            log.error("将程序执行器加入组失败", e);
            return DubboResult.newFailure(DubboResult.SERVER_EXCEPTION, "将程序执行器加入组失败");
        }
    }

    @Override
    public DubboResult removeRobotFromGroup(DriverBase driverBase, String robotKey, String groupKey) {
        try {
            TaskRelease release = this.createRelease(robotKey, groupKey);
            executorManager.removeTaskRelease(release);
            return DubboResult.newSuccess();
        } catch (DriverBusinessException e){
            log.error("将程序执行器移出组失败:{}", e.getMessage());
            return DubboResult.newFailure(DubboResult.SERVER_EXCEPTION, "将程序执行器移出组失败:" + e.getMessage());
        } catch (Exception e){
            log.error("将程序执行器移出组失败", e);
            return DubboResult.newFailure(DubboResult.SERVER_EXCEPTION, "将程序执行器移出组失败");
        }
    }

    private TaskRelease createRelease(String robotKey, String groupKey) throws DriverBusinessException {
        ExecutorPo executor = executorMapper.selectExecutorById(Integer.parseInt(robotKey));
        if(Objects.isNull(executor)){
            throw new DriverBusinessException("未找到对应的程序执行器");
        }
        Integer groupId = Integer.parseInt(groupKey);
        GroupPo groupPo = programGroupMapper.selectGroupById(groupId);
        if(Objects.isNull(groupPo)){
            throw new DriverBusinessException("组不存在");
        }
        ProgramProcessReleaseDto latestReleasedProcess = programProcessReleaseMapper.selectLatestReleaseDto(groupId);
        if(Objects.isNull(latestReleasedProcess)){
            throw new DriverBusinessException("指定组内无已发布的程序流程");
        }
        TaskRelease release = new TaskRelease();
        release.setGroupKey(groupKey);
        release.setExecutorIP(executor.getIp());
        release.setExecutorUser(executor.getUser());
        release.setModule(latestReleasedProcess.getProcessName());
        release.setVersion(latestReleasedProcess.getProcessVersion());
        return release;
    }

    @Override
    public DubboResult enableLicense(DriverBase driverBase, LicenseDto license) {
        return DubboResult.newSuccess();
    }

    @Override
    public DubboResult disableLicense(DriverBase driverBase, LicenseDto license) {
        return DubboResult.newSuccess();
    }

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

    @Override
    public DubboResult<JobDto> startJob(DriverBase driverBase, StartJobDto start) {
        try {
            InvokeDto invoke = InvokeDto.builder()
                    .methodName(EXECUTE_JOB.getMethodName())
                    .build();
            DubboResult result = dynamicModuleService.invoke(invoke);
            return DubboResult.newSuccess(new JobDto());
        } catch (Exception e){
            log.error("执行器方法调用异常", e);
            return DubboResult.newFailure(DubboResult.SERVER_EXCEPTION, "执行器方法调用异常:" + e.getMessage());
        }
    }

    @Override
    public DubboResult<JobDto> restartJob(DriverBase driverBase, String jobId) {
        return null;
    }

    @Override
    public DubboResult pauseJob(DriverBase driverBase, String jobId) {
        return null;
    }

    @Override
    public DubboResult resumeJob(DriverBase driverBase, String jobId) {
        return null;
    }

    @Override
    public DubboResult stopJob(DriverBase driverBase, String jobId) {
        return null;
    }

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

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

    @Override
    public DubboResult<JobDto> queryJobByKey(DriverBase driverBase, String jobId) {
        return null;
    }
}
