package com.ycwl.basic.pricing.service;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.ycwl.basic.pricing.dto.ProductItem;
import com.ycwl.basic.pricing.entity.PriceCouponConfig;
import com.ycwl.basic.pricing.enums.CouponType;
import com.ycwl.basic.pricing.enums.ProductType;
import com.ycwl.basic.pricing.service.impl.CouponServiceImpl;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import static org.junit.jupiter.api.Assertions.*;

/**
 * 优惠券溢出问题测试
 * 测试多SKU场景下优惠券优惠金额不会溢出到不适用的商品
 */
@ExtendWith(MockitoExtension.class)
@DisplayName("优惠券溢出问题测试")
class CouponOverflowTest {

    @InjectMocks
    private CouponServiceImpl couponService;

    @Mock
    private ObjectMapper objectMapper;

    @BeforeEach
    void setUp() {
        // 使用真实的ObjectMapper,避免mock导致的问题
        couponService = new CouponServiceImpl(null, null, new ObjectMapper());
    }

    @Test
    @DisplayName("固定金额优惠券 - 多SKU场景优惠金额超过适用商品总价应被截断")
    void testFixedAmountCoupon_MultiSKU_DiscountExceedsApplicableTotal() {
        // 准备商品列表: PHOTO_PRINT(10元) + PHOTO_PRINT_MU(3元)
        List<ProductItem> products = Arrays.asList(
                createProductItem(ProductType.PHOTO_PRINT, new BigDecimal("10.00")),
                createProductItem(ProductType.PHOTO_PRINT_MU, new BigDecimal("3.00"))
        );
        BigDecimal totalAmount = new BigDecimal("13.00");

        // 优惠券配置: 满0减5元,仅适用于PHOTO_PRINT_MU
        PriceCouponConfig coupon = createCoupon(
                CouponType.FIXED_AMOUNT,
                new BigDecimal("5.00"),
                BigDecimal.ZERO,
                "[\"PHOTO_PRINT_MU\"]"
        );

        // 执行计算
        BigDecimal discount = couponService.calculateCouponDiscount(coupon, products, totalAmount);

        // 验证: 优惠金额应为3元(适用商品总价),而非5元
        assertTrue(discount.compareTo(new BigDecimal("3.00")) == 0, "优惠金额应被截断为适用商品总价");
    }

    @Test
    @DisplayName("固定金额优惠券 - 优惠金额小于适用商品总价应正常优惠")
    void testFixedAmountCoupon_MultiSKU_DiscountWithinApplicableTotal() {
        // 准备商品列表: PHOTO_PRINT(10元) + PHOTO_PRINT_MU(8元)
        List<ProductItem> products = Arrays.asList(
                createProductItem(ProductType.PHOTO_PRINT, new BigDecimal("10.00")),
                createProductItem(ProductType.PHOTO_PRINT_MU, new BigDecimal("8.00"))
        );
        BigDecimal totalAmount = new BigDecimal("18.00");

        // 优惠券配置: 满0减5元,仅适用于PHOTO_PRINT_MU
        PriceCouponConfig coupon = createCoupon(
                CouponType.FIXED_AMOUNT,
                new BigDecimal("5.00"),
                BigDecimal.ZERO,
                "[\"PHOTO_PRINT_MU\"]"
        );

        // 执行计算
        BigDecimal discount = couponService.calculateCouponDiscount(coupon, products, totalAmount);

        // 验证: 优惠金额应为5元(配置值)
        assertTrue(discount.compareTo(new BigDecimal("5.00")) == 0, "优惠金额未超过适用商品总价,应为配置值");
    }

    @Test
    @DisplayName("百分比优惠券 - 应基于适用商品总价计算而非购物车总价")
    void testPercentageCoupon_MultiSKU_CalculateOnApplicableTotal() {
        // 准备商品列表: PHOTO_PRINT(100元) + PHOTO_PRINT_MU(50元)
        List<ProductItem> products = Arrays.asList(
                createProductItem(ProductType.PHOTO_PRINT, new BigDecimal("100.00")),
                createProductItem(ProductType.PHOTO_PRINT_MU, new BigDecimal("50.00"))
        );
        BigDecimal totalAmount = new BigDecimal("150.00");

        // 优惠券配置: 9折,仅适用于PHOTO_PRINT_MU
        PriceCouponConfig coupon = createCoupon(
                CouponType.PERCENTAGE,
                new BigDecimal("10.00"),  // 10% off = 9折
                BigDecimal.ZERO,
                "[\"PHOTO_PRINT_MU\"]"
        );

        // 执行计算
        BigDecimal discount = couponService.calculateCouponDiscount(coupon, products, totalAmount);

        // 验证: 优惠金额应为5元(50 * 10% = 5),而非15元(150 * 10% = 15)
        assertTrue(discount.compareTo(new BigDecimal("5.00")) == 0, "百分比优惠应基于适用商品总价计算");
    }

    @Test
    @DisplayName("百分比优惠券 - 计算结果超过适用商品总价应被截断")
    void testPercentageCoupon_MultiSKU_DiscountExceedsApplicableTotal() {
        // 准备商品列表: PHOTO_PRINT(100元) + PHOTO_PRINT_MU(3元)
        List<ProductItem> products = Arrays.asList(
                createProductItem(ProductType.PHOTO_PRINT, new BigDecimal("100.00")),
                createProductItem(ProductType.PHOTO_PRINT_MU, new BigDecimal("3.00"))
        );
        BigDecimal totalAmount = new BigDecimal("103.00");

        // 优惠券配置: 5折,仅适用于PHOTO_PRINT_MU
        PriceCouponConfig coupon = createCoupon(
                CouponType.PERCENTAGE,
                new BigDecimal("50.00"),  // 50% off = 5折
                BigDecimal.ZERO,
                "[\"PHOTO_PRINT_MU\"]"
        );

        // 执行计算
        BigDecimal discount = couponService.calculateCouponDiscount(coupon, products, totalAmount);

        // 验证: 理论优惠1.5元(3 * 50% = 1.5),实际不应超过3元
        assertTrue(discount.compareTo(new BigDecimal("3.00")) <= 0, "优惠金额不应超过适用商品总价");
    }

    @Test
    @DisplayName("无商品类型限制的优惠券 - 应用于所有商品")
    void testCoupon_NoProductTypeRestriction() {
        // 准备商品列表: PHOTO_PRINT(10元) + PHOTO_PRINT_MU(8元)
        List<ProductItem> products = Arrays.asList(
                createProductItem(ProductType.PHOTO_PRINT, new BigDecimal("10.00")),
                createProductItem(ProductType.PHOTO_PRINT_MU, new BigDecimal("8.00"))
        );
        BigDecimal totalAmount = new BigDecimal("18.00");

        // 优惠券配置: 满0减5元,无商品类型限制
        PriceCouponConfig coupon = createCoupon(
                CouponType.FIXED_AMOUNT,
                new BigDecimal("5.00"),
                BigDecimal.ZERO,
                null  // 无商品类型限制
        );

        // 执行计算
        BigDecimal discount = couponService.calculateCouponDiscount(coupon, products, totalAmount);

        // 验证: 优惠金额应为5元
        assertTrue(discount.compareTo(new BigDecimal("5.00")) == 0, "无商品类型限制时应应用于所有商品");
    }

    @Test
    @DisplayName("无商品类型限制的优惠券 - 优惠金额超过总价应被截断")
    void testCoupon_NoProductTypeRestriction_ExceedsTotalPrice() {
        // 准备商品列表: PHOTO_PRINT(2元) + PHOTO_PRINT_MU(1元)
        List<ProductItem> products = Arrays.asList(
                createProductItem(ProductType.PHOTO_PRINT, new BigDecimal("2.00")),
                createProductItem(ProductType.PHOTO_PRINT_MU, new BigDecimal("1.00"))
        );
        BigDecimal totalAmount = new BigDecimal("3.00");

        // 优惠券配置: 满0减10元,无商品类型限制
        PriceCouponConfig coupon = createCoupon(
                CouponType.FIXED_AMOUNT,
                new BigDecimal("10.00"),
                BigDecimal.ZERO,
                null
        );

        // 执行计算
        BigDecimal discount = couponService.calculateCouponDiscount(coupon, products, totalAmount);

        // 验证: 优惠金额应被截断为3元
        assertTrue(discount.compareTo(new BigDecimal("3.00")) == 0, "优惠金额应被截断为商品总价");
    }

    @Test
    @DisplayName("多商品类型限制的优惠券 - 计算多个适用商品的总价")
    void testCoupon_MultipleProductTypeRestriction() {
        // 准备商品列表: PHOTO_PRINT(10元) + PHOTO_PRINT_MU(8元) + VLOG_VIDEO(20元)
        List<ProductItem> products = Arrays.asList(
                createProductItem(ProductType.PHOTO_PRINT, new BigDecimal("10.00")),
                createProductItem(ProductType.PHOTO_PRINT_MU, new BigDecimal("8.00")),
                createProductItem(ProductType.VLOG_VIDEO, new BigDecimal("20.00"))
        );
        BigDecimal totalAmount = new BigDecimal("38.00");

        // 优惠券配置: 满0减10元,适用于PHOTO_PRINT和PHOTO_PRINT_MU
        PriceCouponConfig coupon = createCoupon(
                CouponType.FIXED_AMOUNT,
                new BigDecimal("10.00"),
                BigDecimal.ZERO,
                "[\"PHOTO_PRINT\",\"PHOTO_PRINT_MU\"]"
        );

        // 执行计算
        BigDecimal discount = couponService.calculateCouponDiscount(coupon, products, totalAmount);

        // 验证: 优惠金额应为10元(适用商品总价18元,优惠10元未超限)
        assertTrue(discount.compareTo(new BigDecimal("10.00")) == 0, "多商品类型限制时应正确计算适用商品总价");
    }

    @Test
    @DisplayName("边界情况 - 适用商品总价为0")
    void testCoupon_ApplicableTotalIsZero() {
        // 准备商品列表: 仅有PHOTO_PRINT(10元)
        List<ProductItem> products = Arrays.asList(
                createProductItem(ProductType.PHOTO_PRINT, new BigDecimal("10.00"))
        );
        BigDecimal totalAmount = new BigDecimal("10.00");

        // 优惠券配置: 满0减5元,仅适用于PHOTO_PRINT_MU(购物车中无此商品)
        PriceCouponConfig coupon = createCoupon(
                CouponType.FIXED_AMOUNT,
                new BigDecimal("5.00"),
                BigDecimal.ZERO,
                "[\"PHOTO_PRINT_MU\"]"
        );

        // 执行计算
        BigDecimal discount = couponService.calculateCouponDiscount(coupon, products, totalAmount);

        // 验证: 优惠金额应为0(适用商品总价为0,自动截断)
        assertTrue(new BigDecimal("0.00").compareTo(discount) == 0, "适用商品总价为0时优惠金额应为0");
    }

    @Test
    @DisplayName("边界情况 - 优惠金额刚好等于适用商品总价")
    void testCoupon_DiscountEqualsApplicableTotal() {
        // 准备商品列表: PHOTO_PRINT(10元) + PHOTO_PRINT_MU(5元)
        List<ProductItem> products = Arrays.asList(
                createProductItem(ProductType.PHOTO_PRINT, new BigDecimal("10.00")),
                createProductItem(ProductType.PHOTO_PRINT_MU, new BigDecimal("5.00"))
        );
        BigDecimal totalAmount = new BigDecimal("15.00");

        // 优惠券配置: 满0减5元,仅适用于PHOTO_PRINT_MU
        PriceCouponConfig coupon = createCoupon(
                CouponType.FIXED_AMOUNT,
                new BigDecimal("5.00"),
                BigDecimal.ZERO,
                "[\"PHOTO_PRINT_MU\"]"
        );

        // 执行计算
        BigDecimal discount = couponService.calculateCouponDiscount(coupon, products, totalAmount);

        // 验证: 优惠金额应为5元
        assertTrue(discount.compareTo(new BigDecimal("5.00")) == 0, "优惠金额刚好等于适用商品总价时应正常优惠");
    }

    @Test
    @DisplayName("百分比优惠券 - 带最大优惠金额限制且超过适用商品总价")
    void testPercentageCoupon_WithMaxDiscount_ExceedsApplicableTotal() {
        // 准备商品列表: PHOTO_PRINT(100元) + PHOTO_PRINT_MU(3元)
        List<ProductItem> products = Arrays.asList(
                createProductItem(ProductType.PHOTO_PRINT, new BigDecimal("100.00")),
                createProductItem(ProductType.PHOTO_PRINT_MU, new BigDecimal("3.00"))
        );
        BigDecimal totalAmount = new BigDecimal("103.00");

        // 优惠券配置: 5折,最大优惠10元,仅适用于PHOTO_PRINT_MU
        PriceCouponConfig coupon = createCoupon(
                CouponType.PERCENTAGE,
                new BigDecimal("50.00"),
                BigDecimal.ZERO,
                "[\"PHOTO_PRINT_MU\"]"
        );
        coupon.setMaxDiscount(new BigDecimal("10.00"));

        // 执行计算
        BigDecimal discount = couponService.calculateCouponDiscount(coupon, products, totalAmount);

        // 验证: 理论优惠1.5元(3 * 50%),不受最大优惠10元限制,但仍不超过适用商品总价3元
        assertTrue(discount.compareTo(new BigDecimal("3.00")) <= 0, "优惠金额不应超过适用商品总价");
        assertTrue(discount.compareTo(new BigDecimal("1.50")) >= 0, "优惠金额应为计算值1.5元");
    }

    // ========== 辅助方法 ==========

    /**
     * 创建商品项
     */
    private ProductItem createProductItem(ProductType productType, BigDecimal subtotal) {
        ProductItem item = new ProductItem();
        item.setProductType(productType);
        item.setSubtotal(subtotal);
        item.setQuantity(1);
        item.setUnitPrice(subtotal);
        return item;
    }

    /**
     * 创建优惠券配置
     */
    private PriceCouponConfig createCoupon(CouponType type, BigDecimal value,
                                          BigDecimal minAmount, String applicableProducts) {
        PriceCouponConfig coupon = new PriceCouponConfig();
        coupon.setId(1L);
        coupon.setCouponName("测试优惠券");
        coupon.setCouponType(type);
        coupon.setDiscountValue(value);
        coupon.setMinAmount(minAmount);
        coupon.setApplicableProducts(applicableProducts);
        coupon.setIsActive(true);
        return coupon;
    }
}
