package com.pingcap.community.blogserver.service;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;

import com.pingcap.community.blogserver.dto.CreatePostDTO;
import com.pingcap.community.blogserver.entity.Category;
import com.pingcap.community.blogserver.entity.Post;
import com.pingcap.community.blogserver.entity.User;
import com.pingcap.community.blogserver.enums.PostOrigin;
import com.pingcap.community.blogserver.enums.PostStatus;
import com.pingcap.community.blogserver.repository.PostEventRepository;
import com.pingcap.community.blogserver.repository.PostRepository;

import java.util.*;
import javax.persistence.EntityManager;

import com.pingcap.community.blogserver.testutils.PopulateDataExtension;
import org.hibernate.Session;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;

@SpringBootTest
@ExtendWith(PopulateDataExtension.class)
public class PostServiceTest {

  private static final User user = new User(1005L);
  @Autowired private PostService service;
  @Autowired private PostRepository postRepository;
  @Autowired private PostEventRepository postEventRepository;
  @Autowired private EntityManager em;

  @BeforeEach
  void initUser() {
    var userId = 1005L;
    user.setId(1005L);
    user.setEnable(true);

    List<GrantedAuthority> authorities = new ArrayList<>();
    authorities.add(new SimpleGrantedAuthority("CREATE_POST"));
    SecurityContextHolder.getContext()
        .setAuthentication(new TestingAuthenticationToken(user, userId, authorities));
  }

  @Test
  public void createPost() {
    actionsTest(
        1,
        () -> {
          CreatePostDTO post = new CreatePostDTO();
          post.setTitle("title");
          post.setContent("[{\"type\": \"heading\", \"depth\": 1, \"children\": [{ \"text\": \"Heading1\" }]}]");
          post.setCategory(new Category(1001L));
          post.setTags(Collections.emptyList());
          post.setOrigin(PostOrigin.ORIGINAL);
          post.setSourceURL(null);
          Post createdPost = service.createPost(user, post);
          assertEquals(PostStatus.DRAFT, createdPost.getStatus());
        });
  }

  private void actionsTest(int actionsCount, Runnable runnable) {
    long beforeEvents = postEventRepository.count();
    runnable.run();
    long afterEvents = postEventRepository.count();
    assertEquals(beforeEvents + actionsCount, afterEvents);
  }

  @Test
  public void cancelPublish() {
    Post post;

    post = newDefaultPost(PostStatus.PENDING);
    postRepository.saveAndFlush(post);
    assertThrows(Exception.class, () -> service.cancelPublishPost(post));

    post.setStatus(PostStatus.PUBLISHED);
    post.setLastPublisher(user);
    post.setPublishedAt(new Date());
    post.setLastPublishedAt(new Date());
    postRepository.saveAndFlush(post);
    actionsTest(
        1,
        () -> {
          service.cancelPublishPost(post);
          assertEquals(PostStatus.DRAFT, post.getStatus());
          assertFalse(((Session) em).isDirty());
        });
  }

  // TODO: add more post action tests

  private static Post newDefaultPost(PostStatus status) {
    Post post = new Post();
    post.setStatus(status);

    post.setTitle("title");
    post.setContent("[{\"type\": \"heading\", \"depth\": 1, \"children\": [{ \"text\": \"Heading1\" }]}]");
    post.setCategory(new Category(1001L));
    post.setTags(Collections.emptySet());
    post.setOrigin(PostOrigin.ORIGINAL);
    post.setSourceURL(null);
    post.setAuthor(user);

    return post;
  }
}
