package com.starry.admin.api;

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.ObjectMapper;
import com.starry.admin.common.apitest.ApiTestDataSeeder;
import com.starry.admin.modules.custom.module.entity.PlayCustomGiftInfoEntity;
import com.starry.admin.modules.custom.service.IPlayCustomGiftInfoService;
import com.starry.admin.modules.order.module.constant.OrderConstant;
import com.starry.admin.modules.order.module.entity.PlayOrderInfoEntity;
import com.starry.admin.modules.shop.module.entity.PlayClerkGiftInfoEntity;
import com.starry.admin.modules.shop.module.entity.PlayGiftInfoEntity;
import com.starry.admin.modules.shop.service.IPlayClerkGiftInfoService;
import com.starry.admin.modules.shop.service.IPlayGiftInfoService;
import com.starry.admin.modules.withdraw.entity.EarningsLineEntity;
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.math.BigDecimal;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;

class WxCustomGiftOrderApiTest extends WxCustomOrderApiTestSupport {

    @Autowired
    private IPlayGiftInfoService playGiftInfoService;

    @Autowired
    private IPlayCustomGiftInfoService playCustomGiftInfoService;

    @Autowired
    private IPlayClerkGiftInfoService playClerkGiftInfoService;

    private final ObjectMapper objectMapper = new ObjectMapper();

    @Test
    void giftOrderFailsWhenBalanceInsufficient() throws Exception {
        SecurityUtils.setTenantId(ApiTestDataSeeder.DEFAULT_TENANT_ID);
        String remark = "API gift insufficient " + IdUtils.getUuid();
        try {
            setCustomerBalance(BigDecimal.ZERO);
            String customerToken = wxTokenService.createWxUserToken(ApiTestDataSeeder.DEFAULT_CUSTOMER_ID);
            customUserInfoService.updateTokenById(ApiTestDataSeeder.DEFAULT_CUSTOMER_ID, customerToken);

            String payload = "{" +
                    "\"clerkId\":\"" + ApiTestDataSeeder.DEFAULT_CLERK_ID + "\"," +
                    "\"giftId\":\"" + ApiTestDataSeeder.DEFAULT_GIFT_ID + "\"," +
                    "\"giftQuantity\":1," +
                    "\"weiChatCode\":\"apitest-customer-wx\"," +
                    "\"remark\":\"" + remark + "\"" +
                    "}";

            mockMvc.perform(post("/wx/custom/order/gift")
                            .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))
                    .andExpect(status().isOk())
                    .andExpect(jsonPath("$.code").value(998));

            ensureTenantContext();
            long count = playOrderInfoService.lambdaQuery()
                    .eq(PlayOrderInfoEntity::getPurchaserBy, ApiTestDataSeeder.DEFAULT_CUSTOMER_ID)
                    .eq(PlayOrderInfoEntity::getRemark, remark)
                    .count();
            Assertions.assertThat(count).isZero();
        } finally {
            CustomSecurityContextHolder.remove();
        }
    }

    @Test
    // 测试用例：用户余额充足且携带有效登录态时，请求 /wx/custom/order/gift 下单指定礼物，
    // 期望生成已完成的礼物奖励订单、产生对应收益记录，同时校验用户/陪玩师礼物计数与账户余额随订单金额同步更新。
    void giftOrderCreatesCompletedRewardAndUpdatesGiftCounters() throws Exception {
        SecurityUtils.setTenantId(ApiTestDataSeeder.DEFAULT_TENANT_ID);
        try {
            resetCustomerBalance();
            String customerToken = wxTokenService.createWxUserToken(ApiTestDataSeeder.DEFAULT_CUSTOMER_ID);
            customUserInfoService.updateTokenById(ApiTestDataSeeder.DEFAULT_CUSTOMER_ID, customerToken);

            PlayGiftInfoEntity gift = playGiftInfoService.selectPlayGiftInfoById(ApiTestDataSeeder.DEFAULT_GIFT_ID);
            Assertions.assertThat(gift).as("seeded gift should exist").isNotNull();

            int giftQuantity = 2;
            String remark = "API gift order " + IdUtils.getUuid();
            BigDecimal totalAmount = gift.getPrice().multiply(BigDecimal.valueOf(giftQuantity));
            BigDecimal initialBalance = customUserInfoService.getById(ApiTestDataSeeder.DEFAULT_CUSTOMER_ID).getAccountBalance();

            String payload = "{" +
                    "\"clerkId\":\"" + ApiTestDataSeeder.DEFAULT_CLERK_ID + "\"," +
                    "\"giftId\":\"" + ApiTestDataSeeder.DEFAULT_GIFT_ID + "\"," +
                    "\"giftQuantity\":" + giftQuantity + "," +
                    "\"weiChatCode\":\"apitest-customer-wx\"," +
                    "\"remark\":\"" + remark + "\"" +
                    "}";

            String response = mockMvc.perform(post("/wx/custom/order/gift")
                            .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))
                    .andExpect(status().isOk())
                    .andExpect(jsonPath("$.code").value(200))
                    .andExpect(jsonPath("$.data").isNotEmpty())
                    .andReturn()
                    .getResponse()
                    .getContentAsString();

            JsonNode root = objectMapper.readTree(response);
            String orderId = root.path("data").asText();

            ensureTenantContext();
            PlayOrderInfoEntity order = playOrderInfoService.selectOrderInfoById(orderId);
            Assertions.assertThat(order).isNotNull();
            Assertions.assertThat(order.getRemark()).isEqualTo(remark);
            Assertions.assertThat(order.getOrderStatus()).isEqualTo(OrderConstant.OrderStatus.COMPLETED.getCode());
            Assertions.assertThat(order.getPlaceType()).isEqualTo(OrderConstant.PlaceType.REWARD.getCode());
            Assertions.assertThat(order.getRewardType()).isEqualTo(OrderConstant.RewardType.GIFT.getCode());
            Assertions.assertThat(order.getFinalAmount()).isEqualByComparingTo(totalAmount);

            ensureTenantContext();
            long earningsCount = earningsService.lambdaQuery()
                    .eq(EarningsLineEntity::getOrderId, orderId)
                    .count();
            Assertions.assertThat(earningsCount).isEqualTo(1);

            ensureTenantContext();
            PlayCustomGiftInfoEntity customerGift = playCustomGiftInfoService.lambdaQuery()
                    .eq(PlayCustomGiftInfoEntity::getCustomId, ApiTestDataSeeder.DEFAULT_CUSTOMER_ID)
                    .eq(PlayCustomGiftInfoEntity::getGiffId, ApiTestDataSeeder.DEFAULT_GIFT_ID)
                    .one();
            Assertions.assertThat(customerGift).isNotNull();
            Assertions.assertThat(customerGift.getGiffNumber()).isEqualTo((long) giftQuantity);

            ensureTenantContext();
            PlayClerkGiftInfoEntity clerkGift = playClerkGiftInfoService.lambdaQuery()
                    .eq(PlayClerkGiftInfoEntity::getClerkId, ApiTestDataSeeder.DEFAULT_CLERK_ID)
                    .eq(PlayClerkGiftInfoEntity::getGiffId, ApiTestDataSeeder.DEFAULT_GIFT_ID)
                    .one();
            Assertions.assertThat(clerkGift).isNotNull();
            Assertions.assertThat(clerkGift.getGiffNumber()).isEqualTo((long) giftQuantity);

            ensureTenantContext();
            BigDecimal finalBalance = customUserInfoService.getById(ApiTestDataSeeder.DEFAULT_CUSTOMER_ID)
                    .getAccountBalance();
            Assertions.assertThat(finalBalance).isEqualByComparingTo(initialBalance.subtract(totalAmount));
        } finally {
            CustomSecurityContextHolder.remove();
        }
    }
}
