package com.starry.admin.modules.shop.service;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;

import com.starry.admin.common.conf.ThreadLocalRequestDetail;
import com.starry.admin.modules.custom.module.entity.PlayCustomUserInfoEntity;
import com.starry.admin.modules.shop.module.constant.CouponUseState;
import com.starry.admin.modules.shop.module.entity.PlayCouponInfoEntity;
import com.starry.admin.modules.shop.module.enums.CouponOnlineState;
import com.starry.admin.modules.shop.module.enums.CouponValidityPeriodType;
import com.starry.admin.modules.shop.module.vo.PlayCommodityInfoVo;
import com.starry.admin.modules.shop.module.vo.PlayCouponDetailsReturnVo;
import com.starry.admin.modules.shop.service.IPlayCommodityInfoService;
import com.starry.admin.modules.shop.service.IPlayCouponDetailsService;
import com.starry.admin.modules.shop.service.IPlayCouponInfoService;
import com.starry.admin.modules.shop.service.impl.PlayCouponInfoServiceImpl;
import com.starry.admin.modules.weichat.controller.WxCouponController;
import com.starry.admin.modules.weichat.entity.WxCouponOrderQueryVo;
import com.starry.admin.modules.weichat.entity.WxCouponOrderReturnVo;
import com.starry.admin.modules.weichat.entity.WxCouponReceiveReturnVo;
import com.starry.common.result.R;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import org.junit.jupiter.api.AfterEach;
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;

@ExtendWith(MockitoExtension.class)
public class CouponWhitelistTest {

    @Test
    @DisplayName("领取校验-白名单：白名单用户可领，非白名单被拒")
    void testWhitelistCheckInService() {
        PlayCouponInfoServiceImpl service = new PlayCouponInfoServiceImpl();

        PlayCouponInfoEntity coupon = new PlayCouponInfoEntity();
        coupon.setClaimConditionType("2");
        coupon.setCustomWhitelist(Arrays.asList("userA", "userB"));

        PlayCustomUserInfoEntity userA = new PlayCustomUserInfoEntity();
        userA.setId("userA");

        PlayCustomUserInfoEntity userX = new PlayCustomUserInfoEntity();
        userX.setId("userX");

        String ok = service.getReasonForNotObtainingCoupons(coupon, userA);
        String deny = service.getReasonForNotObtainingCoupons(coupon, userX);

        assertEquals("", ok, "白名单用户应允许领取");
        assertEquals("非指定用户", deny, "非白名单用户应被拒绝");
    }

    @Mock
    private IPlayCouponDetailsService couponDetailsService;

    @Mock
    private IPlayCouponInfoService couponInfoService;

    @Mock
    private IPlayCommodityInfoService commodityInfoService;

    @InjectMocks
    private WxCouponController wxCouponController;

    @AfterEach
    void clearTL() { ThreadLocalRequestDetail.remove(); }

    @Test
    @DisplayName("列表过滤-白名单：非白名单用户不可见")
    void testWhitelistHiddenInQueryAll() {
        // 当前用户
        PlayCustomUserInfoEntity current = new PlayCustomUserInfoEntity();
        current.setId("uid-1");
        ThreadLocalRequestDetail.setRequestDetail(current);

        // 构造两张券：一张白名单包含uid-1，一张不包含
        PlayCouponInfoEntity visible = new PlayCouponInfoEntity();
        visible.setId("c1");
        visible.setCouponOnLineState("1");
        visible.setClaimConditionType("2");
        visible.setCustomWhitelist(Collections.singletonList("uid-1"));

        PlayCouponInfoEntity hidden = new PlayCouponInfoEntity();
        hidden.setId("c2");
        hidden.setCouponOnLineState("1");
        hidden.setClaimConditionType("2");
        hidden.setCustomWhitelist(Collections.singletonList("other"));

        when(couponDetailsService.selectByCustomId("uid-1")).thenReturn(new ArrayList<>());
        when(couponInfoService.queryAll()).thenReturn(Arrays.asList(visible, hidden));

        R resp = wxCouponController.queryAll();
        @SuppressWarnings("unchecked")
        List<WxCouponReceiveReturnVo> list = (List<WxCouponReceiveReturnVo>) resp.getData();

        assertNotNull(list);
        assertEquals(1, list.size(), "非白名单券应被过滤不可见");
        assertEquals("c1", list.get(0).getId());
    }

    @Test
    @DisplayName("列表过滤-状态：下架或过期优惠券不可见")
    void testQueryAllFiltersInvalidStatus() {
        PlayCustomUserInfoEntity current = new PlayCustomUserInfoEntity();
        current.setId("uid-2");
        ThreadLocalRequestDetail.setRequestDetail(current);

        PlayCouponInfoEntity offline = new PlayCouponInfoEntity();
        offline.setId("offline");
        offline.setCouponOnLineState(CouponOnlineState.OFFLINE.getCode());

        PlayCouponInfoEntity expired = new PlayCouponInfoEntity();
        expired.setId("expired");
        expired.setCouponOnLineState(CouponOnlineState.ONLINE.getCode());
        expired.setValidityPeriodType(CouponValidityPeriodType.FIXED_RANGE.getCode());
        expired.setProductiveTime(LocalDateTime.now().minusDays(5));
        expired.setExpirationTime(LocalDateTime.now().minusDays(1));
        expired.setRemainingQuantity(5);

        PlayCouponInfoEntity active = new PlayCouponInfoEntity();
        active.setId("active");
        active.setCouponOnLineState(CouponOnlineState.ONLINE.getCode());
        active.setValidityPeriodType(CouponValidityPeriodType.PERMANENT.getCode());
        active.setRemainingQuantity(10);

        when(couponDetailsService.selectByCustomId("uid-2")).thenReturn(Collections.emptyList());
        when(couponInfoService.queryAll()).thenReturn(Arrays.asList(offline, expired, active));

        R resp = wxCouponController.queryAll();
        @SuppressWarnings("unchecked")
        List<WxCouponReceiveReturnVo> list = (List<WxCouponReceiveReturnVo>) resp.getData();

        assertNotNull(list);
        assertEquals(1, list.size(), "仅应展示在线有效的优惠券");
        assertEquals("active", list.get(0).getId());
    }

    @Test
    @DisplayName("列表过滤-库存：无限库存的优惠券可见")
    void testQueryAllShowsUnlimitedInventory() {
        PlayCustomUserInfoEntity current = new PlayCustomUserInfoEntity();
        current.setId("uid-4");
        ThreadLocalRequestDetail.setRequestDetail(current);

        PlayCouponInfoEntity unlimited = new PlayCouponInfoEntity();
        unlimited.setId("coupon-unlimited");
        unlimited.setCouponOnLineState(CouponOnlineState.ONLINE.getCode());
        unlimited.setValidityPeriodType(CouponValidityPeriodType.PERMANENT.getCode());
        unlimited.setRemainingQuantity(-1);
        unlimited.setCouponQuantity(-1);

        when(couponDetailsService.selectByCustomId("uid-4")).thenReturn(Collections.emptyList());
        when(couponInfoService.queryAll()).thenReturn(Collections.singletonList(unlimited));

        R resp = wxCouponController.queryAll();
        @SuppressWarnings("unchecked")
        List<WxCouponReceiveReturnVo> list = (List<WxCouponReceiveReturnVo>) resp.getData();

        assertNotNull(list);
        assertEquals(1, list.size(), "无限库存优惠券不应被过滤");
        assertEquals("coupon-unlimited", list.get(0).getId());
    }

    @Test
    @DisplayName("下单查询：存在限制原因的优惠券被过滤")
    void testQueryByOrderFiltersRestrictedCoupons() {
        PlayCustomUserInfoEntity current = new PlayCustomUserInfoEntity();
        current.setId("uid-3");
        ThreadLocalRequestDetail.setRequestDetail(current);

        PlayCouponDetailsReturnVo restricted = new PlayCouponDetailsReturnVo();
        restricted.setId("detail-1");
        restricted.setCouponId("coupon-restrict");
        restricted.setUseState(CouponUseState.USED.getCode());
        restricted.setObtainingTime(LocalDateTime.now().minusDays(3));

        PlayCouponDetailsReturnVo usable = new PlayCouponDetailsReturnVo();
        usable.setId("detail-2");
        usable.setCouponId("coupon-usable");
        usable.setUseState(CouponUseState.UNUSED.getCode());
        usable.setObtainingTime(LocalDateTime.now().minusDays(1));

        when(couponDetailsService.selectByCustomId("uid-3"))
                .thenReturn(Arrays.asList(restricted, usable));

        PlayCouponInfoEntity restrictedInfo = new PlayCouponInfoEntity();
        restrictedInfo.setId("coupon-restrict");
        restrictedInfo.setCouponOnLineState(CouponOnlineState.ONLINE.getCode());
        restrictedInfo.setValidityPeriodType(CouponValidityPeriodType.PERMANENT.getCode());

        PlayCouponInfoEntity usableInfo = new PlayCouponInfoEntity();
        usableInfo.setId("coupon-usable");
        usableInfo.setCouponOnLineState(CouponOnlineState.ONLINE.getCode());
        usableInfo.setValidityPeriodType(CouponValidityPeriodType.PERMANENT.getCode());

        when(couponInfoService.selectPlayCouponInfoById("coupon-restrict")).thenReturn(restrictedInfo);
        when(couponInfoService.selectPlayCouponInfoById("coupon-usable")).thenReturn(usableInfo);
        when(couponInfoService.getCouponDetailRestrictionReason(eq(restrictedInfo), eq(restricted.getUseState()), any(LocalDateTime.class)))
                .thenReturn(Optional.of("优惠券已使用"));
        when(couponInfoService.getCouponDetailRestrictionReason(eq(usableInfo), eq(usable.getUseState()), any(LocalDateTime.class)))
                .thenReturn(Optional.empty());
        when(couponInfoService.getCouponReasonForUnavailableUse(eq(usableInfo), anyString(), anyString(), anyInt(), any()))
                .thenReturn("");

        PlayCommodityInfoVo commodityInfo = new PlayCommodityInfoVo();
        commodityInfo.setCommodityId("cmd-1");
        commodityInfo.setCommodityPrice(BigDecimal.valueOf(50));
        when(commodityInfoService.queryCommodityInfo("cmd-1", "level-1")).thenReturn(commodityInfo);

        WxCouponOrderQueryVo vo = new WxCouponOrderQueryVo();
        vo.setCommodityId("cmd-1");
        vo.setLevelId("level-1");
        vo.setClerkId("");
        vo.setPlaceType("0");
        vo.setCommodityQuantity(1);

        R resp = wxCouponController.queryByOrder(vo);
        @SuppressWarnings("unchecked")
        List<WxCouponOrderReturnVo> list = (List<WxCouponOrderReturnVo>) resp.getData();

        assertNotNull(list);
        assertEquals(1, list.size(), "受限优惠券应被过滤");
        assertEquals("detail-2", list.get(0).getId());
        assertEquals("1", list.get(0).getAvailable());
        verify(couponInfoService).getCouponDetailRestrictionReason(eq(restrictedInfo), eq(restricted.getUseState()), any(LocalDateTime.class));
    }
}
