package com.microservices.dms.referral.service;

import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.google.common.collect.Lists;
import com.microservices.common.core.exception.ServiceException;
import com.microservices.common.core.utils.StringUtils;
import com.microservices.dms.achievementLibrary.domain.Achievements;
import com.microservices.dms.achievementLibrary.domain.KeyValueVo;
import com.microservices.dms.achievementLibrary.mapper.AchievementsMapper;
import com.microservices.dms.constant.ReferralConstant;
import com.microservices.dms.constant.TaskConstant;
import com.microservices.dms.referral.mapper.TalentReferralMapper;
import com.microservices.dms.referral.vo.*;
import com.microservices.dms.resourceLibrary.domain.vo.KeyValVo;
import com.microservices.dms.utils.DmsGitLinkRequestUrl;
import com.microservices.dms.utils.DmsRequestHelper;
import com.microservices.dms.utils.SimilarityService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 人才推荐服务
 */
@Service
public class TalentReferralService {

    @Autowired
    private TalentReferralMapper talentReferralMapper;

    @Autowired
    private DmsRequestHelper dmsRequestHelper;

    @Autowired
    private AchievementsMapper achievementsMapper;

    private static final Logger logger = LoggerFactory.getLogger(TalentReferralService.class);

    public Set<Long> getContributors(String projectFullName) {
        try {
            // 获取项目贡献者数量
            JSONArray contributorsArray = dmsRequestHelper.getAllDataByPage(DmsGitLinkRequestUrl.CONTRIBUTORS(projectFullName), "list", "total_count");

            return contributorsArray.stream().map(o -> (JSONObject) o).map(s -> s.getString("contributions")).filter(StringUtils::isNotBlank).mapToLong(Long::parseLong).boxed().collect(Collectors.toSet());

        } catch (ServiceException e) {
            logger.error("【{}】获取项目贡献者数量失败：{}", projectFullName, e.getMessage());
        }
        return new HashSet<>();
    }

    public Set<Long> getCollaborators(String projectFullName) {
        try {
            // 获取项目成员者数量
            JSONArray contributorsArray = dmsRequestHelper.getAllDataByPage(DmsGitLinkRequestUrl.QUERY_USER_FROM_PROJECT(projectFullName), "members", "total_count");

            return contributorsArray.stream().map(o -> (JSONObject) o).map(s -> s.getString("id")).filter(StringUtils::isNotBlank).mapToLong(Long::parseLong).boxed().collect(Collectors.toSet());

        } catch (ServiceException e) {
            logger.error("【{}】获取项目成员者数量失败：{}", projectFullName, e.getMessage());
        }
        return new HashSet<>();
    }

    public String getRepoName(Long projectId) {
        // 获取仓库的fullName,用来查询贡献者
        String s = talentReferralMapper.selectRepositoriesUrl(projectId);
        return StringUtils.isNotEmpty(s) ? s : "";
    }

    public Set<Long> getContributorsByProjectId(Long projectId) {
        Set<Long> contributors = new HashSet<>();
        String repoName = getRepoName(projectId);
        if (StringUtils.isNotBlank(repoName)) {
            contributors.addAll(getContributors(repoName));
        }
        return contributors;
    }

    public Set<Long> getCollaboratorsByProjectId(Long projectId) {
        Set<Long> collaborators = new HashSet<>();
        String repoName = getRepoName(projectId);
        if (StringUtils.isNotBlank(repoName)) {
            collaborators.addAll(getCollaborators(repoName));
        }
        return collaborators;
    }

    public List<Map<String, Object>> doIssueTalentReferral(Long issueId , Long projectId) {
        List<Map.Entry<Long, Double>> entries = issueTalentReferral(projectId, issueId);
        return entries.stream().limit(3).map(s -> {
            Map<String, Object> mm = talentReferralMapper.selectUsersById(s.getKey());
            return mm;
        }).collect(Collectors.toList());
    }

    public List<Map.Entry<Long, Double>> issueTalentReferral(Long projectId, Long issueId) {

        IssuesVo curIssue = talentReferralMapper.selectIssuesById(issueId);
        if (curIssue == null) {
            return new ArrayList<>();
        }

        List<Long> curIssueAssigners = talentReferralMapper.selectIssuesAssigners(curIssue.getId());
        if (!curIssueAssigners.isEmpty()) {
           // return new ArrayList<>();
        }

        IssuesVo vo = new IssuesVo();
        vo.setProjectId(projectId);
        vo.setStatusIds(Lists.newArrayList(ReferralConstant.ISSUE_CLOSED,ReferralConstant.ISSUE_RESOLVED));
        List<IssuesVo> issuesVos = talentReferralMapper.selectIssuesCondition(vo);
        Map<Long, Double> userFractionMap = new HashMap<>();
        // 计算与解决issue的相似度， 标题+描述
        for (IssuesVo v : issuesVos) {
            String subject = v.getSubject();
            String description = v.getDescription();
            double subjectFraction = SimilarityService.sentence(subject, curIssue.getSubject());
            double descFraction = SimilarityService.text(description, curIssue.getDescription());
            //这个数据可以缓存下来
            List<Long> assigners = talentReferralMapper.selectIssuesAssigners(v.getId());
            for (Long assigner : assigners) {
                userFractionMap.merge(assigner, (subjectFraction + descFraction) * 60.0D, Double::sum);
            }
        }

        // 计算每个负责人关闭的issues数量
        List<IssuesVo> assIs = talentReferralMapper.countIssuesAssigner(projectId);
        for (IssuesVo a : assIs) {
            userFractionMap.merge(a.getId(), a.getNum() * 40.0D, Double::sum);
        }

        List<Map.Entry<Long, Double>> entryList = new ArrayList<>(userFractionMap.entrySet());
        entryList.sort((entry1, entry2) -> Double.compare(entry2.getValue(), entry1.getValue()));

        Set<Long> contributors = getContributorsByProjectId(projectId);
        if (!contributors.isEmpty() && !entryList.isEmpty()) {
            entryList.removeIf(s -> !contributors.contains(s.getKey()));
        }
        return entryList;
    }

    public List<Map.Entry<Long, Double>> PRTalentReferral(Long projectId, Long prId, List<IssueDto> prs) {

        Map<Long, Double> userFractionMap = new HashMap<>();
        Map<String, Double> userNameFractionMap = new HashMap<>();
        Map<String, Set<String>> PRFilesMap = new HashMap<>();
        List<IssueDto> closedPullRequests = getPullRequests(projectId, "11").stream()
                .filter(e->StringUtils.isNotBlank(e.getAssignUserLogin())).collect(Collectors.toList());

        for (IssueDto pr : prs) {
            PrInfoVo curPrInfoVo = talentReferralMapper.selectPRInfoById(pr.getPullRequestId());
            //关闭PR的对比
            for (IssueDto dto : closedPullRequests) {
                String reviewer = dto.getAssignUserLogin();
                Set<String> prFiles = getPRFiles(projectId, dto.getPullRequestNumber());
                PRFilesMap.merge(reviewer, prFiles, (a, b) -> {
                    HashSet<String> set = new HashSet<>();
                    set.addAll(a);
                    set.addAll(b);
                    return set;
                });

                //标题、content匹配
                PrInfoVo prInfoVo = talentReferralMapper.selectPRInfoById(dto.getPullRequestId());
                double subjectFraction = SimilarityService.sentence(curPrInfoVo.getTitle(), prInfoVo.getTitle());
                double descFraction = SimilarityService.text(curPrInfoVo.getContent(), prInfoVo.getContent());
                //这个数据可以缓存下来
                userNameFractionMap.merge(dto.getAssignUserLogin(), (subjectFraction + descFraction) * 40.0D, Double::sum);
            }

            Set<String> curPrFiles = getPRFiles(projectId, pr.getPullRequestNumber());
            for (String curPrFile : curPrFiles) {
                PRFilesMap.forEach((k, v) -> {
                    if (v.contains(curPrFile)) {
                        userNameFractionMap.merge(k, 30.0D, Double::sum);
                    }
                });
            }

            // 计算每个负责人关闭的 PR数量
            closedPullRequests.stream().map(IssueDto::getAssignUserLogin).forEach(s -> userNameFractionMap.merge(s, 30.0D, Double::sum));
        }

        userNameFractionMap.forEach((k, v) -> {
            Long id = talentReferralMapper.selectIdByLogin(k);
            userFractionMap.put(id, v);
        });

        List<Map.Entry<Long, Double>> entryList = new ArrayList<>(userFractionMap.entrySet());
        entryList.sort((entry1, entry2) -> Double.compare(entry2.getValue(), entry1.getValue()));

        //必须是项目成员
        Set<Long> collaborators = getCollaboratorsByProjectId(projectId);
        if (!collaborators.isEmpty() && !entryList.isEmpty()) {
            entryList.removeIf(s -> !collaborators.contains(s.getKey()));
        }

        return entryList;
    }

    public List<IssueDto> getPullRequests(Long projectId, String statusType) {

        String projectFullName = getRepoName(projectId);
        if (StringUtils.isEmpty(projectFullName)) {
            return new ArrayList<>();
        }

        try {
            return dmsRequestHelper.getAllDataByPage(DmsGitLinkRequestUrl.GET_PULL_REQUEST(projectFullName, statusType), "issues", IssueDto.class);
        } catch (ServiceException e) {
            logger.error("【{}】获取PR失败：{}", projectFullName, e.getMessage());
        }
        return new ArrayList<>();
    }

    public Set<String> getPRFiles(Long projectId, int PRNum) {
        String projectFullName = getRepoName(projectId);
        if (StringUtils.isEmpty(projectFullName)) {
            return new HashSet<>();
        }

        try {
            JSONArray allDataByPage = dmsRequestHelper.getAllDataByPage(DmsGitLinkRequestUrl.GET_PR_FILES(projectFullName, PRNum), "files", "file_nums");
            return allDataByPage.stream().map(o -> (JSONObject) o).map(o -> (String) o.get("filename")).collect(Collectors.toSet());
        } catch (ServiceException e) {
            logger.error("【{}】获取PR文件失败：{}", projectFullName, e.getMessage());
        }
        return new HashSet<>();
    }

    public List<Map<String, Object>> doPRTalentReferral(Long prId, Long projectId) {
        List<Map.Entry<Long, Double>> entries = PRTalentReferral(projectId, prId);
        return entries.stream().limit(3).map(s -> {
            Map<String, Object> mm = talentReferralMapper.selectUsersById(s.getKey());
            return mm;
        }).collect(Collectors.toList());
    }

    public List<Map.Entry<Long, Double>> PRTalentReferral(Long projectId, Long prId) {
        List<IssueDto> openPullRequests = getPullRequests(projectId, "1");
        openPullRequests = openPullRequests.stream().filter(e -> StringUtils.isEmpty(e.getAssignUserLogin())).collect(Collectors.toList());

        if (prId != null) {
            openPullRequests = openPullRequests.stream().filter(e -> Objects.equals((long) (e.getPullRequestId()), prId)).collect(Collectors.toList());
            return PRTalentReferral(projectId, prId, openPullRequests);
        }

        return PRTalentReferral(projectId, null, openPullRequests);
    }

    //定时统计
    public void projectsStatistic() {

    }

    /**
     * 1、先查找任务状态为成果征集中、成果评选中的数据，新增存储到数据表中
     * 2、获取系统中的任务领域和数据仓库中的领域是一样 ；50%
     * 3、根据1中任务标题和任务正文 与数据仓库中名称和简介、详情比较 ；取前10获取对比计算分值大于0的结果
     * 4、将2和3的结果相加，取前5个成果进行推荐
     */
    public List<Achievements> taskAchievementTalentReferral(Long taskId) {
        TaskVo task = talentReferralMapper.selectTaskById(taskId);
        Achievements tCond = new Achievements();
        tCond.setSourceId(taskId);
        tCond.setSource("2");
        tCond.setStatus("1");
        List<Achievements> taskAchievements = achievementsMapper.selectAchievementsList(tCond);
        List<Achievements> result;

        List<Long> trTaskIds = doTaskTalentReferral(task, taskAchievements);
        result = trTaskIds.stream().map(s -> taskAchievements.stream().filter(e -> e.getId().equals(s)).findFirst().orElse(null)).filter(Objects::nonNull).limit(5).collect(Collectors.toList());

        return result;
    }

    private List<Long> doTaskTalentReferral(TaskVo task, List<Achievements> taskVos) {
        Long taskDomain = task.getCategoryId();
        Map<Long, Double> step1Map = new TreeMap<>();
        Map<Long, Double> step2Map = new TreeMap<>();
        for (Achievements t1 : taskVos) {
            if (Objects.equals(String.valueOf(taskDomain), t1.getField1())) {
                step1Map.put(t1.getId(), 50.0D);
            }
            double subjectFraction = SimilarityService.sentence(task.getName(), t1.getAchievementName());
            double descFraction = SimilarityService.text(t1.getSummary(), task.getDescription());
            step2Map.put(t1.getId(), (subjectFraction + descFraction) * 50.0D);
        }

        step1Map.forEach((k, v) -> step2Map.merge(k, v, Double::sum));
        step2Map.keySet().removeIf(step -> Double.compare(step2Map.get(step), 0.0D) > 0);

        List<Map.Entry<Long, Double>> entryList = new ArrayList<>(step2Map.entrySet());
        entryList.sort((entry1, entry2) -> Double.compare(entry2.getValue(), entry1.getValue()));

        return entryList.stream().map(Map.Entry::getKey).collect(Collectors.toList());
    }


    /**
     * 创客任务评审专家推荐
     * 1、先查找创客任务状态未结束，并且已加入评审的数据，新增存储到数据表中
     * 2、技术匹配度：系统中专家领域 与 任务是一样的领域   20%
     * 3、根据1中任务标题和任务描述 与专家简介进行比较 ，匹配结果分数，取前10；30%
     * 4、根据1中任务标题和任务描述 与已完成的任务标题与任务描述比较，匹配结果分数，取前3 ，前3个竞赛中对应的专家   分数*50%
     */
    public Object taskExpertTalentReferral(Long taskId) {
        TaskVo task = talentReferralMapper.selectTaskById(taskId);
        List<KeyValueVo> categories = achievementsMapper.getAreasByName(null);
        List<TaskVo> completedTasks = talentReferralMapper.selectTaskByStatus(Lists.newArrayList(TaskConstant.TASK_COMPLETE_STATUS));
        List<ExpertVo> allExperts = talentReferralMapper.selectExpertsBy(new ExpertVo());

        List<Long> expertIds = doTaskExpertTalentReferral(task, categories, completedTasks, allExperts);
        return expertIds.stream().map(s -> allExperts.stream().filter(e -> e.getId().equals(s)).findFirst().orElse(null)).filter(Objects::nonNull).limit(3).collect(Collectors.toList());
    }

    private List<Long> doTaskExpertTalentReferral(TaskVo task, List<KeyValueVo> categories, List<TaskVo> completedTasks, List<ExpertVo> allExperts) {

        Map<Long, String> v2k = categories.stream().collect(Collectors.toMap(KeyValueVo::getValue, KeyValueVo::getKey, (o, n) -> n));

        String taskDomain = v2k.get(task.getCategoryId());
        Map<Long, Double> step1Map = new TreeMap<>();
        Map<Long, Double> step2Map = new TreeMap<>();
        for (ExpertVo t1 : allExperts) {
            if (Objects.equals(taskDomain, t1.getReviewAreaOne()) || Objects.equals(taskDomain, t1.getReviewAreaTwo()) || Objects.equals(taskDomain, t1.getReviewAreaThree())) {
                step1Map.put(t1.getId(), 50.0D);
            }
        }

        List<KeyValVo<Long, String>> allTaskWithExpertIds = talentReferralMapper.allTaskWithExpertIds("1");
        Map<Long, String> t2e = allTaskWithExpertIds.stream().collect(Collectors.toMap(KeyValVo::getK, KeyValVo::getV, (o, n) -> n));
        for (TaskVo t2 : completedTasks) {
            double subjectFraction = SimilarityService.sentence(task.getName(), t2.getName());
            double descFraction = SimilarityService.text(t2.getDescription(), task.getDescription());

            String[] split = t2e.getOrDefault(t2.getId(), "").split(",");
            for (String s : split) {
                if (StringUtils.isNotBlank(s)) {
                    step2Map.put(Long.parseLong(s), (subjectFraction + descFraction) * 50.0D);
                }
            }
        }

        step1Map.forEach((k, v) -> step2Map.merge(k, v, Double::sum));

        List<Map.Entry<Long, Double>> entryList = new ArrayList<>(step2Map.entrySet());
        entryList.sort((entry1, entry2) -> Double.compare(entry2.getValue(), entry1.getValue()));

        return entryList.stream().map(Map.Entry::getKey).collect(Collectors.toList());

    }

    /**
     * 开源竞赛评审专家推荐
     * 1、先查找开放竞赛状态未结束，并且已提交评审的数据，新增存储到数据表中
     * 2、技术匹配度：系统中专家领域 与 竞赛是一样的领域 ； 20%
     * 3、根据1中竞赛标题和竞赛描述 与专家简介进行比较 ，匹配结果分数，取前10；30%
     * 4、根据1中竞赛标题和竞赛描述 与已完成的竞赛标题与竞赛描述比较，匹配结果分数，取前3 ，前3个竞赛中对应的专家；*50%
     */
    public Object competitionExpertTalentReferral(Long competitionId) {
        TaskVo task = talentReferralMapper.selectCompetitionById(competitionId);
        List<KeyValueVo> categories = new ArrayList<>();
        List<TaskVo> completedTasks = talentReferralMapper.selectCompetitionIdByStatus();
        List<ExpertVo> allExperts = talentReferralMapper.selectExpertsBy(new ExpertVo());

        List<Long> expertIds = doCompetitionExpertTalentReferral(task, categories, completedTasks, allExperts);
        return expertIds.stream().map(s -> allExperts.stream().filter(e -> e.getId().equals(s)).findFirst().orElse(null)).filter(Objects::nonNull).limit(3).collect(Collectors.toList());
    }


    private List<Long> doCompetitionExpertTalentReferral(TaskVo task, List<KeyValueVo> categories, List<TaskVo> completedTasks, List<ExpertVo> allExperts) {

        Map<Long, Double> step1Map = new TreeMap<>();
        Map<Long, Double> step2Map = new TreeMap<>();


        List<KeyValVo<Long, String>> allTaskWithExpertIds = talentReferralMapper.allTaskWithExpertIds("2");
        Map<Long, String> t2e = allTaskWithExpertIds.stream().collect(Collectors.toMap(KeyValVo::getK, KeyValVo::getV, (o, n) -> n));
        for (TaskVo t2 : completedTasks) {
            double subjectFraction = SimilarityService.sentence(task.getName(), t2.getName());
            double descFraction = SimilarityService.text(t2.getDescription(), task.getDescription());

            String[] split = t2e.getOrDefault(t2.getId(), "").split(",");
            for (String s : split) {
                if (StringUtils.isNotBlank(s)) {
                    step2Map.put(Long.parseLong(s), (subjectFraction + descFraction) * 100.0D);
                }
            }
        }

        step1Map.forEach((k, v) -> step2Map.merge(k, v, Double::sum));

        List<Map.Entry<Long, Double>> entryList = new ArrayList<>(step2Map.entrySet());
        entryList.sort((entry1, entry2) -> Double.compare(entry2.getValue(), entry1.getValue()));

        return entryList.stream().map(Map.Entry::getKey).collect(Collectors.toList());

    }
}
