package cn.reghao.tnb.search.app.hibernate;

import cn.reghao.tnb.search.app.blog.db.repository.ArticleRepository;
import cn.reghao.tnb.search.app.blog.db.repository.CategoryRepository;
import cn.reghao.tnb.search.app.blog.model.po.Article;
import cn.reghao.tnb.search.app.blog.model.vo.ArticleProjection;
import cn.reghao.tnb.search.app.blog.model.vo.UserArticle;
import cn.reghao.tnb.search.app.db.repository.WenshuDocRepository;
import cn.reghao.tnb.search.app.model.po.Wenshu;
import cn.reghao.tnb.search.app.model.po.WenshuDoc;
import cn.reghao.tnb.search.app.model.vo.WenshuDocProjection;
import cn.reghao.tnb.search.app.model.vo.WenshuResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.*;
import org.apache.lucene.search.highlight.Highlighter;
import org.apache.lucene.search.highlight.QueryScorer;
import org.apache.lucene.search.highlight.SimpleHTMLFormatter;
import org.hibernate.search.engine.search.query.SearchResult;
import org.hibernate.search.mapper.orm.Search;
import org.hibernate.search.mapper.orm.session.SearchSession;
import org.jsoup.Jsoup;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

import jakarta.persistence.EntityManagerFactory;
import org.wltea.analyzer.lucene.IKAnalyzer;

import java.util.List;
import java.util.stream.Collectors;

/**
 * @author reghao
 * @date 2023-04-15 03:47:50
 */
@Slf4j
@Service
public class HibernateQuery {
    private final SearchSession searchSession;
    private final WenshuDocRepository wenshuDocRepository;
    private final CategoryRepository categoryRepository;
    private final ArticleRepository articleRepository;
    private final SimpleHTMLFormatter formatter;
    private final Analyzer analyzer;

    public HibernateQuery(EntityManagerFactory entityManagerFactory, WenshuDocRepository wenshuDocRepository,
                          CategoryRepository categoryRepository, ArticleRepository articleRepository) {
        this.searchSession = Search.session(entityManagerFactory.createEntityManager());
        this.wenshuDocRepository = wenshuDocRepository;
        this.categoryRepository = categoryRepository;
        this.articleRepository = articleRepository;
        this.analyzer = new IKAnalyzer();
        this.formatter = new SimpleHTMLFormatter("<span style='color:red;'>", "</span>");
    }

    public Page<WenshuResult> search(String keyword, Pageable pageable) {
        int pn = pageable.getPageNumber();
        int ps = pageable.getPageSize();
        SearchResult<WenshuDocProjection> highlightResult = searchSession.search(WenshuDoc.class)
                .select(f -> f.composite()
                        .from(f.field("wenshuId", String.class),
                                f.highlight("caseName"))
                        .as(WenshuDocProjection::new))
                .where(f -> f.bool(b -> {
                    b.must(f.matchAll())
                            .should(f.match().field("caseName").matching(keyword).boost(3));
                }))
                .highlighter(f -> f.plain().tag("<span style='color:red;'>", "</span>"))
                .fetch(pn*ps, ps);

        long totalHitCount = highlightResult.total().hitCount();
        List<WenshuDocProjection> list1 = highlightResult.hits();
        List<WenshuResult> wenshuResultList = list1.stream().map(wenshuDocProjection -> {
            String wenshuId = wenshuDocProjection.getWenshuId();
            WenshuDoc wenshuDoc = wenshuDocRepository.findByWenshuId(Long.parseLong(wenshuId));
            Wenshu wenshu = new Wenshu(wenshuDoc);
            WenshuResult wenshuResult = new WenshuResult(wenshu);

            List<String> highlightCaseName = wenshuDocProjection.getHighlightCaseName();
            if (!highlightCaseName.isEmpty()) {
                String caseName = highlightCaseName.get(0);
                wenshuResult.setCaseName(caseName);
            }
            return wenshuResult;
        }).collect(Collectors.toList());
        return new PageImpl<>(wenshuResultList, pageable, totalHitCount);
    }

    public Page<UserArticle> searchFromLucene(String keyword, Pageable pageable) {
        int pn = pageable.getPageNumber();
        int ps = pageable.getPageSize();
        SearchResult<Article> result = searchSession.search(Article.class)
                .where(f -> f.bool(b -> {
                    b.must(f.matchAll())
                            .should(f.match().field("title").matching(keyword).boost(3))
                            .should(f.match().field("content").matching(keyword).boost(3));
                }))
                .fetch(pn*ps, ps);
        long totalHitCount0 = result.total().hitCount();
        List<Article> list0 = result.hits();

        Query luceneQuery = getLuceneQuery(keyword);
        QueryScorer scorer = new QueryScorer(luceneQuery);
        Highlighter highlighter = new Highlighter(formatter, scorer);
        List<UserArticle> userArticles0 = list0.stream().map(article -> {
            int viewCount = 0;
            int categoryId = article.getCategoryId();
            String category = categoryRepository.findById(categoryId).orElse(null).getName();
            UserArticle userArticle = new UserArticle(article, viewCount, category);
            try {
                // 处理高亮
                String title = highlighter.getBestFragment(analyzer, "title", userArticle.getTitle());
                if (title != null && !title.isBlank()) {
                    userArticle.setTitle(title);
                }

                String text = Jsoup.parse(article.getContent()).text();
                String content = highlighter.getBestFragment(analyzer, "content", text);
                if (content != null && !content.isBlank()) {
                    userArticle.setSummary(content);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            return userArticle;
        }).collect(Collectors.toList());
        return new PageImpl<>(userArticles0, pageable, totalHitCount0);
    }

    private Query getLuceneQuery(String keyword) {
        Query query1 = new BoostQuery(new TermQuery(new Term("title", keyword)), 3);
        Query query2 = new BoostQuery(new TermQuery(new Term("content", keyword)), 3);
        BooleanQuery.Builder builder = new BooleanQuery.Builder();
        builder.add(query1, BooleanClause.Occur.SHOULD);
        builder.add(query2, BooleanClause.Occur.SHOULD);
        BooleanQuery luceneQuery = builder.build();
        return luceneQuery;
    }

    public Page<UserArticle> searchArticle(String keyword, Pageable pageable) {
        int pn = pageable.getPageNumber();
        int ps = pageable.getPageSize();
        SearchResult<ArticleProjection> highlightResult = searchSession.search(Article.class)
                .select(f -> f.composite()
                        .from(f.field("articleId", String.class),
                                f.highlight("title"),
                                f.highlight("content"))
                        .as(ArticleProjection::new))
                .where(f -> f.bool(b -> {
                    b.must(f.matchAll())
                            .should(f.match().field("title").matching(keyword).boost(3))
                            .should(f.match().field("content").matching(keyword).boost(3));
                }))
                .highlighter(f -> f.plain().tag("<span style='color:red;'>", "</span>"))
                .fetch(pn*ps, ps);

        long totalHitCount = highlightResult.total().hitCount();
        List<ArticleProjection> list1 = highlightResult.hits();
        List<UserArticle> userArticles = list1.stream().map(article -> {
            int viewCount = 0;
            String articleId = article.getArticleId();
            Article article1 = articleRepository.findByArticleId(articleId);
            int categoryId = article1.getCategoryId();
            String category = categoryRepository.findById(categoryId).orElse(null).getName();
            UserArticle userArticle = new UserArticle(article1, viewCount, category);
            List<String> highlightTitle = article.getHighlightTitle();
            if (!highlightTitle.isEmpty()) {
                userArticle.setTitle(highlightTitle.get(0));
            }

            List<String> highlightContent = article.getHighlightContent();
            if (!highlightContent.isEmpty()) {
                userArticle.setSummary(highlightContent.get(0));
            }

            return userArticle;
        }).collect(Collectors.toList());

        return new PageImpl<>(userArticles, pageable, totalHitCount);
    }
}
