package com.starry.admin.api;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.starry.admin.common.apitest.ApiTestDataSeeder;
import com.starry.admin.modules.clerk.module.entity.PlayClerkLevelInfoEntity;
import com.starry.admin.modules.clerk.module.entity.PlayClerkUserInfoEntity;
import com.starry.admin.modules.clerk.service.IPlayClerkLevelInfoService;
import com.starry.admin.modules.clerk.service.IPlayClerkUserInfoService;
import com.starry.admin.modules.custom.module.entity.PlayCustomUserInfoEntity;
import com.starry.admin.modules.custom.service.IPlayCustomUserInfoService;
import com.starry.admin.modules.order.module.constant.OrderConstant;
import com.starry.admin.modules.order.module.constant.OrderConstant.PlaceType;
import com.starry.admin.modules.order.service.IPlayOrderInfoService;
import com.starry.admin.modules.shop.module.constant.CouponUseState;
import com.starry.admin.modules.shop.module.entity.PlayCommodityAndLevelInfoEntity;
import com.starry.admin.modules.shop.module.entity.PlayCouponDetailsEntity;
import com.starry.admin.modules.shop.module.entity.PlayCouponInfoEntity;
import com.starry.admin.modules.shop.module.enums.CouponClaimConditionType;
import com.starry.admin.modules.shop.module.enums.CouponDiscountType;
import com.starry.admin.modules.shop.module.enums.CouponOnlineState;
import com.starry.admin.modules.shop.module.enums.CouponValidityPeriodType;
import com.starry.admin.modules.shop.service.IPlayCommodityAndLevelInfoService;
import com.starry.admin.modules.shop.service.IPlayCouponDetailsService;
import com.starry.admin.modules.shop.service.IPlayCouponInfoService;
import com.starry.admin.modules.weichat.service.WxTokenService;
import com.starry.admin.modules.withdraw.service.IEarningsService;
import com.starry.admin.modules.withdraw.service.IFreezePolicyService;
import com.starry.admin.utils.SecurityUtils;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.format.DateTimeFormatter;
import java.util.Collections;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.util.StringUtils;

abstract class WxCustomOrderApiTestSupport extends AbstractApiTest {

    protected static final DateTimeFormatter DATE_TIME_FORMATTER =
            DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    protected final ObjectMapper objectMapper = new ObjectMapper();

    @Autowired
    protected WxTokenService wxTokenService;

    @Autowired
    protected IPlayOrderInfoService playOrderInfoService;

    @Autowired
    protected IPlayCustomUserInfoService customUserInfoService;

    @Autowired
    protected IPlayClerkUserInfoService clerkUserInfoService;

    @Autowired
    protected IEarningsService earningsService;

    @Autowired
    protected IFreezePolicyService freezePolicyService;

    @Autowired
    protected IPlayClerkLevelInfoService clerkLevelInfoService;

    @Autowired
    protected IPlayCouponInfoService couponInfoService;

    @Autowired
    protected IPlayCouponDetailsService couponDetailsService;

    @Autowired
    protected IPlayCommodityAndLevelInfoService commodityAndLevelInfoService;

    protected void resetCustomerBalance() {
        ensureDefaultClerkLevelBinding();
        BigDecimal balance = new BigDecimal("200.00");
        customUserInfoService.updateAccountBalanceById(ApiTestDataSeeder.DEFAULT_CUSTOMER_ID, balance);
        customUserInfoService.lambdaUpdate()
                .set(PlayCustomUserInfoEntity::getAccumulatedConsumptionAmount, BigDecimal.ZERO)
                .set(PlayCustomUserInfoEntity::getAccumulatedRechargeAmount, balance)
                .set(PlayCustomUserInfoEntity::getAccountState, "1")
                .set(PlayCustomUserInfoEntity::getSubscribeState, "1")
                .set(PlayCustomUserInfoEntity::getPurchaseState, "1")
                .set(PlayCustomUserInfoEntity::getMobilePhoneState, "1")
                .eq(PlayCustomUserInfoEntity::getId, ApiTestDataSeeder.DEFAULT_CUSTOMER_ID)
                .update();
    }

    protected void setCustomerBalance(BigDecimal balance) {
        ensureDefaultClerkLevelBinding();
        customUserInfoService.updateAccountBalanceById(ApiTestDataSeeder.DEFAULT_CUSTOMER_ID, balance);
    }

    protected void ensureTenantContext() {
        SecurityUtils.setTenantId(ApiTestDataSeeder.DEFAULT_TENANT_ID);
    }

    protected String createCouponId(String prefix) {
        String raw = com.starry.common.utils.IdUtils.getUuid().replace("-", "");
        String suffix = raw.length() > 10 ? raw.substring(0, 10) : raw;
        return prefix + suffix;
    }

    protected String ensureFixedReductionCoupon(
            String couponId,
            OrderConstant.PlaceType placeType,
            BigDecimal discountAmount,
            BigDecimal minAmount,
            String attributionDiscounts) {
        ensureTenantContext();
        PlayCouponInfoEntity existing = couponInfoService.getById(couponId);
        if (existing != null) {
            return couponId;
        }

        PlayCouponInfoEntity coupon = new PlayCouponInfoEntity();
        coupon.setId(couponId);
        coupon.setTenantId(ApiTestDataSeeder.DEFAULT_TENANT_ID);
        coupon.setCouponName(couponId);
        coupon.setValidityPeriodType(CouponValidityPeriodType.PERMANENT.getCode());
        coupon.setUseMinAmount(minAmount);
        coupon.setDiscountType(CouponDiscountType.FULL_REDUCTION.getCode());
        coupon.setDiscountAmount(discountAmount);
        coupon.setDiscountContent("立减" + discountAmount.setScale(2, RoundingMode.HALF_UP) + "元");
        coupon.setAttributionDiscounts(attributionDiscounts);
        coupon.setPlaceType(Collections.singletonList(placeType.getCode()));
        coupon.setClerkType("0");
        coupon.setCouponQuantity(200);
        coupon.setIssuedQuantity(0);
        coupon.setRemainingQuantity(200);
        coupon.setClerkObtainedMaxQuantity(5);
        coupon.setClaimConditionType(CouponClaimConditionType.ALL.code());
        coupon.setCustomLevelCheckType("0");
        coupon.setCustomSexCheckType("0");
        coupon.setCustomLevel(Collections.emptyList());
        coupon.setCustomSex(Collections.emptyList());
        coupon.setCustomFollowStatusCheckType("0");
        coupon.setCustomFollowStatus("1");
        coupon.setNewUser("0");
        coupon.setCouponOnLineState(CouponOnlineState.ONLINE.getCode());
        couponInfoService.save(coupon);
        return couponId;
    }

    protected String claimCouponForOrder(
            String couponInfoId,
            PlaceType placeType,
            String customerToken) throws Exception {
        ensureTenantContext();
        mockMvc.perform(org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get("/wx/coupon/custom/obtainCoupon")
                        .param("id", couponInfoId)
                        .header(USER_HEADER, DEFAULT_USER)
                        .header(TENANT_HEADER, DEFAULT_TENANT)
                        .header(com.starry.common.constant.Constants.CUSTOM_USER_LOGIN_TOKEN,
                                com.starry.common.constant.Constants.TOKEN_PREFIX + customerToken))
                .andExpect(org.springframework.test.web.servlet.result.MockMvcResultMatchers.status().isOk())
                .andExpect(org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath("$.code").value(200))
                .andExpect(org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath("$.data.success").value(true));

        String levelId = placeType == PlaceType.SPECIFIED ? "" : ApiTestDataSeeder.DEFAULT_CLERK_LEVEL_ID;
        String clerkId = placeType == PlaceType.SPECIFIED ? ApiTestDataSeeder.DEFAULT_CLERK_ID : "";
        String queryPayload = "{" +
                "\"commodityId\":\"" + ApiTestDataSeeder.DEFAULT_COMMODITY_ID + "\"," +
                "\"levelId\":\"" + levelId + "\"," +
                "\"clerkId\":\"" + clerkId + "\"," +
                "\"placeType\":\"" + placeType.getCode() + "\"," +
                "\"commodityQuantity\":1" +
                "}";

        MvcResult result = mockMvc.perform(org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post("/wx/coupon/custom/queryByOrder")
                        .header(USER_HEADER, DEFAULT_USER)
                        .header(TENANT_HEADER, DEFAULT_TENANT)
                        .header(com.starry.common.constant.Constants.CUSTOM_USER_LOGIN_TOKEN,
                                com.starry.common.constant.Constants.TOKEN_PREFIX + customerToken)
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(queryPayload))
                .andExpect(org.springframework.test.web.servlet.result.MockMvcResultMatchers.status().isOk())
                .andExpect(org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath("$.code").value(200))
                .andReturn();

        String responseBody = result.getResponse().getContentAsString();
        JsonNode dataNode = objectMapper.readTree(responseBody).path("data");
        if (!dataNode.isArray()) {
            throw new AssertionError("未返回优惠券列表");
        }
        String detailId = null;
        for (JsonNode node : dataNode) {
            if (!couponInfoId.equals(node.path("couponName").asText())) {
                continue;
            }
            if (!"1".equals(node.path("available").asText())) {
                throw new AssertionError("优惠券不可用: " + node.path("reasonForUnavailableUse").asText());
            }
            detailId = node.path("id").asText();
            if (!StringUtils.hasText(detailId)) {
                throw new AssertionError("优惠券详情ID缺失");
            }
            return detailId;
        }
        throw new AssertionError("未在可用列表中找到优惠券 " + couponInfoId + "，响应：" + responseBody);
    }

    protected void assertCouponUsed(String couponDetailId) {
        ensureTenantContext();
        PlayCouponDetailsEntity detail = couponDetailsService.getById(couponDetailId);
        if (detail == null) {
            throw new AssertionError("优惠券详情不存在: " + couponDetailId);
        }
        if (!CouponUseState.USED.getCode().equals(detail.getUseState())) {
            throw new AssertionError("优惠券未标记为已使用");
        }
    }

    private void ensureDefaultClerkLevelBinding() {
        ensureTenantContext();
        PlayClerkUserInfoEntity clerk = clerkUserInfoService.selectById(ApiTestDataSeeder.DEFAULT_CLERK_ID);
        if (!StringUtils.hasText(clerk.getLevelId())
                || !ApiTestDataSeeder.DEFAULT_CLERK_LEVEL_ID.equals(clerk.getLevelId())) {
            boolean updated = clerkUserInfoService.lambdaUpdate()
                    .set(PlayClerkUserInfoEntity::getLevelId, ApiTestDataSeeder.DEFAULT_CLERK_LEVEL_ID)
                    .eq(PlayClerkUserInfoEntity::getId, ApiTestDataSeeder.DEFAULT_CLERK_ID)
                    .eq(PlayClerkUserInfoEntity::getTenantId, ApiTestDataSeeder.DEFAULT_TENANT_ID)
                    .update();
            if (!updated) {
                throw new IllegalStateException("ApiTest fixtures failed to bind default level to test clerk");
            }
            clerk = clerkUserInfoService.selectById(ApiTestDataSeeder.DEFAULT_CLERK_ID);
        }

        long pricingCount = commodityAndLevelInfoService.lambdaQuery()
                .eq(PlayCommodityAndLevelInfoEntity::getCommodityId, ApiTestDataSeeder.DEFAULT_COMMODITY_ID)
                .eq(PlayCommodityAndLevelInfoEntity::getLevelId, ApiTestDataSeeder.DEFAULT_CLERK_LEVEL_ID)
                .count();
        if (pricingCount == 0) {
            PlayCommodityAndLevelInfoEntity mapping = new PlayCommodityAndLevelInfoEntity();
            mapping.setId(com.starry.common.utils.IdUtils.getUuid());
            mapping.setTenantId(ApiTestDataSeeder.DEFAULT_TENANT_ID);
            mapping.setCommodityId(ApiTestDataSeeder.DEFAULT_COMMODITY_ID);
            mapping.setLevelId(ApiTestDataSeeder.DEFAULT_CLERK_LEVEL_ID);
            mapping.setPrice(ApiTestDataSeeder.DEFAULT_COMMODITY_PRICE);
            mapping.setSort(1L);
            commodityAndLevelInfoService.save(mapping);
            pricingCount = commodityAndLevelInfoService.lambdaQuery()
                    .eq(PlayCommodityAndLevelInfoEntity::getCommodityId, ApiTestDataSeeder.DEFAULT_COMMODITY_ID)
                    .eq(PlayCommodityAndLevelInfoEntity::getLevelId, ApiTestDataSeeder.DEFAULT_CLERK_LEVEL_ID)
                    .count();
        }
        if (pricingCount == 0) {
            throw new IllegalStateException("ApiTest fixtures missing commodity pricing for default clerk level");
        }
    }
}
