package com.microservices.pms.productLibrary.service.impl;

import com.alibaba.fastjson2.JSONObject;
import com.microservices.common.core.constant.CacheConstants;
import com.microservices.common.core.exception.ServiceException;
import com.microservices.common.core.utils.DateUtils;
import com.microservices.common.core.utils.PageUtils;
import com.microservices.common.core.utils.StringUtils;
import com.microservices.common.core.web.page.GenericsTableDataInfo;
import com.microservices.common.redis.service.RedisService;
import com.microservices.common.security.utils.SecurityUtils;
import com.microservices.pms.enterprise.domain.PmsEnterprise;
import com.microservices.pms.enterprise.service.IPmsEnterpriseService;
import com.microservices.pms.productLibrary.domain.*;
import com.microservices.pms.productLibrary.domain.vo.*;
import com.microservices.pms.productLibrary.mapper.PmsProductLibraryRepositoriesMapper;
import com.microservices.pms.productLibrary.service.IPmsProductLibraryRepositoriesService;
import com.microservices.pms.productLibrary.service.IPmsProductLibraryService;
import com.microservices.pms.productLibrary.service.IPmsProductLibrarySnapshotRepoService;
import com.microservices.pms.utils.NexusRequestHelper;
import com.microservices.pms.utils.NexusRequestUrl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

/**
 * 制品库-仓库Service业务层处理
 *
 * @author otto
 * @date 2024-06-20
 */
@Service
public class PmsProductLibraryRepositoriesServiceImpl implements IPmsProductLibraryRepositoriesService {
    @Autowired
    private PmsProductLibraryRepositoriesMapper pmsProductLibraryRepositoriesMapper;
    @Autowired
    private IPmsEnterpriseService pmsEnterpriseService;
    @Autowired
    private NexusRequestHelper nexusRequestHelper;
    @Autowired
    private RedisService redisService;
    @Autowired
    private IPmsProductLibrarySnapshotRepoService pmsProductLibrarySnapshotRepoService;
    @Autowired
    private IPmsProductLibraryService pmsProductLibraryService;

    @Override
    public PmsProductLibraryRepositories selectPmsProductLibraryRepositoriesByName(String enterpriseIdentifier, String name) {
        PmsProductLibraryRepositories pmsProductLibraryRepositories =
                pmsProductLibraryRepositoriesMapper.selectPmsProductLibraryRepositoriesByName(enterpriseIdentifier, name);
        if (pmsProductLibraryRepositories == null) {
            throw new ServiceException("组织下不存在该制品库(组织标识[%s],制品库标识[%s])", enterpriseIdentifier, name);
        }
        return pmsProductLibraryRepositories;
    }

    @Override
    public PmsProductLibraryRepositories selectPmsProductLibraryRepositoriesById(Long repoId) {
        PmsProductLibraryRepositories pmsProductLibraryRepositories =
                pmsProductLibraryRepositoriesMapper.selectPmsProductLibraryRepositoriesById(repoId);
        if (pmsProductLibraryRepositories == null) {
            throw new ServiceException("组织下不存在该制品库(制品库id[%s])", repoId);
        }
        return pmsProductLibraryRepositories;
    }

    @Override
    public NexusHostedRepoInputVo getNexusRepo(String type, String format, String nexusRepoName) {
        NexusHostedRepoInputVo nexusRepo = nexusRequestHelper.doRequestToJavaObject(
                NexusRequestUrl.GET_REPOSITORIES(type, format, nexusRepoName),
                NexusHostedRepoInputVo.class);
        if (nexusRepo == null) {
            throw new ServiceException("该Nexus制品库不存在(制品库标识[%s])）", nexusRepoName);
        }
        return nexusRepo;
    }

    @Override
    public List<Long> getLockStatusRepositoryList(String enterpriseIdentifier, String repoIds) {
        List<Long> lockRepoIdList = new ArrayList<>();
        String[] repoIdArr = repoIds.split(",");
        for (String repoIdStr : repoIdArr) {
            if (StringUtils.isNumeric(repoIdStr)) {
                Long repoId = Long.valueOf(repoIdStr);
                if (getLockRepositoryStatus(enterpriseIdentifier, repoId)) {
                    lockRepoIdList.add(repoId);
                }
            }
        }
        return lockRepoIdList;
    }


    @Override
    public int insertGroupPmsProductLibraryRepositories(GroupRepositoriesInputVo groupRepositoriesInputVo) {
        PmsEnterprise pmsEnterprise = pmsEnterpriseService.selectPmsEnterpriseByIdentifier(groupRepositoriesInputVo.getEnterpriseIdentifier());
        // 校验制品库标识
        Integer count = pmsProductLibraryRepositoriesMapper.selectPmsProductLibraryRepositoriesByNameCount(
                groupRepositoriesInputVo.getName(), groupRepositoriesInputVo.getEnterpriseIdentifier(), groupRepositoriesInputVo.getFormat());
        if (count > 0) {
            throw new ServiceException("该制品库标识在当前组织下已存在(制品库标识[%s])", groupRepositoriesInputVo.getName());
        }
        // 校验制品库别名
        PmsProductLibraryRepositories aliasRepo = pmsProductLibraryRepositoriesMapper.selectPmsProductLibraryRepositoriesByAlias(
                groupRepositoriesInputVo.getAlias(), groupRepositoriesInputVo.getEnterpriseIdentifier());
        if (aliasRepo != null) {
            throw new ServiceException("该制品库名称在当前组织下已存在(制品库名称[%s])", groupRepositoriesInputVo.getAlias());
        }
        checkGroupMemberRepoList(groupRepositoriesInputVo);
        NexusGroupRepoInputVo requestBody = groupRepositoriesInputVo.toNexusGroupRepoInputVo();

        // 调用制品库创建接口创建制品库
        nexusRequestHelper.doRequest(
                NexusRequestUrl.ADD_REPOSITORIES(
                        groupRepositoriesInputVo.getType(),
                        groupRepositoriesInputVo.getFormat()),
                JSONObject.from(requestBody)
        );
        // 调用制品库详情接口获取制品库详情
        NexusHostedRepoInputVo nexusDataVo = getNexusRepo(
                groupRepositoriesInputVo.getType(),
                groupRepositoriesInputVo.getFormat(),
                PmsProductLibraryRepositories.getNexusRepositoriesName(
                        groupRepositoriesInputVo.getEnterpriseIdentifier(),
                        groupRepositoriesInputVo.getName()
                ));
        // 数据库添加该制品库
        PmsProductLibraryRepositories pmsProductLibraryRepositories = groupRepositoriesInputVo.toPmsProductLibraryRepositories();
        pmsProductLibraryRepositories.setUrl(nexusDataVo.getUrl());
        pmsProductLibraryRepositories.setPmsEnterpriseId(pmsEnterprise.getId());
        pmsProductLibraryRepositories.setDeptId(pmsEnterprise.getDeptId());
        pmsProductLibraryRepositories.setCreateTime(DateUtils.getNowDate());
        pmsProductLibraryRepositories.setCreateBy(SecurityUtils.getUsername());
        return pmsProductLibraryRepositoriesMapper.insertPmsProductLibraryRepositories(pmsProductLibraryRepositories);
    }

    private void checkGroupMemberRepoList(GroupRepositoriesOperateVo groupRepositoriesOperateVo) {
        // 校验虚拟仓库成员是否存在
        for (String memberRepoName : groupRepositoriesOperateVo.getGroup().getMemberNames()) {
            PmsProductLibraryRepositories pmsProductLibraryRepositories =
                    selectPmsProductLibraryRepositoriesByName(groupRepositoriesOperateVo.getEnterpriseIdentifier(), memberRepoName);
            if (!pmsProductLibraryRepositories.getFormat().equals(groupRepositoriesOperateVo.getFormat())) {
                throw new ServiceException("请选择相同格式的制品库作为虚拟仓库成员(制品库格式[%s])", groupRepositoriesOperateVo.getFormat());
            }
        }
        if (ProductLibraryRepositoriesFormatEnum.DOCKER.getKey().equals(groupRepositoriesOperateVo.getFormat())) {
            checkDockerRepoParameter(groupRepositoriesOperateVo.getDocker(), groupRepositoriesOperateVo.getId());
        }
    }

    private void checkDockerRepoParameter(NexusRepoDockerVo nexusRepoDockerVo, Long oldRepoId) {
        if (nexusRepoDockerVo == null) {
            throw new ServiceException("新增【%s】格式制品库时，docker参数不能为空", ProductLibraryRepositoriesFormatEnum.DOCKER.getName());
        }
        // 检查映射端口是否已被占用
        PmsProductLibraryRepositories searchRepo = new PmsProductLibraryRepositories();
        searchRepo.setFormat(ProductLibraryRepositoriesFormatEnum.DOCKER.getKey());
        List<PmsProductLibraryRepositories> dockerRepoList =
                pmsProductLibraryRepositoriesMapper.selectPmsProductLibraryRepositoriesList(searchRepo);

        // 获取端口相同的仓库
        PmsProductLibraryRepositories sameDockerRepo = dockerRepoList.stream().filter(repo -> {
            String attributeStr = repo.getAttributes();
            JSONObject attributeJson = JSONObject.parseObject(attributeStr);
            if (attributeJson.containsKey("docker")) {
                NexusRepoDockerVo repoDockerVo = attributeJson.getObject("docker", NexusRepoDockerVo.class);
                return repoDockerVo.getHttpPort() != null &&
                        repoDockerVo.getHttpPort().equals(nexusRepoDockerVo.getHttpPort());
            }
            return false;
        }).findFirst().orElse(null);
        // 判断端口相同的仓库是否为当前仓库（代表更新），若不是则返回错误信息
        if (sameDockerRepo != null && !sameDockerRepo.getId().equals(oldRepoId)) {
            throw new ServiceException("Docker类型制品库的HTTP端口映射已被占用(HTTP端口映射[%d])", nexusRepoDockerVo.getHttpPort());
        }
    }

    /**
     * 新增制品库-仓库
     *
     * @param hostedPmsProductLibraryRepositoriesInputVo 制品库-仓库
     * @return 结果
     */
    @Override
    public int insertHostedPmsProductLibraryRepositories(HostedRepositoriesInputVo hostedPmsProductLibraryRepositoriesInputVo) {
        PmsEnterprise pmsEnterprise = pmsEnterpriseService.selectPmsEnterpriseByIdentifier(hostedPmsProductLibraryRepositoriesInputVo.getEnterpriseIdentifier());
        // 校验制品库标识
        Integer nameCount = pmsProductLibraryRepositoriesMapper.selectPmsProductLibraryRepositoriesByNameCount(
                hostedPmsProductLibraryRepositoriesInputVo.getName(), hostedPmsProductLibraryRepositoriesInputVo.getEnterpriseIdentifier(), hostedPmsProductLibraryRepositoriesInputVo.getFormat());
        if (nameCount > 0) {
            throw new ServiceException("该制品库标识在当前组织下已存在(制品库标识[%s])", hostedPmsProductLibraryRepositoriesInputVo.getName());
        }
        // 校验制品库别名
        PmsProductLibraryRepositories aliasRepo = pmsProductLibraryRepositoriesMapper.selectPmsProductLibraryRepositoriesByAlias(
                hostedPmsProductLibraryRepositoriesInputVo.getAlias(), hostedPmsProductLibraryRepositoriesInputVo.getEnterpriseIdentifier());
        if (aliasRepo != null) {
            throw new ServiceException("该制品库名称在当前组织下已存在(制品库名称[%s])", hostedPmsProductLibraryRepositoriesInputVo.getAlias());
        }
        checkInputParameter(hostedPmsProductLibraryRepositoriesInputVo);
        NexusHostedRepoInputVo requestBody = hostedPmsProductLibraryRepositoriesInputVo.toNexusRepoDataVo();

        // 调用制品库创建接口创建制品库
        nexusRequestHelper.doRequest(
                NexusRequestUrl.ADD_REPOSITORIES(
                        requestBody.getType(),
                        requestBody.getFormat()),
                JSONObject.from(requestBody)
        );
        // 调用Nexus制品库详情接口获取制品库详情
        NexusHostedRepoInputVo nexusDataVo = getNexusRepo(
                hostedPmsProductLibraryRepositoriesInputVo.getType(),
                hostedPmsProductLibraryRepositoriesInputVo.getFormat(),
                PmsProductLibraryRepositories.getNexusRepositoriesName(
                        hostedPmsProductLibraryRepositoriesInputVo.getEnterpriseIdentifier(),
                        hostedPmsProductLibraryRepositoriesInputVo.getName()
                ));
        // 数据库添加该制品库
        PmsProductLibraryRepositories pmsProductLibraryRepositories = hostedPmsProductLibraryRepositoriesInputVo.toPmsProductLibraryRepositories();
        pmsProductLibraryRepositories.setUrl(nexusDataVo.getUrl());
        pmsProductLibraryRepositories.setPmsEnterpriseId(pmsEnterprise.getId());
        pmsProductLibraryRepositories.setDeptId(pmsEnterprise.getDeptId());
        pmsProductLibraryRepositories.setCreateTime(DateUtils.getNowDate());
        pmsProductLibraryRepositories.setCreateBy(SecurityUtils.getUsername());
        return pmsProductLibraryRepositoriesMapper.insertPmsProductLibraryRepositories(pmsProductLibraryRepositories);
    }

    private void checkInputParameter(HostedRepositoriesOperateVo hostedRepositoriesOperateVo) {
        // 校验制品库类型
        ProductLibraryRepositoriesTypeEnum.getByKey(hostedRepositoriesOperateVo.getType());
        ProductLibraryRepositoriesFormatEnum.getNoHiddenByKey(hostedRepositoriesOperateVo.getFormat());
        if (ProductLibraryRepositoriesFormatEnum.MAVEN.getKey().equals(hostedRepositoriesOperateVo.getFormat())) {
            if (hostedRepositoriesOperateVo.getMaven() == null) {
                throw new ServiceException("新增【%s】格式制品库时，maven参数不能为空", ProductLibraryRepositoriesFormatEnum.MAVEN.getName());
            }
            VersionPolicyEnum.getByKey(hostedRepositoriesOperateVo.getMaven().getVersionPolicy());
            LayoutPolicyEnum.getByKey(hostedRepositoriesOperateVo.getMaven().getLayoutPolicy());
        } else if (ProductLibraryRepositoriesFormatEnum.DOCKER.getKey().equals(hostedRepositoriesOperateVo.getFormat())) {
            checkDockerRepoParameter(hostedRepositoriesOperateVo.getDocker(), hostedRepositoriesOperateVo.getId());
        }
    }


    /**
     * 修改制品库-仓库
     *
     * @param hostedRepositoriesOperateVo 制品库-仓库
     * @return 结果
     */
    @Override
    public int updateHostedPmsProductLibraryRepositories(HostedRepositoriesOperateVo hostedRepositoriesOperateVo) {
        if (StringUtils.isBlank(hostedRepositoriesOperateVo.getAlias())) {
            throw new ServiceException("制品库名称不能为空");
        }
        PmsProductLibraryRepositories oldRepo = selectPmsProductLibraryRepositoriesByName(
                hostedRepositoriesOperateVo.getEnterpriseIdentifier(),
                hostedRepositoriesOperateVo.getName());
        checkLockRepositoryStatus(hostedRepositoriesOperateVo.getEnterpriseIdentifier(), oldRepo.getId());
        hostedRepositoriesOperateVo.setFormat(oldRepo.getFormat());
        hostedRepositoriesOperateVo.setId(oldRepo.getId());
        // 校验制品库别名
        PmsProductLibraryRepositories aliasRepo = pmsProductLibraryRepositoriesMapper.selectPmsProductLibraryRepositoriesByAlias(
                hostedRepositoriesOperateVo.getAlias(), hostedRepositoriesOperateVo.getEnterpriseIdentifier());
        if (aliasRepo != null && !aliasRepo.getId().equals(oldRepo.getId())) {
            throw new ServiceException("该制品库名称在当前组织下已存在(制品库名称[%s])", hostedRepositoriesOperateVo.getAlias());
        }
        checkInputParameter(hostedRepositoriesOperateVo);
        // 构建更新对象
        PmsProductLibraryRepositories pmsProductLibraryRepositories = hostedRepositoriesOperateVo.toPmsProductLibraryRepositories();
        pmsProductLibraryRepositories.setId(oldRepo.getId());
        pmsProductLibraryRepositories.setUpdateTime(DateUtils.getNowDate());
        pmsProductLibraryRepositories.setUpdateBy(SecurityUtils.getUsername());
        NexusHostedRepoInputVo requestBody = hostedRepositoriesOperateVo.toNexusRepoDataVo();

        // 调用制品库更新接口更新制品库
        nexusRequestHelper.doRequest(
                NexusRequestUrl.UPDATE_REPOSITORIES(
                        requestBody.getType(),
                        requestBody.getFormat(),
                        requestBody.getName()),
                JSONObject.from(requestBody)
        );

        return pmsProductLibraryRepositoriesMapper.updatePmsProductLibraryRepositories(pmsProductLibraryRepositories);
    }

    @Override
    public int updateGroupPmsProductLibraryRepositories(GroupRepositoriesOperateVo groupRepositoriesOperateVo) {
        PmsProductLibraryRepositories oldRepo = selectPmsProductLibraryRepositoriesByName(
                groupRepositoriesOperateVo.getEnterpriseIdentifier(),
                groupRepositoriesOperateVo.getName());
        checkLockRepositoryStatus(groupRepositoriesOperateVo.getEnterpriseIdentifier(), oldRepo.getId());
        groupRepositoriesOperateVo.setFormat(oldRepo.getFormat());
        groupRepositoriesOperateVo.setId(oldRepo.getId());
        // 校验制品库别名
        PmsProductLibraryRepositories aliasRepo = pmsProductLibraryRepositoriesMapper.selectPmsProductLibraryRepositoriesByAlias(
                groupRepositoriesOperateVo.getAlias(), groupRepositoriesOperateVo.getEnterpriseIdentifier());
        if (aliasRepo != null && !aliasRepo.getId().equals(oldRepo.getId())) {
            throw new ServiceException("该制品库名称在当前组织下已存在(制品库名称[%s])", groupRepositoriesOperateVo.getAlias());
        }
        checkGroupMemberRepoList(groupRepositoriesOperateVo);
        NexusGroupRepoInputVo requestBody = groupRepositoriesOperateVo.toNexusGroupRepoInputVo();
        // 调用制品库更新接口更新制品库
        nexusRequestHelper.doRequest(
                NexusRequestUrl.UPDATE_REPOSITORIES(
                        oldRepo.getType(),
                        oldRepo.getFormat(),
                        requestBody.getName()),
                JSONObject.from(requestBody)
        );
        PmsProductLibraryRepositories pmsProductLibraryRepositories = groupRepositoriesOperateVo.toPmsProductLibraryRepositories();
        pmsProductLibraryRepositories.setId(oldRepo.getId());
        pmsProductLibraryRepositories.setUpdateTime(DateUtils.getNowDate());
        pmsProductLibraryRepositories.setUpdateBy(SecurityUtils.getUsername());
        return pmsProductLibraryRepositoriesMapper.updatePmsProductLibraryRepositories(pmsProductLibraryRepositories);
    }

    @Override
    public GenericsTableDataInfo<PmsProductLibraryRepositoriesSimpleVo> selectPmsProductLibraryRepositoriesPageList(String enterpriseIdentifier, PmsProductLibraryRepositoriesSearchVo searchVo) {
        PmsProductLibraryRepositories pmsProductLibraryRepositoriesSearch = new PmsProductLibraryRepositories();
        if (searchVo != null) {
            pmsProductLibraryRepositoriesSearch.setName(searchVo.getName());
            pmsProductLibraryRepositoriesSearch.setType(searchVo.getType());
            pmsProductLibraryRepositoriesSearch.setFormat(searchVo.getFormat());
        }

        pmsProductLibraryRepositoriesSearch.setPmsEnterpriseIdentifier(enterpriseIdentifier);
        return PageUtils.toPage(
                pmsProductLibraryRepositoriesMapper.selectPmsProductLibraryRepositoriesList(pmsProductLibraryRepositoriesSearch),
                PmsProductLibraryRepositories::toPmsProductLibraryRepositoriesSimpleVo);
    }

    @Override
    public List<CommonTypeVo> selectVersionPolicyList() {
        return VersionPolicyEnum.getAllToList();
    }

    @Override
    public List<CommonTypeVo> selectLayoutPolicyList() {
        return LayoutPolicyEnum.getAllToList();
    }

    @Override
    public List<CommonTypeVo> selectFormatList() {
        return ProductLibraryRepositoriesFormatEnum.getNoHiddenToList();
    }

    @Override
    public List<CommonTypeVo> selectTypeList() {
        return ProductLibraryRepositoriesTypeEnum.getAllToList();
    }


    @Override
    public PmsProductLibraryRepositoriesDataVo selectPmsProductLibraryRepositoriesDataByName(String enterpriseIdentifier, String repoName) {
        PmsProductLibraryRepositories pmsProductLibraryRepositories =
                selectPmsProductLibraryRepositoriesByName(enterpriseIdentifier, repoName);
        return pmsProductLibraryRepositories.toPmsProductLibraryRepositoriesDataVo();
    }

    @Override
    public int deletePmsProductLibraryRepositoriesByName(String enterpriseIdentifier, String name) {
        PmsProductLibraryRepositories oldRepo = selectPmsProductLibraryRepositoriesByName(enterpriseIdentifier, name);
        checkLockRepositoryStatus(enterpriseIdentifier, oldRepo.getId());
        // 调用制品库删除接口删除制品库
        nexusRequestHelper.doRequest(
                NexusRequestUrl.DELETE_REPOSITORIES(oldRepo.getNexusRepositoriesName())
        );
        return pmsProductLibraryRepositoriesMapper.deletePmsProductLibraryRepositoriesById(oldRepo.getId());
    }

    @Override
    public void lockRepository(String enterpriseIdentifier, Long repoId) {
        String lockRepoKey = CacheConstants.getLockEnterpriseIdentifierRepoIdKey(enterpriseIdentifier, repoId);
        if (redisService.hasKey(lockRepoKey)) {
            int count = redisService.getCacheObject(lockRepoKey);
            redisService.setCacheObject(lockRepoKey, ++count);
        } else {
            redisService.setCacheObject(lockRepoKey, 1);
        }
    }

    @Override
    public void unlockRepository(String enterpriseIdentifier, Long repoId) {
        String lockRepoKey = CacheConstants.getLockEnterpriseIdentifierRepoIdKey(enterpriseIdentifier, repoId);
        if (redisService.hasKey(lockRepoKey)) {
            int count = redisService.getCacheObject(lockRepoKey);
            count--;
            if (count <= 0) {
                redisService.deleteObject(lockRepoKey);
            } else {
                redisService.setCacheObject(lockRepoKey, count);
            }
        }
    }

    @Override
    public Boolean getLockRepositoryStatus(String enterpriseIdentifier, Long repoId) {
        String lockRepoKey = CacheConstants.getLockEnterpriseIdentifierRepoIdKey(enterpriseIdentifier, repoId);
        if (redisService.hasKey(lockRepoKey)) {
            int count = redisService.getCacheObject(lockRepoKey);
            return count > 0;
        }
        return false;
    }

    @Override
    public void checkLockRepositoryStatus(String enterpriseIdentifier, Long repoId) {
        if (getLockRepositoryStatus(enterpriseIdentifier, repoId)) {
            throw new ServiceException("该制品库锁定中，无法进行操作(制品库Id[%s])", repoId);
        }
    }

    @Override
    public PmsProductLibraryRepositories getPmsProductLibraryRepositories(Boolean isProduct, String enterpriseIdentifier, String repoName, String snapshotName) {
        PmsProductLibraryRepositories pmsProductLibraryRepositories;
        if (isProduct) {
            pmsProductLibraryRepositories =
                    pmsProductLibraryService.selectPmsProductLibraryByName(enterpriseIdentifier, repoName);
        } else {
            if (StringUtils.isNotEmpty(snapshotName)) {
                pmsProductLibraryRepositories =
                        pmsProductLibrarySnapshotRepoService.selectPmsProductLibrarySnapshotByName(enterpriseIdentifier, repoName, snapshotName);
            } else {
                pmsProductLibraryRepositories =
                        selectPmsProductLibraryRepositoriesByName(enterpriseIdentifier, repoName);
            }
        }
        return pmsProductLibraryRepositories;
    }
}
