package com.cf.imes.module.system.service.funds.productpromotion;

import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.cf.imes.framework.common.pojo.PageResult;
import com.cf.imes.framework.common.util.i18n.core.util.I18nUtils;
import com.cf.imes.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.cf.imes.framework.test.core.ut.BaseMockitoUnitTest;
import com.cf.imes.module.system.controller.admin.funds.productpromotion.vo.ProductPromotionPageReqVO;
import com.cf.imes.module.system.controller.admin.funds.productpromotion.vo.ProductPromotionSaveReqVO;
import com.cf.imes.module.system.dal.dataobject.funds.productpromotion.ProductPromotionDO;
import com.cf.imes.module.system.dal.dataobject.funds.products.ProductsDO;
import com.cf.imes.module.system.dal.mysql.funds.productpromotion.ProductPromotionMapper;
import com.cf.imes.module.system.enums.pay.DurationUnitEnum;
import com.cf.imes.module.system.service.funds.products.ProductsDetailService;
import org.apache.ibatis.builder.MapperBuilderAssistant;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;

import java.time.LocalDateTime;
import java.util.List;

import static com.cf.imes.framework.test.core.util.AssertUtils.assertPojoEquals;
import static com.cf.imes.framework.test.core.util.AssertUtils.assertServiceException;
import static com.cf.imes.framework.test.core.util.RandomUtils.randomPojo;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.PRODUCT_PROMOTION_ACTIVE_NAME_IS_EXIST;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.PRODUCT_PROMOTION_NO_EXIST;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.PRODUCT_PROMOTION_PURCHASEDURATION_IS_EXIST;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;


class ProductPromotionServiceImplTest extends BaseMockitoUnitTest {
    @InjectMocks
    private ProductPromotionServiceImpl productPromotionService;

    @Mock
    private ProductPromotionMapper productPromotionMapper;

    @Mock
    private ProductsDetailService productsDetailService;

    @Mock
    private I18nUtils i18nUtils;

    @BeforeAll
    static void init(){
        TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new MybatisConfiguration(), ""), ProductPromotionDO.class);
    }

    @Test
    void testCreate() {
        ProductPromotionSaveReqVO reqVO = randomPojo(ProductPromotionSaveReqVO.class, o -> {
            o.setId(100L);
            o.setActivityName("充值送活动");
            o.setPurchaseDuration(1);
            o.setPurchaseDurationUnit(DurationUnitEnum.YEAR.getCode());
            o.setProductId(1L);
            o.setGiftDuration(1);
            o.setGiftDurationUnit(DurationUnitEnum.DAY.getCode());
            o.setStartTime(LocalDateTime.now());
            o.setEndTime(LocalDateTime.now().plusDays(1));
        });

        ProductsDO productsDO = new ProductsDO();
        productsDO.setId(1L);
        productsDO.setProductName("产品名称");
        when(productsDetailService.validateProductExist(any())).thenReturn(productsDO);

        productPromotionService.create(reqVO);

        // 校验数据库插入结果
        // 验证插入
        verify(productPromotionMapper).insert(argThat((ProductPromotionDO infoDO) ->
                infoDO.getProductId().equals(productsDO.getId())
                        && infoDO.getProductName().equals(productsDO.getProductName())
                        && infoDO.getId().equals(100L)
                        && infoDO.getActivityName().equals(reqVO.getActivityName())
                        && infoDO.getPurchaseDuration().equals(reqVO.getPurchaseDuration())
                        && infoDO.getPurchaseDurationUnit().equals(reqVO.getPurchaseDurationUnit())
                        && infoDO.getGiftDuration().equals(reqVO.getGiftDuration())
                        && infoDO.getGiftDurationUnit().equals(reqVO.getGiftDurationUnit())
        ));

        // 再次插入相同活动名称，断言异常
        when(productPromotionMapper.selectOne(any(LambdaQueryWrapperX.class))).thenReturn(new ProductPromotionDO());
        assertServiceException(() -> productPromotionService.create(reqVO), PRODUCT_PROMOTION_ACTIVE_NAME_IS_EXIST);

        // 再次插入相同产品-相同购买时长和单位，断言异常
        reqVO.setActivityName("充值送活动1");
        when(productPromotionMapper.selectOne(any(LambdaQueryWrapper.class))).thenReturn(null).thenReturn(new ProductPromotionDO());
        when(i18nUtils.getMessage(anyString())).thenReturn("年");
        assertServiceException(() -> productPromotionService.create(reqVO), PRODUCT_PROMOTION_PURCHASEDURATION_IS_EXIST, reqVO.getPurchaseDuration(), DurationUnitEnum.getDesc(reqVO.getPurchaseDurationUnit()));
    }

    @Test
    void testUpdate_not_exist() {
        ProductPromotionSaveReqVO reqVO = new ProductPromotionSaveReqVO();
        reqVO.setId(321L);

        assertServiceException(() -> productPromotionService.update(reqVO),
                PRODUCT_PROMOTION_NO_EXIST);

        reqVO.setId(null);

        assertServiceException(() -> productPromotionService.update(reqVO),
                PRODUCT_PROMOTION_NO_EXIST);
    }

    @Test
    void testUpdate() {
        ProductPromotionSaveReqVO reqVO = new ProductPromotionSaveReqVO();
        reqVO.setId(100L);
        reqVO.setActivityName("充值送活动");
        reqVO.setPurchaseDuration(1);
        reqVO.setPurchaseDurationUnit(DurationUnitEnum.YEAR.getCode());

        ProductsDO productsDO = new ProductsDO();
        productsDO.setId(2L);
        productsDO.setProductName("产品名称2");
        when(productsDetailService.validateProductExist(any())).thenReturn(productsDO);

        // 再次插入相同活动名称，断言异常
        when(productPromotionMapper.selectOne(any(LambdaQueryWrapperX.class))).thenReturn(new ProductPromotionDO()).thenReturn(new ProductPromotionDO());
        assertServiceException(() -> productPromotionService.update(reqVO), PRODUCT_PROMOTION_ACTIVE_NAME_IS_EXIST);

        // 再次插入相同产品-相同购买时长和单位，断言异常
        reqVO.setActivityName("充值送活动1");
        when(productPromotionMapper.selectOne(any(LambdaQueryWrapper.class))).thenReturn(new ProductPromotionDO()).thenReturn(null).thenReturn(new ProductPromotionDO());
        when(i18nUtils.getMessage(anyString())).thenReturn("年");
        assertServiceException(() -> productPromotionService.update(reqVO), PRODUCT_PROMOTION_PURCHASEDURATION_IS_EXIST, reqVO.getPurchaseDuration(), DurationUnitEnum.getDesc(reqVO.getPurchaseDurationUnit()));

        // 更新成功
        when(productPromotionMapper.selectOne(any(LambdaQueryWrapper.class))).thenReturn(new ProductPromotionDO()).thenReturn(null).thenReturn(null);
        productPromotionService.update(reqVO);

        // 验证数据库更新
        verify(productPromotionMapper).updateById(argThat((ProductPromotionDO productPromotionDO) ->
                productPromotionDO.getProductId().equals(productsDO.getId())
                        && productPromotionDO.getProductName().equals(productsDO.getProductName())
                        && productPromotionDO.getId().equals(reqVO.getId())
                        && productPromotionDO.getActivityName().equals(reqVO.getActivityName())
        ));
    }


    @Test
    void testGet_not_exist() {
        assertServiceException(() -> productPromotionService.get(null), PRODUCT_PROMOTION_NO_EXIST);
    }

    @Test
    void testGetPage_success() {
        ProductPromotionDO detail = randomPojo(ProductPromotionDO.class, o -> {
            o.setId(null);
            o.setActivityName("晨丰分页活动");
            o.setPurchaseDuration(3);
            o.setPurchaseDurationUnit(DurationUnitEnum.YEAR.getCode());
            o.setGiftDuration(4);
            o.setGiftDurationUnit(DurationUnitEnum.DAY.getCode());
            o.setRemark("晨丰备注1");
        });

        // ============ 构造查询参数 ============
        ProductPromotionPageReqVO reqVO = new ProductPromotionPageReqVO();
        reqVO.setActivityName("晨丰");
        reqVO.setPurchaseDuration(3);
        reqVO.setGiftDuration(4);
        reqVO.setRemark("晨丰备注");
        reqVO.setPageNo(1);
        reqVO.setPageSize(10);

        when(productPromotionMapper.selectPage(reqVO)).thenReturn(new PageResult<>(List.of(detail), 1L));

        // ============ 调用方法 ============
        PageResult<ProductPromotionDO> pageResult = productPromotionService.getPage(reqVO);

        // ============ 断言结果 ============
        assertEquals(1, pageResult.getTotal(), "分页结果数量应为1");
        assertEquals(1, pageResult.getList().size(), "分页列表长度应为1");
        assertPojoEquals(detail, pageResult.getList().get(0));
    }

    @Test
    void testDelete() {
        ProductPromotionDO exist = randomPojo(ProductPromotionDO.class);

        when(productPromotionMapper.selectOne(any(LambdaQueryWrapperX.class))).thenReturn(exist);

        assertDoesNotThrow(() -> productPromotionService.delete(exist.getId()));

        verify(productPromotionMapper, times(1)).deleteById(exist.getId());
    }

    @Test
    void testDelete_not_exist() {
        assertServiceException(() -> productPromotionService.delete(null), PRODUCT_PROMOTION_NO_EXIST);
        assertServiceException(() -> productPromotionService.delete(321L), PRODUCT_PROMOTION_NO_EXIST);
    }

    @Test
    void testGetAvailableRechargeActiveList() {
        // 当前时间
        LocalDateTime now = LocalDateTime.now();

        Long productId = 1000L;

        // 有效活动（时间区间内）
        ProductPromotionDO valid = new ProductPromotionDO();
        valid.setActivityName("有效活动");
        valid.setProductId(productId);
        valid.setProductName("产品名称");
        valid.setPurchaseDuration(1);
        valid.setPurchaseDurationUnit(DurationUnitEnum.YEAR.getCode());
        valid.setGiftDuration(1);
        valid.setGiftDurationUnit(DurationUnitEnum.DAY.getCode());
        valid.setStartTime(now.minusDays(1));
        valid.setEndTime(now.plusDays(1));
        valid.setDeleted(false);
        productPromotionMapper.insert(valid);

        when(productPromotionMapper.selectList(any(LambdaQueryWrapper.class))).thenReturn(List.of(valid));

        // 执行被测方法
        List<ProductPromotionDO> result = productPromotionService.getAvailableProductPromotionList(productId);

        // then
        assertNotNull(result);
        assertEquals(1L, result.size());

        // 校验 查询条件
        verify(productPromotionMapper).selectList(
                argThat((LambdaQueryWrapper wrapper) ->
                        wrapper.getSqlSegment().contains("deleted")
                                && wrapper.getSqlSegment().contains("product_id")
                                && wrapper.getSqlSegment().contains("start_time")
                                && wrapper.getSqlSegment().contains("end_time")
                )
        );

    }

}
