package io.kaif.service.impl;

import java.time.Instant;
import java.util.List;
import java.util.Optional;

import javax.annotation.Nullable;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.util.HtmlUtils;

import io.kaif.flake.FlakeId;
import io.kaif.model.account.Account;
import io.kaif.model.account.AccountDao;
import io.kaif.model.account.Authorization;
import io.kaif.model.article.Article;
import io.kaif.model.article.ArticleDao;
import io.kaif.model.debate.Debate;
import io.kaif.model.debate.DebateDao;
import io.kaif.model.zone.Zone;
import io.kaif.model.zone.ZoneDao;
import io.kaif.model.zone.ZoneInfo;
import io.kaif.service.ArticleService;
import io.kaif.web.support.AccessDeniedException;

@Service
@Transactional
public class ArticleServiceImpl implements ArticleService {

  private static final int PAGE_SIZE = 25;

  @Autowired
  private AccountDao accountDao;

  @Autowired
  private ArticleDao articleDao;

  @Autowired
  private ZoneDao zoneDao;

  @Autowired
  private DebateDao debateDao;

  @Override
  public Article createExternalLink(Authorization authorization,
      Zone zone,
      String title,
      String url) {
    //creating article should not use cache
    ZoneInfo zoneInfo = zoneDao.loadZoneWithoutCache(zone);

    Account author = accountDao.strongVerifyAccount(authorization)
        .filter(zoneInfo::canWriteArticle)
        .orElseThrow(() -> new AccessDeniedException("no write to create article at zone:" + zone));

    Article article = articleDao.createExternalLink(zone,
        author,
        HtmlUtils.htmlEscape(title),
        HtmlUtils.htmlEscape(url),
        Instant.now());
    accountDao.increaseArticleCount(author);
    return article;
  }

  public Optional<Article> findArticle(Zone zone, FlakeId articleId) {
    return articleDao.findArticle(zone, articleId);
  }

  @Override
  public List<Article> listLatestZoneArticles(Zone zone, @Nullable FlakeId startArticleId) {
    return articleDao.listZoneArticlesDesc(zone, startArticleId, PAGE_SIZE);
  }

  @Override
  public Article loadArticle(Zone zone, FlakeId articleId) throws EmptyResultDataAccessException {
    return articleDao.loadArticle(zone, articleId);
  }

  @Override
  public String loadEditableDebateContent(FlakeId articleId,
      FlakeId debateId,
      Authorization editor) {
    Debate debate = debateDao.loadDebate(articleId, debateId);
    if (!debate.canEdit(editor)) {
      throw new AccessDeniedException("no permission to edit debate:" + debate.getDebateId());
    }
    return HtmlUtils.htmlEscape(debate.getContent());
  }

  @Override
  public String updateDebateContent(FlakeId articleId,
      FlakeId debateId,
      Authorization editorAuth,
      String content) {

    Account editor = accountDao.strongVerifyAccount(editorAuth)
        .orElseThrow(() -> new AccessDeniedException("no permission to edit debate:" + debateId));

    Debate debate = debateDao.loadDebate(articleId, debateId);
    if (!debate.canEdit(editor)) {
      throw new AccessDeniedException("no permission to edit debate:" + debate.getDebateId());
    }
    debateDao.changeContent(articleId, debateId, content);

    return debateDao.loadDebate(articleId, debateId).getRenderContent();
  }

  @Override
  public Debate debate(Zone zone,
      FlakeId articleId,
      @Nullable FlakeId parentDebateId,
      Authorization debaterAuth,
      String content) {
    //creating debate should not use cache
    ZoneInfo zoneInfo = zoneDao.loadZoneWithoutCache(zone);
    Article article = articleDao.loadArticle(zoneInfo.getZone(), articleId);

    Account debater = accountDao.strongVerifyAccount(debaterAuth)
        .filter(zoneInfo::canDebate)
        .orElseThrow(() -> new AccessDeniedException("no write to debate at zone:"
            + article.getZone()));

    Debate parent = Optional.ofNullable(parentDebateId)
        .flatMap(pId -> debateDao.findDebate(article.getArticleId(), pId))
        .orElse(null);
    Debate debate = debateDao.create(article, parent, content, debater, Instant.now());

    //may improve later to make it async, but async has transaction problem
    articleDao.increaseDebateCount(article);
    accountDao.increaseDebateCount(debater);
    return debate;
  }

  /**
   * tree structure, but flatten to list, with order by vote score
   */
  @Override
  public List<Debate> listHotDebates(Zone zone, FlakeId articleId, int offset) {
    //TODO cache
    //TODO paging
    //TODO order by rank
    //TODO do not use offset, use start item instead
    return debateDao.listTreeByArticle(articleId);
  }

  @Override
  public List<Article> listHotZoneArticles(Zone zone, FlakeId startArticleId) {
    //TODO cache
    return articleDao.listZoneHotArticles(zone, startArticleId, PAGE_SIZE);
  }

  @Override
  public List<Article> listLatestArticles(@Nullable FlakeId startArticleId) {
    return articleDao.listArticlesDesc(startArticleId, PAGE_SIZE);
  }

  @Override
  public List<Article> listTopArticles(@Nullable FlakeId startArticleId) {
    //TODO cache
    return articleDao.listHotArticlesExcludeHidden(startArticleId, PAGE_SIZE);
  }
}

@@ -2603,50 +2603,106 @@
     
-if (!
+accountDao.strongVerifyAccount(editor)%0A        .filter(
 debate
-.
+::
 canEdit
-(editor)) %7B%0A      throw
+)%0A        .orElseThrow(() -%3E
  new
@@ -2747,32 +2747,44 @@
 to edit debate:%22
+%0A           
  + debate.getDeb
@@ -2787,31 +2787,27 @@
 tDebateId())
+)
 ;%0A
-    %7D
 %0A    return 
@@ -3006,172 +3006,8 @@
  %7B%0A%0A
-    Account editor = accountDao.strongVerifyAccount(editorAuth)%0A        .orElseThrow(() -%3E new AccessDeniedException(%22no permission to edit debate:%22 + debateId));%0A%0A
     
@@ -3073,50 +3073,110 @@
     
-if (!
+accountDao.strongVerifyAccount(editorAuth)%0A        .filter(
 debate
-.
+::
 canEdit
-(editor)) %7B%0A      throw
+)%0A        .orElseThrow(() -%3E
  new
@@ -3242,30 +3242,14 @@
 bate
-.getDebate
 Id
-(
 ));%0A
-    %7D
 %0A   

combine edit permission check with auth check optional