package com.pingcap.community.blogserver.service;

import com.pingcap.community.blogserver.dto.CreateCommentDTO;
import com.pingcap.community.blogserver.entity.Comment;
import com.pingcap.community.blogserver.entity.Post;
import com.pingcap.community.blogserver.entity.User;
import com.pingcap.community.blogserver.exception.comment.CommentNotFound;
import com.pingcap.community.blogserver.exception.comment.CommentRepliedNotFound;
import com.pingcap.community.blogserver.exception.post.PostNotPublished;
import com.pingcap.community.blogserver.repository.CommentRepository;
import com.pingcap.community.blogserver.vo.comment.CommentItemVO;
import lombok.AllArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

import java.util.Optional;

@AllArgsConstructor
@Service
public class CommentService {

  private final CommentRepository commentRepository;

  public Page<CommentItemVO> getUserComments(Long commenterID, Pageable pageable) {
    Page<Comment> commentPage = commentRepository.findByCommenterIdAndDeletedAtIsNull(commenterID, pageable);
    return wrapperComments(commentPage);
  }

  public Page<CommentItemVO> getUserCommentsByUsername(String username, Pageable pageable) {
    Page<Comment> commentPage = commentRepository.findByCommenterUsernameAndDeletedAtIsNull(username, pageable);
    return wrapperComments(commentPage);
  }

  public Page<CommentItemVO> getPostComments(Long postID, Pageable pageable) {
    Page<Comment> commentPage = commentRepository.findByPostIdAndDeletedAtIsNull(postID, pageable);
    return wrapperComments(commentPage);
  }

  private Page<CommentItemVO> wrapperComments(Page<Comment> commentPage) {
    return commentPage.map((Comment comment) -> {
      CommentItemVO commentItemVO = new CommentItemVO();
      commentItemVO.setId(comment.getId());
      commentItemVO.setContent(comment.getContent());
      commentItemVO.setCommenter(comment.getCommenter());
      commentItemVO.setPost(comment.getPost());
      commentItemVO.setCreatedAt(comment.getCreatedAt());
      commentItemVO.setLastModifiedAt(comment.getLastModifiedAt());

      Comment replyComment = comment.getReplyComment();
      if (replyComment != null) {
        commentItemVO.setRepliedTo(replyComment.getCommenter());
      }

      return commentItemVO;
    });
  }

  public Comment addComment(
    Post post, User user, CreateCommentDTO createCommentDTO
  ) throws CommentRepliedNotFound, PostNotPublished {
    if (!post.isPublic()) {
      throw new PostNotPublished();
    }

    Comment comment = new Comment();

    // Handle the reply comment.
    Long replyCommentID = createCommentDTO.getReplyCommentID();
    int level = 1;
    if (replyCommentID != null) {
      Optional<Comment> replyCommentOptional = commentRepository.findById(replyCommentID);
      if (replyCommentOptional.isEmpty()) {
        throw new CommentRepliedNotFound();
      }
      Comment replyComment = replyCommentOptional.get();
      level = replyComment.getLevel() + 1;
      comment.setReplyComment(replyComment);
    }

    comment.setLevel(level);
    comment.setCommenter(user);
    comment.setContent(createCommentDTO.getContent());
    comment.setPost(post);

    return commentRepository.save(comment);
  }

  public void removeComment(Comment comment) throws CommentNotFound {
    if (comment == null || comment.getDeletedAt() != null) {
      throw new CommentNotFound();
    }

    commentRepository.deleteById(comment.getId());
  }

}
