package com.starry.admin.api;

import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.starry.admin.common.apitest.ApiTestDataSeeder;
import com.starry.admin.modules.order.module.constant.OrderConstant;
import com.starry.admin.modules.order.module.entity.PlayOrderInfoEntity;
import com.starry.admin.modules.order.service.IPlayOrderEvaluateInfoService;
import com.starry.admin.modules.order.service.IPlayOrderInfoService;
import com.starry.admin.utils.SecurityUtils;
import com.starry.common.constant.Constants;
import com.starry.common.context.CustomSecurityContextHolder;
import com.starry.common.utils.IdUtils;
import java.time.LocalDateTime;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MvcResult;

class WxCustomOrderEvaluationApiTest extends WxCustomOrderApiTestSupport {

    private static final String EVALUATION_MESSAGE = "评价成功";
    private String customerToken;

    @Autowired
    private IPlayOrderEvaluateInfoService orderEvaluateInfoService;

    @Autowired
    private IPlayOrderInfoService orderInfoService;

    @BeforeEach
    void setUp() {
        ensureTenantContext();
        resetCustomerBalance();
        customerToken = wxTokenService.createWxUserToken(ApiTestDataSeeder.DEFAULT_CUSTOMER_ID);
        customUserInfoService.updateTokenById(ApiTestDataSeeder.DEFAULT_CUSTOMER_ID, customerToken);
    }

    @AfterEach
    void tearDown() {
        CustomSecurityContextHolder.remove();
    }

    @Test
    void customerCanEvaluateCompletedOrderAndRetrieveResult() throws Exception {
        ensureTenantContext();
        String remark = "evaluate-" + LocalDateTime.now();
        String orderId = createRandomOrder(remark);

        ensureTenantContext();
        orderInfoService.lambdaUpdate()
                .eq(PlayOrderInfoEntity::getId, orderId)
                .set(PlayOrderInfoEntity::getAcceptBy, ApiTestDataSeeder.DEFAULT_CLERK_ID)
                .set(PlayOrderInfoEntity::getOrderStatus, OrderConstant.OrderStatus.COMPLETED.getCode())
                .update();

        ObjectNode payload = objectMapper.createObjectNode();
        payload.put("orderId", orderId);
        payload.put("anonymous", "1");
        payload.put("evaluateLevel", 5);
        String evaluationSuffix = IdUtils.getUuid();
        String evaluationText = "API评价-" + evaluationSuffix;
        payload.put("evaluateCon", evaluationText);

        mockMvc.perform(post("/wx/custom/order/evaluate/add")
                        .header(USER_HEADER, DEFAULT_USER)
                        .header(TENANT_HEADER, DEFAULT_TENANT)
                        .header(Constants.CUSTOM_USER_LOGIN_TOKEN, Constants.TOKEN_PREFIX + customerToken)
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(payload.toString()))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200))
                .andExpect(jsonPath("$.data").value(EVALUATION_MESSAGE));

        ensureTenantContext();
        assertThat(orderEvaluateInfoService.queryCustomToOrderEvaluateInfo(
                        ApiTestDataSeeder.DEFAULT_CUSTOMER_ID, orderId))
                .isNotNull();

        MvcResult result = mockMvc.perform(get("/wx/custom/order/evaluate/queryByOrderId")
                        .param("id", orderId)
                        .header(USER_HEADER, DEFAULT_USER)
                        .header(TENANT_HEADER, DEFAULT_TENANT)
                        .header(Constants.CUSTOM_USER_LOGIN_TOKEN, Constants.TOKEN_PREFIX + customerToken))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200))
                .andReturn();

        JsonNode data = objectMapper.readTree(result.getResponse().getContentAsString()).path("data");
        assertThat(data.path("id").asText()).isNotBlank();
        assertThat(data.path("evaluateLevel").asInt()).isEqualTo(5);
        assertThat(data.path("anonymous").asText()).isEqualTo("1");
        assertThat(data.path("evaluateCon").asText()).endsWith(evaluationSuffix);
    }

    private String createRandomOrder(String remark) throws Exception {
        ensureTenantContext();

        ObjectNode payload = objectMapper.createObjectNode();
        payload.put("sex", OrderConstant.Gender.FEMALE.getCode());
        payload.put("levelId", ApiTestDataSeeder.DEFAULT_CLERK_LEVEL_ID);
        payload.put("commodityId", ApiTestDataSeeder.DEFAULT_COMMODITY_ID);
        payload.put("commodityQuantity", 1);
        payload.put("weiChatCode", "apitest-customer-wx");
        payload.put("excludeHistory", "0");
        payload.set("couponIds", objectMapper.createArrayNode());
        payload.put("remark", remark);

        mockMvc.perform(post("/wx/custom/order/random")
                        .header(USER_HEADER, DEFAULT_USER)
                        .header(TENANT_HEADER, DEFAULT_TENANT)
                        .header(Constants.CUSTOM_USER_LOGIN_TOKEN, Constants.TOKEN_PREFIX + customerToken)
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(payload.toString()))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200));

        ensureTenantContext();
        PlayOrderInfoEntity order = orderInfoService.lambdaQuery()
                .eq(PlayOrderInfoEntity::getPurchaserBy, ApiTestDataSeeder.DEFAULT_CUSTOMER_ID)
                .eq(PlayOrderInfoEntity::getRemark, remark)
                .orderByDesc(PlayOrderInfoEntity::getCreatedTime)
                .last("limit 1")
                .one();
        assertThat(order).isNotNull();
        return order.getId();
    }
}
