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

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import com.starry.admin.common.exception.CustomException;
import com.starry.admin.modules.custom.module.entity.PlayCustomUserInfoEntity;
import com.starry.admin.modules.shop.mapper.PlayCouponInfoMapper;
import com.starry.admin.modules.shop.module.entity.PlayCouponInfoEntity;
import com.starry.admin.modules.shop.module.enums.CouponObtainChannel;
import com.starry.admin.modules.shop.module.enums.CouponOnlineState;
import com.starry.admin.modules.shop.service.IPlayCouponDetailsService;
import org.junit.jupiter.api.BeforeEach;
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)
class PlayCouponInfoServiceImplTest {

    @Mock
    private PlayCouponInfoMapper playCouponInfoMapper;

    @Mock
    private IPlayCouponDetailsService playCouponDetailsService;

    @InjectMocks
    private PlayCouponInfoServiceImpl service;

    private PlayCouponInfoEntity couponInfo;
    private PlayCustomUserInfoEntity customer;

    @BeforeEach
    void setUp() {
        couponInfo = new PlayCouponInfoEntity();
        couponInfo.setId("coupon-1");
        couponInfo.setCouponOnLineState(CouponOnlineState.ONLINE.getCode());
        couponInfo.setCouponQuantity(5);
        couponInfo.setRemainingQuantity(5);

        customer = new PlayCustomUserInfoEntity();
        customer.setId("custom-1");
        customer.setNickname("nick");
        customer.setLevelId("level-1");
    }

    @Test
    void claimCouponForCustom_consumesInventoryAndCreatesDetail() {
        couponInfo.setClerkObtainedMaxQuantity(2);
        when(playCouponInfoMapper.selectByIdForUpdate("coupon-1")).thenReturn(couponInfo);
        when(playCouponDetailsService.countActiveByCustomAndCoupon("coupon-1", "custom-1"))
                .thenReturn(0L);
        when(playCouponInfoMapper.increaseIssuedAndConsumeRemaining("coupon-1")).thenReturn(1);

        service.claimCouponForCustom("coupon-1", customer, CouponObtainChannel.SELF_SERVICE);

        verify(playCouponInfoMapper).increaseIssuedAndConsumeRemaining("coupon-1");
        verify(playCouponDetailsService)
                .create(eq("custom-1"), eq(customer.getNickname()), eq(customer.getLevelId()),
                        eq("coupon-1"), eq(CouponObtainChannel.SELF_SERVICE.getCode()), anyString());
    }

    @Test
    void claimCouponForCustom_throwsWhenLimitReached() {
        couponInfo.setClerkObtainedMaxQuantity(1);
        when(playCouponInfoMapper.selectByIdForUpdate("coupon-1")).thenReturn(couponInfo);
        when(playCouponDetailsService.countActiveByCustomAndCoupon("coupon-1", "custom-1"))
                .thenReturn(1L);

        CustomException ex = assertThrows(CustomException.class, () ->
                service.claimCouponForCustom("coupon-1", customer, CouponObtainChannel.SELF_SERVICE));

        assertEquals("优惠券已达到领取上限", ex.getMessage());
    }

    @Test
    void claimCouponForCustom_throwsWhenDepleted() {
        couponInfo.setRemainingQuantity(0);
        when(playCouponInfoMapper.selectByIdForUpdate("coupon-1")).thenReturn(couponInfo);

        CustomException ex = assertThrows(CustomException.class, () ->
                service.claimCouponForCustom("coupon-1", customer, CouponObtainChannel.SELF_SERVICE));

        assertEquals("优惠券已领完", ex.getMessage());
    }

    @Test
    void claimCouponForCustom_allowsUnlimitedInventory() {
        couponInfo.setRemainingQuantity(-1);
        couponInfo.setCouponQuantity(-1);
        when(playCouponInfoMapper.selectByIdForUpdate("coupon-1")).thenReturn(couponInfo);
        when(playCouponInfoMapper.increaseIssuedAndConsumeRemaining("coupon-1")).thenReturn(1);

        service.claimCouponForCustom("coupon-1", customer, CouponObtainChannel.BACKEND_GRANT);

        verify(playCouponInfoMapper).increaseIssuedAndConsumeRemaining("coupon-1");
        verify(playCouponDetailsService)
                .create(eq("custom-1"), eq(customer.getNickname()), eq(customer.getLevelId()),
                        eq("coupon-1"), eq(CouponObtainChannel.BACKEND_GRANT.getCode()), anyString());
    }
}
