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

import com.microservices.common.core.utils.StringUtils;
import com.microservices.common.core.utils.html.EscapeUtil;
import com.microservices.pms.productLibrary.domain.PmsProductLibraryComponent;
import com.microservices.pms.productLibrary.domain.PmsProductLibraryRepositories;
import com.microservices.pms.productLibrary.domain.ProductLibraryRepositoriesFormatEnum;
import com.microservices.pms.productLibrary.domain.vo.*;
import com.microservices.pms.productLibrary.service.IPmsProductLibraryAsyncService;
import com.microservices.pms.productLibrary.service.IPmsProductLibraryComponentService;
import com.microservices.pms.productLibrary.service.IPmsProductLibraryRepositoriesService;
import com.microservices.pms.utils.CustomExecutorFactory;
import com.microservices.pms.utils.NexusRequestHelper;
import com.microservices.pms.utils.NexusRequestUrl;
import com.microservices.system.api.domain.SysFileBytes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

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

/**
 * @author OTTO
 */
@Service
public class PmsProductLibraryAsyncServiceImpl implements IPmsProductLibraryAsyncService {

    private static final Logger log = LoggerFactory.getLogger(PmsProductLibraryAsyncServiceImpl.class);
    @Autowired
    private NexusRequestHelper nexusRequestHelper;
    @Autowired
    @Lazy
    private IPmsProductLibraryComponentService pmsProductLibraryComponentService;
    @Autowired
    @Lazy
    private IPmsProductLibraryRepositoriesService pmsProductLibraryRepositoriesService;

    @Override
    public void syncNexusMavenRepoComponent(PmsProductLibraryRepositories repo, List<PmsProductLibraryComponent> pmsProductLibraryComponents) {
        CustomExecutorFactory.threadPoolExecutor.execute(() -> {
            // 遍历制品库制品文件列表，根据数据库制品文件记录获取对应的制品标识
            boolean isContinuationGet = true;
            NexusComponentAndAssetListVo<NexusAssetVo> assetListVo = new NexusComponentAndAssetListVo<>();
            while (isContinuationGet) {
                assetListVo = nexusRequestHelper.doRequestToJavaObject(
                        NexusRequestUrl.GET_ASSETS(repo.getNexusRepositoriesName(), assetListVo.getContinuationToken()),
                        NexusComponentAndAssetListVo.class,
                        NexusAssetVo.class);
                List<NexusAssetVo> assetList = assetListVo.getItems();
                if (pmsProductLibraryComponents.stream().allMatch(x -> StringUtils.isNotEmpty(x.getIdentifier()))) {
                    break;
                }
                if (StringUtils.isEmpty(assetListVo.getContinuationToken())) {
                    isContinuationGet = false;
                }
                pmsProductLibraryComponents.forEach(component -> {
                    if (StringUtils.isEmpty(component.getIdentifier())) {
                        NexusAssetVo matchAssetVo = assetList.stream().filter(x -> x.getPath().equals(component.getPath())).findFirst().orElse(null);
                        if (matchAssetVo != null) {
                            component.setNexusMavenAssetVo(matchAssetVo);
                            pmsProductLibraryComponentService.updatePmsProductLibraryComponent(component);
                        }
                    }
                });
            }
        });
    }

    @Override
    public void asyncAddCommonAssetToProductRepo(PmsProductLibraryRepositories productRepo, PmsProductLibraryRepositories repo, List<NexusAssetResultDataVo> nexusAssetResultDataVoList) {
        CustomExecutorFactory.threadPoolExecutor.execute(() -> {
            pmsProductLibraryRepositoriesService.lockRepository(repo.getPmsEnterpriseIdentifier(), repo.getId());
            try {
                for (NexusAssetResultDataVo nexusAssetResultDataVo : nexusAssetResultDataVoList) {
                    if (StringUtils.isEmpty(nexusAssetResultDataVo.getVersion())) {
                        nexusAssetResultDataVo.setVersion("");
                    }
                    if (StringUtils.isNotEmpty(nexusAssetResultDataVo.getFileIdentifier())) {
                        List<AssetRawInputVo> assetRawInputVoList = new ArrayList<>();
                        AssetRawInputVo assetRawInputVo = new AssetRawInputVo();
                        assetRawInputVo.setFilename(nexusAssetResultDataVo.getFileName());
                        assetRawInputVo.setFileIdentifier(nexusAssetResultDataVo.getFileIdentifier());
                        assetRawInputVoList.add(assetRawInputVo);
                        insertRawComponentToProductRepo(productRepo, repo, nexusAssetResultDataVo, assetRawInputVoList);
                    } else {
                        List<AssetRawInputVo> assetRawInputVoList = new ArrayList<>();
                        AssetRawInputVo assetRawInputVo = new AssetRawInputVo();
                        assetRawInputVo.setFilename(nexusAssetResultDataVo.getFileName());
                        assetRawInputVo.setFileIdentifier("");
                        String nexusFileUrl =
                                EscapeUtil.removeExtraSlashOfUrl(repo.getUrl() + "/" + nexusAssetResultDataVo.getName());
                        SysFileBytes fileBytes = nexusRequestHelper.getFileBytes(nexusFileUrl);
                        assetRawInputVo.setFileBytes(fileBytes);
                        assetRawInputVoList.add(assetRawInputVo);
                        insertRawComponentToProductRepo(productRepo, repo, nexusAssetResultDataVo, assetRawInputVoList);
                    }
                }
            } finally {
                pmsProductLibraryRepositoriesService.unlockRepository(repo.getPmsEnterpriseIdentifier(), repo.getId());
            }
        });
    }

    @Override
    public void syncRepoToSnapshotRepo(PmsProductLibraryRepositories repo, PmsProductLibraryRepositories snapshotRepo) {
        CustomExecutorFactory.threadPoolExecutor.execute(() -> {
            try {
                pmsProductLibraryRepositoriesService.lockRepository(repo.getPmsEnterpriseIdentifier(), repo.getId());
                ProductLibraryRepositoriesFormatEnum formatEnum = ProductLibraryRepositoriesFormatEnum.getByKey(repo.getFormat());
                switch (formatEnum) {
                    case MAVEN: {
                        syncMavenRepoToSnapshotRepo(repo, snapshotRepo);
                        break;
                    }
                    case NPM: {
                        syncNpmRepoToSnapshotRepo(repo, snapshotRepo);
                        break;
                    }
                    case PYPI: {
                        syncPypiRepoToSnapshotRepo(repo, snapshotRepo);
                        break;
                    }
                    case RAW:
                    case GO:
                    case CONDA:
                    case XXXXX: {
                        syncRawRepoToSnapshotRepo(repo, snapshotRepo);
                        break;
                    }
                }
            } catch (Exception e) {
                log.error("同步制品库制品到快照库时失败(制品库id[{}],快照库id[{}]):{}", repo.getId(), snapshotRepo.getId(), e.getMessage());
            } finally {
                pmsProductLibraryRepositoriesService.unlockRepository(repo.getPmsEnterpriseIdentifier(), repo.getId());
            }

        });
    }

    private void syncMavenRepoToSnapshotRepo(PmsProductLibraryRepositories repo, PmsProductLibraryRepositories snapshotRepo) {
        List<PmsProductLibraryComponent> pmsProductLibraryComponentList =
                pmsProductLibraryComponentService.selectPmsProductLibraryComponentListByDatabase(repo.getId());
        for (PmsProductLibraryComponent pmsProductLibraryComponent : pmsProductLibraryComponentList) {
            ComponentMavenInputVo componentMavenInputVo = pmsProductLibraryComponent.toComponentMavenInputVo(snapshotRepo);
            List<AssetMavenInputVo> assetMavenInputVoList = getAssetMavenInputVoList(repo, pmsProductLibraryComponent, componentMavenInputVo);
            if (assetMavenInputVoList != null) {
                componentMavenInputVo.setAssetList(assetMavenInputVoList);
                pmsProductLibraryComponentService.insertMavenComponent(componentMavenInputVo);
            }
        }
    }

    private void syncNpmRepoToSnapshotRepo(PmsProductLibraryRepositories repo, PmsProductLibraryRepositories snapshotRepo) {
        List<NexusAssetVo> assetList = getAllAssetByNexus(repo);
        for (NexusAssetVo asset : assetList) {
            if (asset.getNpm() == null) {
                continue;
            }
            ComponentInputVo componentInputVo = new ComponentInputVo();
            componentInputVo.setRepoName(snapshotRepo.getName());
            componentInputVo.setSnapshotName(snapshotRepo.getSnapshotName());
            componentInputVo.setEnterpriseIdentifier(snapshotRepo.getPmsEnterpriseIdentifier());
            componentInputVo.setFileIdentifier("");
            String nexusFileUrl =
                    EscapeUtil.removeExtraSlashOfUrl(repo.getUrl() + "/" + asset.getPath());
            SysFileBytes fileBytes = nexusRequestHelper.getFileBytes(nexusFileUrl);
            if (fileBytes == null) {
                continue;
            }
            componentInputVo.setFileBytes(fileBytes);
            pmsProductLibraryComponentService.insertNpmComponent(componentInputVo);
        }
    }

    private void syncPypiRepoToSnapshotRepo(PmsProductLibraryRepositories repo, PmsProductLibraryRepositories snapshotRepo) {
        List<NexusAssetVo> assetList = getAllAssetByNexus(repo);
        for (NexusAssetVo asset : assetList) {
            if (asset.getPypi() == null) {
                continue;
            }
            ComponentInputVo componentInputVo = new ComponentInputVo();
            componentInputVo.setRepoName(snapshotRepo.getName());
            componentInputVo.setSnapshotName(snapshotRepo.getSnapshotName());
            componentInputVo.setEnterpriseIdentifier(snapshotRepo.getPmsEnterpriseIdentifier());
            componentInputVo.setFileIdentifier("");
            String nexusFileUrl =
                    EscapeUtil.removeExtraSlashOfUrl(repo.getUrl() + "/" + asset.getPath());
            SysFileBytes fileBytes = nexusRequestHelper.getFileBytes(nexusFileUrl);
            if (fileBytes == null) {
                continue;
            }
            componentInputVo.setFileBytes(fileBytes);
            pmsProductLibraryComponentService.insertPypiComponent(componentInputVo);
        }
    }

    private void syncRawRepoToSnapshotRepo(PmsProductLibraryRepositories repo, PmsProductLibraryRepositories snapshotRepo) {
        List<PmsProductLibraryComponent> pmsProductLibraryComponentList =
                pmsProductLibraryComponentService.selectPmsProductLibraryComponentListByDatabase(repo.getId());
        for (PmsProductLibraryComponent pmsProductLibraryComponent : pmsProductLibraryComponentList) {
            ComponentRawInputVo componentRawInputVo = pmsProductLibraryComponent.toComponentRawInputVo(snapshotRepo);
            List<AssetRawInputVo> assetRawInputVoList = getAssetRawInputVoList(repo, pmsProductLibraryComponent, componentRawInputVo);
            if (assetRawInputVoList != null) {
                componentRawInputVo.setAssetList(assetRawInputVoList);
                pmsProductLibraryComponentService.insertRawComponent(componentRawInputVo);
            }
        }
    }

    private List<AssetRawInputVo> getAssetRawInputVoList(PmsProductLibraryRepositories repo, PmsProductLibraryComponent pmsProductLibraryComponent, ComponentRawInputVo componentRawInputVo) {
        List<AssetRawInputVo> assetRawInputVoList = componentRawInputVo.getAssetList();
        for (AssetRawInputVo assetRawInputVo : assetRawInputVoList) {
            if (StringUtils.isEmpty(assetRawInputVo.getFileIdentifier())) {
                assetRawInputVo.setFileIdentifier("");
                String nexusFileUrl =
                        EscapeUtil.removeExtraSlashOfUrl(repo.getUrl() + "/" + pmsProductLibraryComponent.getPath());
                SysFileBytes fileBytes = nexusRequestHelper.getFileBytes(nexusFileUrl);
                if (fileBytes == null) {
                    return null;
                }
                assetRawInputVo.setFileBytes(fileBytes);
            }
        }
        return assetRawInputVoList;
    }

    private List<AssetMavenInputVo> getAssetMavenInputVoList(PmsProductLibraryRepositories repo, PmsProductLibraryComponent pmsProductLibraryComponent, ComponentMavenInputVo componentMavenInputVo) {
        List<AssetMavenInputVo> assetMavenInputVoList = componentMavenInputVo.getAssetList();
        for (AssetMavenInputVo assetMavenInputVo : assetMavenInputVoList) {
            if (StringUtils.isEmpty(assetMavenInputVo.getFileIdentifier())) {
                assetMavenInputVo.setFileIdentifier("");
                String nexusFileUrl =
                        EscapeUtil.removeExtraSlashOfUrl(repo.getUrl() + "/" + pmsProductLibraryComponent.getPath());
                SysFileBytes fileBytes = nexusRequestHelper.getFileBytes(nexusFileUrl);
                if (fileBytes == null) {
                    return null;
                }
                assetMavenInputVo.setFileBytes(fileBytes);
            }
        }
        return assetMavenInputVoList;
    }

    private void insertRawComponentToProductRepo(PmsProductLibraryRepositories productRepo,
                                                 PmsProductLibraryRepositories repo,
                                                 NexusAssetResultDataVo nexusAssetResultDataVo,
                                                 List<AssetRawInputVo> assetRawInputVoList) {
        ComponentRawInputVo componentRawInputVo = new ComponentRawInputVo();
        componentRawInputVo.setRepoName(productRepo.getName());
        componentRawInputVo.setEnterpriseIdentifier(productRepo.getPmsEnterpriseIdentifier());
        componentRawInputVo.setFormat(ProductLibraryRepositoriesFormatEnum.PRODUCT.getKey());
        componentRawInputVo.setVersion(nexusAssetResultDataVo.getVersion());
        if (nexusAssetResultDataVo.getAttributes() != null
                && nexusAssetResultDataVo.getAttributes().containsKey("customAttribute")) {
            componentRawInputVo.setAttributes(nexusAssetResultDataVo.getAttributes().getJSONObject("customAttribute").toJSONString());
        }
        componentRawInputVo.setAssetList(assetRawInputVoList);
        componentRawInputVo.setDirectory(String.format("/%s/%s", repo.getAlias(), nexusAssetResultDataVo.getDirectory()));
        pmsProductLibraryComponentService.insertRawComponent(componentRawInputVo);
    }

    private List<NexusAssetVo> getAllAssetByNexus(PmsProductLibraryRepositories repo) {
        List<NexusAssetVo> assetList = new ArrayList<>();
        // 遍历制品库制品文件列表，根据数据库制品文件记录获取对应的制品标识
        boolean isContinuationGet = true;
        NexusComponentAndAssetListVo<NexusAssetVo> assetListVo = new NexusComponentAndAssetListVo<>();
        while (isContinuationGet) {
            assetListVo = nexusRequestHelper.doRequestToJavaObject(
                    NexusRequestUrl.GET_ASSETS(repo.getNexusRepositoriesName(), assetListVo.getContinuationToken()),
                    NexusComponentAndAssetListVo.class,
                    NexusAssetVo.class);
            assetList.addAll(assetListVo.getItems());
            if (StringUtils.isEmpty(assetListVo.getContinuationToken())) {
                isContinuationGet = false;
            }
        }
        return assetList;
    }
}
