package cn.reghao.tnb.search.esapp.blog.service;

import cn.reghao.jutil.jdk.string.IdGenerator;
import cn.reghao.jutil.jdk.web.result.Result;
import cn.reghao.tnb.search.esapp.blog.db.repository.ArticleRepository;
import cn.reghao.tnb.search.esapp.blog.db.repository.ArticleTagRepository;
import cn.reghao.tnb.search.esapp.blog.db.repository.CategoryRepository;
import cn.reghao.tnb.search.esapp.blog.model.CategoryType;
import cn.reghao.tnb.search.esapp.blog.model.dto.ArticleDto;
import cn.reghao.tnb.search.esapp.blog.model.po.Article;
import cn.reghao.tnb.search.esapp.blog.model.po.ArticleTag;
import cn.reghao.tnb.search.esapp.blog.model.po.Category;
import cn.reghao.tnb.search.esapp.blog.util.MarkdownUtil;
import lombok.extern.slf4j.Slf4j;
import org.jsoup.Jsoup;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

/**
 * @author reghao
 * @date 2023-04-10 18:00:50
 */
@Slf4j
@Service
public class ArticleService {
    private final ArticleRepository articleRepository;
    private final ArticleTagRepository articleTagRepository;
    private final CategoryRepository categoryRepository;
    private final IdGenerator idGenerator;

    public ArticleService(ArticleRepository articleRepository, ArticleTagRepository articleTagRepository,
                          CategoryRepository categoryRepository) {
        this.articleRepository = articleRepository;
        this.articleTagRepository = articleTagRepository;
        this.categoryRepository = categoryRepository;
        this.idGenerator = new IdGenerator("article-id");
    }

    public Result addArticle(ArticleDto articleDto) {
        int categoryId = articleDto.getCategoryId();
        Category category = categoryRepository.findById(categoryId).orElse(null);
        if (category == null) {
            String errMsg = String.format("category %s not exist", categoryId);
            return Result.fail(errMsg);
        }

        if (articleDto.getEditor() == null) {
            log.error("editor not exist");
            articleDto.setEditor("markdown");
        }

        String articleId = idGenerator.stringId();
        String content = articleDto.getContent();
        String editor = articleDto.getEditor();
        String excerpt = getExcerpt(editor, content);
        Article article = new Article(articleId, articleDto, excerpt);
        Set<String> tagNameSet = new HashSet<>(articleDto.getTags());
        List<ArticleTag> articleTags = getArticleTags(articleId, tagNameSet);
        save(article, articleTags);
        return Result.success();
    }

    private List<ArticleTag> getArticleTags(String articleId, Set<String> tagNameSet) {
        int type = CategoryType.Tag.getValue();
        Set<Integer> tagIds = new HashSet<>();
        tagNameSet.forEach(tagName -> {
            Category tag = categoryRepository.findByTypeAndName(type, tagName);
            if (tag != null) {
                tagIds.add(tag.getId());
            } else {
                tag = new Category(type, tagName);
                categoryRepository.save(tag);
                tagIds.add(tag.getId());
            }
        });

        return tagIds.stream()
                .map(tagId -> new ArticleTag(articleId, tagId))
                .collect(Collectors.toList());
    }

    @Transactional(rollbackFor = Exception.class)
    public void save(Article article, List<ArticleTag> articleTags) {
        articleRepository.save(article);
        if (!articleTags.isEmpty()) {
            // 删除当前存在的所有标签, 然后再添加更新后的标签
            articleTagRepository.deleteByArticleId(article.getArticleId());
            articleTagRepository.saveAll(articleTags);
        }
    }

    @CacheEvict(cacheNames = "ArticleVOs", key = "#articleDto.getArticleId()")
    public Result updateArticle(ArticleDto articleDto) {
        String articleId = articleDto.getArticleId();
        Article article = articleRepository.findByArticleId(articleId);
        if (article == null) {
            return Result.fail("article not exist");
        }

        int categoryId = articleDto.getCategoryId();
        if (article.getCategoryId() != categoryId) {
            Category category = categoryRepository.findById(categoryId).orElse(null);
            if (category == null) {
                String errMsg = String.format("category %s not exist", categoryId);
                return Result.fail(errMsg);
            }
            article.setCategoryId(categoryId);
        }

        List<Category> currentTags = articleTagRepository.findByArticleId(articleId).stream()
                .map(articleTag -> categoryRepository.findById(articleTag.getTagId()).orElse(null))
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
        Set<String> currentTagNames = currentTags.stream().map(Category::getName).collect(Collectors.toSet());
        boolean notChange = currentTagNames.size() == articleDto.getTags().size();
        if (notChange) {
            for (String tag : articleDto.getTags()) {
                if (!currentTagNames.contains(tag)) {
                    notChange = false;
                    break;
                }
            }
        }

        List<ArticleTag> articleTags = new ArrayList<>();
        if (!notChange) {
            // 如果文章标签有变化, 则重新设置
            Set<String> tagNameSet = new HashSet<>(articleDto.getTags());
            articleTags = getArticleTags(articleId, tagNameSet);
        }

        String editor = article.getEditor();
        String title = articleDto.getTitle();
        String content = articleDto.getContent();
        String excerpt = getExcerpt(editor, content);
        boolean published = articleDto.getPublished();
        article.setTitle(title);
        article.setExcerpt(excerpt);
        article.setContent(content);
        article.setPublished(published);
        save(article, articleTags);
        return Result.success();
    }

    private String getExcerpt(String editor, String content) {
        String text = "";
        if (editor.equals("markdown")) {
            text = MarkdownUtil.getText(content);
        } else if (editor.equals("tinymce")) {
            text = Jsoup.parse(content).text();
        }

        String excerpt = text.length() < 50 ? text : text.substring(0, 50);
        return String.format("<p>%s</p>", excerpt);
    }

    @CacheEvict(cacheNames = "ArticleVOs", key = "#articleId")
    @Transactional(rollbackFor = Exception.class)
    public void deleteArticle(String articleId) {
        Article article = articleRepository.findByArticleId(articleId);
        if (article != null) {
            articleRepository.delete(article);
            articleTagRepository.deleteByArticleId(articleId);
        }
    }
}
