package com.pingcap.community.blogserver.controller;

import com.pingcap.community.blogserver.testutils.PopulateDataExtension;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.servlet.MockMvc;

import javax.servlet.http.Cookie;

import static com.pingcap.community.blogserver.testutils.RequestPostProcessors.admin1User;
import static com.pingcap.community.blogserver.testutils.RequestPostProcessors.reader1User;
import static org.hamcrest.Matchers.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@AutoConfigureMockMvc
@ExtendWith(PopulateDataExtension.class)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
class NotificationControllerTest {

  @Autowired
  private MockMvc mockMvc;

  @Test
  @Order(101)
  void getOwnNotificationList() throws Exception {
    mockMvc
        .perform(
            get("/api/notifications")
                .queryParam("sort", "createdAt,desc")
                .queryParam("size", "5")
                .cookie(new Cookie("dev_sid", "0"))
                .with(reader1User())
        )
        .andExpect(status().isOk())
        .andExpect(jsonPath("$.content[*].id", containsInRelativeOrder(301, 201, 101)))
        .andExpect(jsonPath("$.content[*].relatedPost.id", containsInRelativeOrder(1, 1, 1)))
        .andExpect(jsonPath("$.content[*].haveRead", containsInRelativeOrder(false, false, true)))
        .andExpect(jsonPath("$.content[*].title", containsInRelativeOrder(notNullValue(), notNullValue(), notNullValue())))
        .andExpect(jsonPath("$.content[*].createdAt", containsInRelativeOrder(notNullValue(), notNullValue(), notNullValue())))
        .andExpect(jsonPath("$.page.number", equalTo(1)))
        .andExpect(jsonPath("$.page.size", equalTo(5)));
  }

  @Test
  @Order(102)
  void getOwnUnreadNotificationList() throws Exception {
    mockMvc
        .perform(
            get("/api/notifications")
                .queryParam("haveRead", "false")
                .queryParam("sort", "createdAt,desc")
                .queryParam("size", "5")
                .cookie(new Cookie("dev_sid", "0"))
                .with(reader1User())
        )
        .andExpect(status().isOk())
        .andExpect(jsonPath("$.content[*].id", containsInRelativeOrder(301, 201)))
        .andExpect(jsonPath("$.content[*].relatedPost.id", containsInRelativeOrder(1, 1)))
        .andExpect(jsonPath("$.content[*].haveRead", containsInRelativeOrder(false, false)))
        .andExpect(jsonPath("$.content[*].title", containsInRelativeOrder(notNullValue(), notNullValue())))
        .andExpect(jsonPath("$.content[*].createdAt", containsInRelativeOrder(notNullValue(), notNullValue())))
        .andExpect(jsonPath("$.page.number", equalTo(1)))
        .andExpect(jsonPath("$.page.size", equalTo(5)));
  }

  @Test
  @Order(103)
  void getOwnLikeNotificationList() throws Exception {
    mockMvc
        .perform(
            get("/api/notifications")
                .queryParam("type", "LIKE")
                .queryParam("sort", "createdAt,desc")
                .queryParam("size", "5")
                .cookie(new Cookie("dev_sid", "0"))
                .with(reader1User())
        )
        .andExpect(status().isOk())
        .andExpect(jsonPath("$.content[*].id", containsInRelativeOrder(101)))
        .andExpect(jsonPath("$.content[*].relatedPost.id", containsInRelativeOrder(1)))
        .andExpect(jsonPath("$.content[*].haveRead", containsInRelativeOrder(true)))
        .andExpect(jsonPath("$.content[*].title", containsInRelativeOrder(notNullValue())))
        .andExpect(jsonPath("$.content[*].createdAt", containsInRelativeOrder(notNullValue())))
        .andExpect(jsonPath("$.page.number", equalTo(1)))
        .andExpect(jsonPath("$.page.size", equalTo(5)));
  }

  @Test
  @Order(104)
  void getOthersNotificationList() throws Exception {
    mockMvc
        .perform(
            get("/api/notifications")
                .queryParam("recipientID", "1016")
                .queryParam("sort", "createdAt,desc")
                .queryParam("size", "5")
                .cookie(new Cookie("dev_sid", "0"))
                .with(admin1User())
        )
        .andExpect(status().isOk())
        .andExpect(jsonPath("$.content[*].id", containsInRelativeOrder(302, 202, 102)))
        .andExpect(jsonPath("$.content[*].relatedPost.id", containsInRelativeOrder(2, 2, 2)))
        .andExpect(jsonPath("$.content[*].haveRead", containsInRelativeOrder(false, false, true)))
        .andExpect(jsonPath("$.content[*].title", containsInRelativeOrder(notNullValue(), notNullValue(), notNullValue())))
        .andExpect(jsonPath("$.content[*].createdAt", containsInRelativeOrder(notNullValue(), notNullValue(), notNullValue())))
        .andExpect(jsonPath("$.page.number", equalTo(1)))
        .andExpect(jsonPath("$.page.size", equalTo(5)));
  }

  @Test
  @Order(201)
  void getOwnNotificationSummary() throws Exception {
    mockMvc
        .perform(
            get("/api/notifications/summary")
                .cookie(new Cookie("dev_sid", "0"))
                .with(reader1User())
        )
        .andExpect(status().isOk())
        .andExpect(jsonPath("$.unreadCount", equalTo(2)))
        .andExpect(jsonPath("$.newCount", equalTo(2)));
  }

  @Test
  @Order(202)
  void getOwnNotificationSummaryWithLastReadTime() throws Exception {
    mockMvc
        .perform(
            get("/api/notifications/summary")
                .queryParam("lastReadDate", "2021-12-15")
                .cookie(new Cookie("dev_sid", "0"))
                .with(reader1User())
        )
        .andExpect(status().isOk())
        .andExpect(jsonPath("$.unreadCount", equalTo(2)))
        .andExpect(jsonPath("$.newCount", equalTo(1)));
  }

  @Test
  @Order(301)
  void readNotification() throws Exception {
    mockMvc
        .perform(
            patch("/api/notifications/201/read")
                .cookie(new Cookie("dev_sid", "0"))
                .with(reader1User())
        )
        .andExpect(status().isOk());
  }
}