package com.mooctest.service;

import com.hankcs.hanlp.mining.word2vec.DocVectorModel;
import com.hankcs.hanlp.utility.SentencesUtil;
import com.mooctest.cluster.Group;
import com.mooctest.dao.SupplementDao;
import com.mooctest.data.*;
import com.mooctest.image.FingerPrint;
import com.mooctest.model.SupplementItem;
import com.mooctest.util.Doc2VecUtil;
import com.mooctest.util.ImageUtil;
import com.mooctest.util.IndexUtil;
import org.jgrapht.alg.interfaces.VertexScoringAlgorithm;
import org.jgrapht.alg.scoring.PageRank;
import org.jgrapht.graph.DefaultWeightedEdge;
import org.jgrapht.graph.DirectedWeightedPseudograph;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

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

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

@Service
public class SupplementService {

    @Autowired
    SupplementDao supplementDao;

    @Autowired
    MasterReportService masterReportService;

    public void rankAndStoreDiffText(Map<String, List<Group<String, DiffText>>> masterDiffTextClustersMap) {
        masterDiffTextClustersMap.forEach((masterId, diffTextClusters) -> {

            diffTextClusters.forEach(group -> {
                Set<DiffText> cluster = group.getCluster();
                DirectedWeightedPseudograph graph = buildDirectedGraph(cluster);
                VertexScoringAlgorithm<String, Double> pr = new PageRank<>(graph, 0.85, 100, 0.0001);
                cluster.forEach(diffText -> {
                    SupplementItem supplementItem = new SupplementItem(0,
                            masterId,
                            diffText.getBugId(),
                            diffText.getIndex(),
                            group.getId(),
                            pr.getVertexScore(IndexUtil.joinByUnderLine(diffText.getBugId(), diffText.getIndex())),
                            diffText.getSentence(),
                            false);
                    SupplementItem save = supplementDao.save(supplementItem);
                });

            });


        });

    }

    public DirectedWeightedPseudograph buildDirectedGraph(Set<DiffText> cluster) {
        DirectedWeightedPseudograph<String, DefaultWeightedEdge> g =
                new DirectedWeightedPseudograph<>(DefaultWeightedEdge.class);
        cluster.forEach(diffText -> g.addVertex(diffText.getBugId() + "_" + diffText.getIndex()));
        cluster.forEach(outDiffText -> {
            String outId = outDiffText.getBugId() + "_" + outDiffText.getIndex();
            cluster.forEach(inDiffText -> {
                String inId = inDiffText.getBugId() + "_" + inDiffText.getIndex();
                if (!outId.equals(inId) && !g.containsEdge(outId, inId)) {
                    g.setEdgeWeight(g.addEdge(outId, inId), getSim(outDiffText, inDiffText));
                }
            });
        });

        return g;
    }

    public double getSim(DiffText diffText1, DiffText diffText2) {
        DocVectorModel docVectorModel = Doc2VecUtil.loadModel();
        return docVectorModel.similarity(diffText1.getSentence(), diffText2.getSentence());
    }

    public void rankAndStoreDiffImg(Map<String, List<Group<String, DiffImg>>> masterDiffImgClustersMap) {
        masterDiffImgClustersMap.forEach((masterId, diffTextClusters) -> {

            diffTextClusters.forEach(group -> {
                Set<DiffImg> cluster = group.getCluster();
                DirectedWeightedPseudograph graph = buildDirectedGraphForDiffImg(cluster);
                VertexScoringAlgorithm<String, Double> pr = new PageRank<>(graph, 0.85, 100, 0.0001);
                cluster.forEach(diffImg -> {
                    SupplementItem supplementItem = new SupplementItem(0,
                            masterId,
                            diffImg.getBugId(),
                            diffImg.getIndex(),
                            group.getId(),
                            pr.getVertexScore(IndexUtil.joinByUnderLine(diffImg.getBugId(), diffImg.getIndex())),
                            diffImg.getImgUrl(),
                            true);
                    SupplementItem save = supplementDao.save(supplementItem);
                });

            });


        });

    }

    public DirectedWeightedPseudograph buildDirectedGraphForDiffImg(Set<DiffImg> cluster) {
        DirectedWeightedPseudograph<String, DefaultWeightedEdge> g =
                new DirectedWeightedPseudograph<>(DefaultWeightedEdge.class);
        cluster.forEach(diffText -> g.addVertex(diffText.getBugId() + "_" + diffText.getIndex()));
        cluster.forEach(outDiffImg -> {
            String outId = outDiffImg.getBugId() + "_" + outDiffImg.getIndex();
            cluster.forEach(inDiffImg -> {
                String inId = inDiffImg.getBugId() + "_" + inDiffImg.getIndex();
                if (!outId.equals(inId) && !g.containsEdge(outId, inId)) {
                    g.setEdgeWeight(g.addEdge(outId, inId), getSim(outDiffImg, inDiffImg));
                }
            });
        });

        return g;
    }

    public double getSim(DiffImg diffImg1, DiffImg diffImg2) {
        long[] ids = masterReportService.getExamIdAndCaseIdByMasterId(diffImg1.getMasterId());
        long examId = ids[0];
        long caseId = ids[1];
        String taskId = examId + "_" + caseId;

        File img1 = new File(ImageUtil.genImagePath(taskId, diffImg1.getBugId(), diffImg1.getIndex()));
        File img2 = new File(ImageUtil.genImagePath(taskId, diffImg2.getBugId(), diffImg2.getIndex()));
        if (!img1.exists() || !img2.exists()) {
            return 0;
        }
        FingerPrint fp1 = ImageUtil.readImgFingerPrint(img1);
        FingerPrint fp2 = ImageUtil.readImgFingerPrint(img2);
        return fp1.compare(fp2);
    }

    public List<SupplementDTO> getSupplementByMasterId(String masterId, Map<String, BugDTO> bugMap) {
        List<SupplementItem> supplementItems = supplementDao.findByMasterId(masterId);
        List<String> sups = supplementItems.stream().map(SupplementItem::getSupplementId).distinct().collect(toList());
        Map<String, List<SupplementItem>> supplementMap = supplementItems.stream().collect(groupingBy(SupplementItem::getSupplementId));
        List<SupplementDTO> sortedSupplements = new ArrayList<>(supplementMap.size());


        sups.forEach(supId -> {
            SupplementDTO su = buildSupplement(supId, supplementMap, bugMap);
            sortedSupplements.add(su);
        });
        return sortedSupplements;
    }

    public List<SupplementDTO> getSupplementTopInfoByMasterId(String masterId) {
        return getSupplementByMasterId(masterId, null);
    }

    private SupplementDTO buildSupplement(String supId,
                                          Map<String, List<SupplementItem>> supplementMap,
                                          Map<String, BugDTO> bugMap) {

        List<SupplementItem> items = supplementMap.get(supId);


        boolean hasTxt = items.stream().filter(item -> !item.isImg()).count() > 0 ? true : false;

        items = items.stream().sorted(Comparator.comparingDouble(SupplementItem::getPageRankScore).reversed()).collect(Collectors.toList());
        SupplementDTO su = SupplementDTO.builder()
                .supplementId(supId)
                .items(items)
                .hasTxt(hasTxt)
                .build();

        if (bugMap != null) {
            List<String> bugIds = items.stream()
                    .map(SupplementItem::getBugId)
                    .distinct()
                    .collect(Collectors.toList());
            su.setBugs(queryBug(bugIds, bugMap, items));
        }

        if (hasTxt) {
            String topTxt = findTopTxt(items);
            su.setTopTxt(topTxt);
        } else {
            List<String> top3Img = findTop3Img(items);
            su.setTop3Img(top3Img);
        }
        return su;
    }



    private Map<String, Long> sortByValue(final Map<String, Long> wordCounts) {
        return wordCounts.entrySet()
                .stream()
                .sorted((Map.Entry.<String, Long>comparingByValue().reversed()))
                .collect(toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
    }

    private List<BugDTO> queryBug(List<String> bugIds,
                                  Map<String, BugDTO> bugMap,
                                  List<SupplementItem> items) {
        return bugIds.stream()
                .map(bugId -> bugMap.get(bugId).toBuilder().build())
                .map(bugDTO -> {
                    bugDTO.setTaggedSentences(tagSentence(bugDTO, items));
                    bugDTO.setTaggedImgs(tagImg(bugDTO, items));
                    return bugDTO;
                })
                .collect(Collectors.toList());
    }

    private List<SentenceDTO> tagSentence(BugDTO bug,
                                          List<SupplementItem> items) {
        List<String> sens = SentencesUtil.toSentenceList(bug.getDescription());
        List<SentenceDTO> sentenceDTOs = new ArrayList<>(sens.size());
        for (int i = 0; i < sens.size(); i++) {
            String s = sens.get(i);
            SentenceDTO sentenceDTO = new SentenceDTO(s, false);
            for (SupplementItem item : items) {
                if (item.getBugId().equals(bug.getId()) && !item.isImg() && item.getIndex() == i) {
                    sentenceDTO.setDiff(true);
                }
            }
            sentenceDTOs.add(sentenceDTO);
        }

        return sentenceDTOs;
    }

    private List<ImgDTO> tagImg(BugDTO bug,
                                List<SupplementItem> items) {
        String[] imgUrls = bug.getImgUrls();
        List<ImgDTO> imgDTOs = new ArrayList<>(imgUrls.length);
        for (int i = 0; i < imgUrls.length; i++) {
            String imgUrl = imgUrls[i];
            ImgDTO imgDTO = new ImgDTO(imgUrl, false);
            for (SupplementItem item : items) {
                if (item.getBugId().equals(bug.getId()) && item.isImg() && item.getIndex() == i) {
                    imgDTO.setDiff(true);
                }
            }
            imgDTOs.add(imgDTO);
        }
        return imgDTOs;
    }
    private String findTopTxt(List<SupplementItem> items) {
        for (SupplementItem item : items) {
            if (item.isImg()) {
                continue;
            }
            return item.getContent();
        }
        return "";
    }

    private List<String> findTop3Img(List<SupplementItem> items) {
        List<String> top3Imgs = new ArrayList<>(3);
        for (SupplementItem item : items) {
            if (item.isImg() && top3Imgs.size() < 3) {
                top3Imgs.add(item.getContent());
            }
        }
        return top3Imgs;
    }

    public SupplementDTO findBySupId(String supId, Map<String, BugDTO> bugMap) {

        List<SupplementItem> items = supplementDao.findBySupplementId(supId);
        Map<String, List<SupplementItem>> supplementMap = items.stream().collect(groupingBy(SupplementItem::getSupplementId));

        return buildSupplement(supId, supplementMap, bugMap);
    }

    public Map<String, List<String>> getMaster2BugIdsMap(List<String> masterIds) {
        List<SupplementItem> supItems = supplementDao.findByMasterIdIn(masterIds);
        List<String[]> masterBugs = supItems.stream()
                .map(item -> item.getMasterId() + "-" + item.getBugId())
                .distinct()
                .map(s -> s.split("-"))
                .collect(toList());
        Map<String, List<String>> masterBugIdsMap = new HashMap<>();
        for (String[] masterBug : masterBugs) {
            String masterId = masterBug[0];
            String bugId = masterBug[1];

            if (masterBugIdsMap.get(masterId) == null) {
                List<String> bugs = new ArrayList<>();
                bugs.add(bugId);
                masterBugIdsMap.put(masterId, bugs);
            } else {
                List<String> bugs = masterBugIdsMap.get(masterId);
                bugs.add(bugId);
                masterBugIdsMap.put(masterId, bugs);
            }
        }
        return masterBugIdsMap;
    }

    public void deleteAll(long examId, long caseId) {

        List<String> masterIds = masterReportService.getAllMasterIdByExamIdAndCaseId(examId, caseId);
        supplementDao.deleteByMasterIdIn(masterIds);
    }
}
