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

import com.alibaba.fastjson2.JSONObject;
import com.microservices.common.core.constant.HttpStatus;
import com.microservices.common.core.exception.ServiceException;
import com.microservices.common.core.utils.DateUtils;
import com.microservices.common.core.utils.StringUtils;
import com.microservices.common.core.web.page.Breadcrumb;
import com.microservices.common.core.web.page.GenericsTableDataInfo;
import com.microservices.common.httpClient.util.GitLinkRequestHelper;
import com.microservices.common.security.utils.SecurityUtils;
import com.microservices.pms.common.service.IPmsCommonService;
import com.microservices.pms.document.domain.PmsDocument;
import com.microservices.pms.document.domain.vo.*;
import com.microservices.pms.document.mapper.PmsDocumentMapper;
import com.microservices.pms.document.mapper.PmsDocumentUserFavoriteMapper;
import com.microservices.pms.document.service.IPmsDocumentService;
import com.microservices.pms.enterprise.domain.PmsEnterprise;
import com.microservices.pms.enterprise.service.IPmsEnterpriseService;
import com.microservices.pms.enums.PmsDocumentType;
import com.microservices.pms.project.mapper.PmsProjectRepositoryMapper;
import com.microservices.pms.project.service.IPmsProjectService;
import com.microservices.pms.utils.PmsGitLinkRequestUrl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.net.URISyntaxException;
import java.util.*;
import java.util.stream.Collectors;

import static com.microservices.common.core.utils.PageUtils.startPage;
import static com.microservices.common.core.utils.PageUtils.toPage;
import static com.microservices.common.httpClient.constant.GitLinkConstants.DATA_KEY;

/**
 * 项目管理-文档库Service业务层处理
 * 
 * @author wanjia
 * @date 2024-01-08
 */
@Service
public class PmsDocumentServiceImpl implements IPmsDocumentService 
{
    private static final Logger logger = LoggerFactory.getLogger(PmsDocumentServiceImpl.class);
    @Resource
    private PmsDocumentMapper pmsDocumentMapper;

    @Autowired
    private IPmsCommonService pmsCommonService;

    @Autowired
    private GitLinkRequestHelper gitLinkRequestHelper;

    @Resource
    private PmsProjectRepositoryMapper pmsProjectRepositoryMapper;

    @Autowired
    private IPmsEnterpriseService pmsEnterpriseService;

    @Resource
    private PmsDocumentUserFavoriteMapper pmsDocumentUserFavoriteMapper;

    @Autowired
    private IPmsProjectService pmsProjectService;

    /**
     * 查询项目管理-文档库
     * 
     * @param id 项目管理-文档库主键
     * @return 项目管理-文档库
     */
    @Override
    public PmsDocument selectPmsDocumentByIdAndEnterpriseId(Long id, Long enterpriseId)
    {
        boolean isEnterpriseAdmin = pmsCommonService.hasDocsManageRole(enterpriseId);
        return pmsDocumentMapper.selectPmsDocumentByIdAndEnterpriseId(id, enterpriseId, isEnterpriseAdmin, SecurityUtils.getUsername());
    }

    @Override
    public PmsDocumentDetailVo selectPmsDocumentDetailVoByIdAndEnterpriseId(Long id, Long enterpriseId) {
        PmsDocument pmsDocument = this.selectPmsDocumentByIdAndEnterpriseId(id, enterpriseId);
        if (pmsDocument == null) {
            throw new ServiceException("组织知识库下不存在该文档(文档标识[%s])", id);
        }
        PmsDocumentDetailVo pmsDocumentDetailVo = new PmsDocumentDetailVo();
        BeanUtils.copyProperties(pmsDocument, pmsDocumentDetailVo);
        pmsDocumentDetailVo.setCreator(pmsCommonService.getSimpleSysUserByUsername(pmsDocument.getCreateBy()));
        JSONObject wikiRepoDetails = getWikiRepositoryDetailsByEnterpriseIdAndRepoId(pmsDocumentDetailVo.getWikiGitlinkRepoId());
        pmsDocumentDetailVo.setWikiGitlinkRepoDetails(wikiRepoDetails);
        pmsDocumentDetailVo.setIsFavour(pmsDocumentUserFavoriteMapper.isDocumentInFavorites(SecurityUtils.getUserId(), pmsDocument.getId()));
        return pmsDocumentDetailVo;
    }

    /**
     * 查询项目管理-文档库列表
     * 
     * @param pmsDocument 项目管理-文档库
     * @return 项目管理-文档库
     */
    @Override
    public List<PmsDocument> selectPmsDocumentList(PmsDocument pmsDocument)
    {
        checkAuthAndReturnAdminStatus(pmsDocument.getPmsEnterpriseId());
        return pmsDocumentMapper.selectPmsDocumentList(pmsDocument);
    }

    @Override
    public GenericsTableDataInfo<PmsDocumentVo> selectPmsDocumentVoList(PmsDocumentSearchVo pmsDocumentSearchVo) {
        String currentUserName = SecurityUtils.getUsername();
        boolean isEnterpriseAdmin = checkAuthAndReturnAdminStatus(pmsDocumentSearchVo.getPmsEnterpriseId());
        pmsDocumentSearchVo.setEnterpriseAdmin(isEnterpriseAdmin);
        pmsDocumentSearchVo.setCurrentUserName(currentUserName);
        startPage();
        List<PmsDocument> pmsDocumentList = pmsDocumentMapper.selectPmsDocumentListBySearchInput(pmsDocumentSearchVo);
        GenericsTableDataInfo<PmsDocumentVo> page = toPage(pmsDocumentList,this::buildPmsDocumentVo);
        //构建面包屑列表
        if (pmsDocumentSearchVo.getParentId() != 0) {
            List<Breadcrumb> breadcrumbs = buildBreadcrumbs(pmsDocumentSearchVo.getParentId());
            page.setBreadcrumb(breadcrumbs);
        }
        return page;
    }

    @Override
    public JSONObject getOptionalWikiRepoList(OptionalWikiReposSearchVo optionalWikiReposSearchVo) {
        Long enterpriseId = optionalWikiReposSearchVo.getEnterpriseId();
        checkAuthAndReturnAdminStatus(enterpriseId);
        PmsEnterprise pmsEnterprise = pmsEnterpriseService.selectPmsEnterpriseById(enterpriseId);
        Map<String, String> queryParams = buildRepoListQueryParams(optionalWikiReposSearchVo);
        try {
            return  gitLinkRequestHelper.doGet(PmsGitLinkRequestUrl.GET_ORGANIZATION_REPO_LIST(pmsEnterprise.getEnterpriseIdentifier(), queryParams));
        } catch (URISyntaxException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 新增项目管理-文档库
     * 
     * @param pmsDocument 项目管理-文档库
     * @return 结果
     */
    @Override
    public int insertPmsDocument(PmsDocument pmsDocument)
    {
        checkPmsProjectById(pmsDocument.getPmsProjectId());
        checkAuthAndReturnAdminStatus(pmsDocument.getPmsEnterpriseId());
        String currentUserName = SecurityUtils.getUsername();
        checkForSameDocumentExistence(pmsDocument.getName(), pmsDocument.getDocType(), pmsDocument.getParentId(), pmsDocument.getPmsProjectId());
        pmsDocument.setCreateTime(DateUtils.getNowDate());
        pmsDocument.setCreateBy(currentUserName);
        return pmsDocumentMapper.insertPmsDocument(pmsDocument);
    }

    @Override
    public int insertPmsFileTypeDocument(PmsFileTypeDocumentInputVo pmsFileTypeDocumentInputVo) {
        if (pmsFileTypeDocumentInputVo.getDocType() != PmsDocumentType.ATTACHMENT.getKey()) {
            throw new ServiceException("参数docType错误");
        }
        checkPmsProjectById(pmsFileTypeDocumentInputVo.getPmsProjectId());
        String currentUserName = SecurityUtils.getUsername();
        checkAuthAndReturnAdminStatus(pmsFileTypeDocumentInputVo.getPmsEnterpriseId());
        return batchInsertPmsDocumentByInputVo(pmsFileTypeDocumentInputVo, currentUserName);
    }

    /**
     * 修改项目管理-文档库
     * 
     * @param pmsDocument 项目管理-文档库
     * @return 结果
     */
    @Override
    public int updatePmsDocument(PmsDocument pmsDocument)
    {
        checkPmsProjectById(pmsDocument.getPmsProjectId());
        String currentUserName = SecurityUtils.getUsername();
        boolean isEnterpriseAdmin = checkAuthAndReturnAdminStatus(pmsDocument.getPmsEnterpriseId());
        pmsDocument.setUpdateTime(DateUtils.getNowDate());
        pmsDocument.setUpdateBy(currentUserName);
        if (pmsDocumentMapper.updatePmsDocument(pmsDocument, isEnterpriseAdmin, currentUserName) == 0) {
            throw new ServiceException("更新失败，请检查参数或权限");
        }
        return 1;
    }

    /**
     * 批量删除项目管理-文档库
     *
     * @param ids          需要删除的项目管理-文档库主键
     * @param enterpriseId 企业id
     * @return 结果
     */
    @Override
    public int deletePmsDocumentByIds(Long[] ids, Long enterpriseId, Long projectId)
    {
        checkPmsProjectById(projectId);
        String currentUserName = SecurityUtils.getUsername();
        boolean isEnterpriseAdmin = checkAuthAndReturnAdminStatus(enterpriseId);
        //存在子文档，不允许删除
        checkForChildDocumentsExistence(ids);
        return pmsDocumentMapper.deletePmsDocumentByIds(ids, isEnterpriseAdmin, currentUserName);
    }

    /**
     * 删除项目管理-文档库信息
     * 
     * @param id 项目管理-文档库主键
     * @return 结果
     */
    @Override
    public int deletePmsDocumentById(Long id, Long enterpriseId, Long projectId)
    {
        checkPmsProjectById(projectId);
        String currentUserName = SecurityUtils.getUsername();
        boolean isEnterpriseAdmin = checkAuthAndReturnAdminStatus(enterpriseId);
        return pmsDocumentMapper.deletePmsDocumentById(id, isEnterpriseAdmin, currentUserName);
    }

    public PmsDocumentVo buildPmsDocumentVo(PmsDocument pmsDocument) {
        PmsDocumentVo pmsDocumentVo = new PmsDocumentVo();
        BeanUtils.copyProperties(pmsDocument, pmsDocumentVo);
        pmsDocumentVo.setCreator(pmsCommonService.getSimpleSysUserByUsername(pmsDocument.getCreateBy()));
        SimpleWikiRepositoryVo wikiRepositoryInfo = getWikiRepositorySimpleInfoByRepoId(pmsDocumentVo.getId(), pmsDocumentVo.getWikiGitlinkRepoId());
        //wiki库主仓id不为空，仓库数据查询结果为空（主仓不存在)
        if (pmsDocumentVo.getWikiGitlinkRepoId() != null && wikiRepositoryInfo == null) {
            return pmsDocumentVo;
        }
        pmsDocumentVo.setWikiRepositorySimpleInfo(wikiRepositoryInfo);
        pmsDocumentVo.setIsFavour(pmsDocumentUserFavoriteMapper.isDocumentInFavorites(SecurityUtils.getUserId(), pmsDocument.getId()));
        return pmsDocumentVo;
    }

    private List<Breadcrumb> buildBreadcrumbs(Long parentId) {
        List<Breadcrumb> breadcrumbs = new ArrayList<>();
        Map<Long, PmsDocument> documentMap = new HashMap<>();
        while (parentId != 0) {
            PmsDocument pmsDocument = documentMap.get(parentId);
            if (pmsDocument == null) {
                pmsDocument = pmsDocumentMapper.selectPmsDocumentById(parentId);
                if (pmsDocument != null) {
                    documentMap.put(parentId, pmsDocument);
                } else {
                    throw new ServiceException("数据异常,文件夹不存在[id:%s]", parentId);
                }
            } else {
                logger.error("文件夹id:" + parentId + "已被其子目录关联，文件层级存在环结构");
                throw new ServiceException("数据异常");
            }
            Breadcrumb breadcrumb = new Breadcrumb();
            breadcrumb.setId(pmsDocument.getId());
            breadcrumb.setType(pmsDocument.getDocType());
            breadcrumb.setName(pmsDocument.getName());
            breadcrumbs.add(breadcrumb);
            parentId = pmsDocument.getParentId();
        }
        Collections.reverse(breadcrumbs);
        return breadcrumbs;
    }

    private JSONObject getWikiRepositoryDetailsByEnterpriseIdAndRepoId(Long wikiGitlinkRepoId) {
        if (wikiGitlinkRepoId != null) {
            JSONObject repoInfo = gitLinkRequestHelper.doGet(PmsGitLinkRequestUrl.GET_OWNER_AND_REPO_BY_REPO_ID(wikiGitlinkRepoId));
            JSONObject repoData = repoInfo.getJSONObject(DATA_KEY);
            if (repoData != null) {
                return gitLinkRequestHelper.doGet(PmsGitLinkRequestUrl.GET_REPO_DETAILS(repoData.getString("owner"), repoData.getString("identifier")));
            }
        }
        return null;
    }

    private SimpleWikiRepositoryVo getWikiRepositorySimpleInfoByRepoId(Long docId, Long wikiGitlinkRepoId) {
        if (wikiGitlinkRepoId != null) {
            JSONObject repoInfo = new JSONObject();
            try {
                repoInfo = gitLinkRequestHelper.doGet(PmsGitLinkRequestUrl.GET_OWNER_AND_REPO_BY_REPO_ID(wikiGitlinkRepoId));
            } catch (ServiceException e) {
                if (e.getCode() != null && e.getCode() == HttpStatus.NOT_FOUND) {
                    logger.info("id:" + docId + "关联的wiki仓库主仓已被删除");
                    pmsDocumentMapper.deletePmsDocumentByIdWithoutAuthCheck(docId);
                    return null;
                }
            }
            JSONObject repoData = repoInfo.getJSONObject(DATA_KEY);
            if (repoData != null) {
                SimpleWikiRepositoryVo wikiRepositoryInfo = new SimpleWikiRepositoryVo();
                wikiRepositoryInfo.setRepoIdentifier(repoData.getString("identifier"));
                wikiRepositoryInfo.setRepoOwner(repoData.getString("owner"));
                wikiRepositoryInfo.setName(repoData.getString("name"));
                return wikiRepositoryInfo;
            }
        }
        return null;
    }

    private int batchInsertPmsDocumentByInputVo(PmsFileTypeDocumentInputVo pmsFileTypeDocumentInputVo, String currentUserName) {
        String[] names = pmsFileTypeDocumentInputVo.getNames();
        String[] attachmentIdentifiers = pmsFileTypeDocumentInputVo.getAttachmentIdentifiers();
        if (names.length != attachmentIdentifiers.length) {
            throw new ServiceException("文件类型的文档名称个数与文件标识个数不匹配");
        }
        List<PmsDocument> pmsDocumentList = new ArrayList<>();
        for (int i=0;  i < names.length; i++) {
            PmsDocument pmsDocument = new PmsDocument();
            BeanUtils.copyProperties(pmsFileTypeDocumentInputVo, pmsDocument);
            pmsDocument.setName(names[i]);
            pmsDocument.setAttachmentIdentifier(attachmentIdentifiers[i]);
            pmsDocument.setCreateTime(DateUtils.getNowDate());
            pmsDocument.setUpdateTime(DateUtils.getNowDate());
            pmsDocument.setCreateBy(currentUserName);
            checkForSameDocumentExistence(pmsDocument.getName(), pmsDocument.getDocType(), pmsDocument.getParentId(), pmsDocument.getPmsProjectId());
            pmsDocumentList.add(pmsDocument);
        }
        return pmsDocumentMapper.batchInsertPmsDocument(pmsDocumentList);
    }

    private void checkForChildDocumentsExistence(Long[] ids) {
        int count = pmsDocumentMapper.countChildDocByIds(ids);
        if (count > 0) {
            throw new ServiceException("文件夹内存在子文档，不允许删除");
        }
    }

    private void checkForSameDocumentExistence(String name, Integer docType, Long parentId, Long pmsProjectId) {
        if (pmsDocumentMapper.countDocumentByNameAndTypeAndParenId(name, docType, parentId, pmsProjectId) > 0) {
            throw new ServiceException("当前目录已存在同名文档：[%s]", name);
        }
    }

    /**
     * 校验用户在企业和项目中的权限，并返回是否为企业管理员
     *
     * @param enterpriseId 企业id
     * @return boolean true-传入企业id下管理员 false-非传入企业id下管理员
     */
    private boolean checkAuthAndReturnAdminStatus(Long enterpriseId) {
        return pmsCommonService.hasDocsManageRole(enterpriseId);
    }

    /**
     * 构建组织下仓库列表接口的请求参数
     *
     * @param optionalWikiReposSearchVo 可选则的wiki仓库搜索对象
     * @return query map
     */
    private Map<String, String> buildRepoListQueryParams(OptionalWikiReposSearchVo optionalWikiReposSearchVo) {
        Long[] linkedRepoIds = pmsDocumentMapper.selectLinkedRepoIds(optionalWikiReposSearchVo.getEnterpriseId(), optionalWikiReposSearchVo.getProjectId());
        Map<String, String> queryParams = new HashMap<>();
        //企业下
        if (optionalWikiReposSearchVo.getProjectId() == 0) {
            String linkedRepoIdsString = StringUtils.join(linkedRepoIds, ",");
            //组织仓库中排除已关联的
            queryParams.put("exclude_ids", linkedRepoIdsString);
        }
        //pms项目下
        if (optionalWikiReposSearchVo.getProjectId() != 0) {
            List<Integer> repositoryIds = pmsProjectRepositoryMapper.selectPmsProjectRepoIdsByProjectId(optionalWikiReposSearchVo.getProjectId());
            String unlinkedRepoIdsString;
            //pms项目未添加仓库，组织下仓库id查询参数置为0
            if (repositoryIds == null || repositoryIds.isEmpty()) {
                unlinkedRepoIdsString = "0";
            } else {
                //从repositoryIds中去除已关联的仓库linkedRepoIds
                List<Integer> unlinkedRepoIds = repositoryIds.stream()
                        .filter(repoId -> !Arrays.asList(linkedRepoIds).contains(repoId.longValue()))
                        .collect(Collectors.toList());
                unlinkedRepoIdsString = StringUtils.join(unlinkedRepoIds, ",");
            }
            //在组织仓库中，仅查询pms项目内未被关联的仓库
            queryParams.put("pm_project_repository_ids", unlinkedRepoIdsString);
        }
        queryParams.put("search", optionalWikiReposSearchVo.getSearch());
        queryParams.put("page" , String.valueOf(optionalWikiReposSearchVo.getPage()));
        queryParams.put("limit", String.valueOf(optionalWikiReposSearchVo.getLimit()));
        queryParams.put("sort_by", optionalWikiReposSearchVo.getSortBy());
        queryParams.put("sort_direction", "desc");
        return queryParams;
    }

    public void checkPmsProjectById(Long pmsProjectId) {
        //项目id不为0，校验项目是否存在且拥有操作权限
        if (pmsProjectId != 0) {
            pmsProjectService.selectAndCheckPmsProjectById(pmsProjectId);
        }
    }
}
