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

import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.cf.imes.framework.common.pojo.PageResult;
import com.cf.imes.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.cf.imes.framework.mybatis.core.query.LambdaUpdateWrapperX;
import com.cf.imes.framework.test.core.ut.BaseMockitoUnitTest;
import com.cf.imes.module.system.controller.admin.funds.products.vo.product.ProductPageReqVO;
import com.cf.imes.module.system.controller.admin.funds.products.vo.product.ProductSaveReqVO;
import com.cf.imes.module.system.dal.dataobject.funds.products.ProductsDO;
import com.cf.imes.module.system.dal.mysql.funds.products.ProductsMapper;
import com.cf.imes.module.system.enums.products.ProductStatusEnum;
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.math.BigDecimal;
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.randomLongId;
import static com.cf.imes.framework.test.core.util.RandomUtils.randomPojo;
import static com.cf.imes.framework.test.core.util.RandomUtils.randomString;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.PRODUCTS_IS_EXIST;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.PRODUCTS_NO_DELETE;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.PRODUCTS_NO_EXIST;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.PRODUCTS_NO_UPDATE;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;


class ProductsServiceImplTest extends BaseMockitoUnitTest {
    @InjectMocks
    private ProductsServiceImpl productsService;

    @Mock
    private ProductsMapper productsMapper;

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

    @Test
    void testCreateProductSuccess() {
        // 构造请求对象
        String productName = randomString();
        ProductSaveReqVO req = new ProductSaveReqVO();
        req.setProductName(productName);
        req.setPrice(new BigDecimal("88.88"));
        req.setStatus(ProductStatusEnum.PRODUCT_UPDATES.getStatus());

        // 调用真实 Service 方法
        productsService.create(req);

        // 校验插入
        verify(productsMapper).insert(argThat((ProductsDO infoDO) ->
                infoDO.getProductName().equals(req.getProductName())
                        && infoDO.getPrice().equals(req.getPrice())
                        && infoDO.getSoftwareId().equals(0)
                        && infoDO.getStatus().equals(req.getStatus())
        ));
    }

    /**
     * 下架更新为上架
     */
    @Test
    void testUpdate_takeoff_to_listed() {
        ProductsDO productsDO = new ProductsDO();
        productsDO.setStatus(ProductStatusEnum.PRODUCT_DELIS.getStatus());
        when(productsMapper.selectOne(any(LambdaQueryWrapperX.class))).thenReturn(productsDO);

        ProductSaveReqVO updateVO = new ProductSaveReqVO();
        updateVO.setId(100L);
        String newProductName = randomString();
        updateVO.setProductName(newProductName);
        updateVO.setStatus(ProductStatusEnum.PRODUCT_UPDATES.getStatus());
        updateVO.setPrice(new BigDecimal("199.99"));

        productsService.update(updateVO);

        // 校验状态更新
        verify(productsMapper).update(argThat((LambdaUpdateWrapperX wrapper) ->
                wrapper.getSqlSegment().contains("id")
                        && wrapper.getSqlSet().contains("product_name")
                        && wrapper.getSqlSet().contains("price")
                        && wrapper.getSqlSet().contains("status")
        ));
    }

    /**
     * 已上架更新
     */
    @Test
    void testUpdate_statusUpdatableThrowsException() {
        ProductsDO productsDO = new ProductsDO();
        productsDO.setStatus(ProductStatusEnum.PRODUCT_UPDATES.getStatus());
        when(productsMapper.selectOne(any(LambdaQueryWrapperX.class))).thenReturn(productsDO);

        ProductSaveReqVO reqVO = new ProductSaveReqVO();
        reqVO.setId(100L);
        reqVO.setStatus(ProductStatusEnum.PRODUCT_UPDATES.getStatus()); // 非下架

        assertServiceException(() -> productsService.update(reqVO),
                PRODUCTS_NO_UPDATE);
    }

    /**
     * 已上架更新下架
     */
    @Test
    void testUpdate_listed_to_takeoff() {
        ProductsDO productsDO = new ProductsDO();
        productsDO.setStatus(ProductStatusEnum.PRODUCT_UPDATES.getStatus());
        when(productsMapper.selectOne(any(LambdaQueryWrapperX.class))).thenReturn(productsDO);

        ProductSaveReqVO updateVO = new ProductSaveReqVO();
        updateVO.setId(100L);
        String newProductName = randomString();
        updateVO.setProductName(newProductName);
        updateVO.setStatus(ProductStatusEnum.PRODUCT_DELIS.getStatus());
        updateVO.setPrice(new BigDecimal("199.99"));

        productsService.update(updateVO);

        // 校验状态更新
        verify(productsMapper).update(argThat((LambdaUpdateWrapperX wrapper) ->
                wrapper.getSqlSegment().contains("id")
                        && wrapper.getSqlSet().contains("product_name")
                        && wrapper.getSqlSet().contains("price")
                        && wrapper.getSqlSet().contains("status")
        ));
    }


    @Test
    void testDelete() {
        ProductsDO productsDO = new ProductsDO();
        productsDO.setId(100L);
        productsDO.setStatus(ProductStatusEnum.PRODUCT_DELIS.getStatus());
        when(productsMapper.selectOne(any(LambdaQueryWrapperX.class))).thenReturn(productsDO);

        productsService.delete(productsDO.getId());
        // 断言不存在了
        assertNull(productsMapper.selectById(productsDO.getId()));
    }

    @Test
    void testDelete_listed() {
        ProductsDO productsDO = new ProductsDO();
        productsDO.setId(100L);
        productsDO.setStatus(ProductStatusEnum.PRODUCT_UPDATES.getStatus());
        when(productsMapper.selectOne(any(LambdaQueryWrapperX.class))).thenReturn(productsDO);

        assertServiceException(() -> productsService.delete(productsDO.getId()),
                PRODUCTS_NO_DELETE);
    }

    @Test
    void testGet() {
        ProductsDO productsDO = new ProductsDO();
        productsDO.setId(100L);
        productsDO.setProductName("产品名称");
        productsDO.setStatus(ProductStatusEnum.PRODUCT_UPDATES.getStatus());
        when(productsMapper.selectOne(any(LambdaQueryWrapperX.class))).thenReturn(productsDO);

        ProductsDO db = productsService.get(productsDO.getId());

        assertEquals(db.getId(), productsDO.getId());
        assertEquals(db.getProductName(), productsDO.getProductName());
        assertEquals(db.getStatus(), productsDO.getStatus());
    }

    @Test
    void testGetProductPage() {
        // ============ mock 数据 ============
        ProductsDO dbProduct = randomPojo(ProductsDO.class, o -> { // 这个会被查到
            o.setProductName("晨丰-软件");
            o.setStatus(ProductStatusEnum.PRODUCT_UPDATES.getStatus()); // 假设0=上架
            o.setDeleted(false);
            o.setPrice(new BigDecimal("99.99"));
        });

        // ============ 构造查询参数 ============
        ProductPageReqVO reqVO = new ProductPageReqVO();
        reqVO.setProductName("晨丰");
        reqVO.setStatus(ProductStatusEnum.PRODUCT_UPDATES.getStatus());
        reqVO.setPageNo(1);
        reqVO.setPageSize(10);

        when(productsService.getPage(reqVO)).thenReturn(new PageResult<>(List.of(dbProduct), 1L));

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

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


    @Test
    void testDuplicateNameThrowsException() {
        when(productsMapper.selectList(any(LambdaQueryWrapperX.class))).thenReturn(List.of(new ProductsDO()));

        ProductSaveReqVO createVO = new ProductSaveReqVO();
        String productName = randomString();
        createVO.setProductName(productName);
        createVO.setPrice(new BigDecimal("88.88"));
        createVO.setStatus(1);
        assertServiceException(() -> productsService.create(createVO),
                PRODUCTS_IS_EXIST, productName);
    }

    @Test
    void testGetNotExist() {
        assertServiceException(() -> productsService.get(randomLongId()),
                PRODUCTS_NO_EXIST);
    }
}
