package com.ruoyi.web.modules.goods.controller;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.junit.jupiter.api.AfterEach;
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.Spy;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ruoyi.web.modules.category.facade.IShopGoodsCategoryFacade;
import com.ruoyi.web.modules.category.service.IPlatformCategoryService;
import com.ruoyi.web.modules.category.support.SellerShopContext;
import com.ruoyi.web.modules.category.vo.CategoryLevel2OptionVO;
import com.ruoyi.web.modules.category.vo.ShopGoodsCategoryOptionVO;
import com.ruoyi.web.modules.goodsservice.facade.IGoodsServiceFacade;
import com.ruoyi.web.modules.goods.constant.GoodsConstants;
import com.ruoyi.web.modules.goods.dto.GoodsBatchIdsDTO;
import com.ruoyi.web.modules.goods.dto.GoodsSaveDTO;
import com.ruoyi.web.modules.goods.exception.GoodsBatchOperationException;
import com.ruoyi.web.modules.goods.service.IGoodsService;
import com.ruoyi.web.modules.goods.vo.GoodsDetailVO;
import com.ruoyi.web.modules.goods.vo.GoodsListVO;
import com.ruoyi.web.modules.freight.facade.IFreightTemplateFacade;
import com.ruoyi.web.modules.freight.vo.FreightTemplateDetailVO;
import com.ruoyi.web.modules.freight.vo.FreightTemplateOptionVO;
import com.ruoyi.web.modules.template.dto.AttrTemplateItemDTO;
import com.ruoyi.web.modules.template.facade.IAttrTemplateFacade;
import com.ruoyi.web.modules.template.vo.AttrTemplateDetailVO;
import com.ruoyi.web.modules.template.vo.AttrTemplateOptionVO;

@ExtendWith(MockitoExtension.class)
class SellerGoodsControllerTest
{
    @Mock
    private IGoodsService goodsService;

    @Mock
    private IPlatformCategoryService platformCategoryService;

    @Mock
    private IShopGoodsCategoryFacade shopGoodsCategoryFacade;

    @Mock
    private IGoodsServiceFacade goodsServiceFacade;

    @Mock
    private IAttrTemplateFacade attrTemplateFacade;

    @Mock
    private IFreightTemplateFacade freightTemplateFacade;

    @Spy
    @InjectMocks
    private SellerGoodsController controller;

    private MockMvc mockMvc;

    private final ObjectMapper objectMapper = new ObjectMapper();

    @BeforeEach
    void setUp()
    {
        SellerShopContext.setShopId(101L);
        mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
        lenient().doReturn("seller").when(controller).getUsername();
    }

    @AfterEach
    void tearDown()
    {
        SellerShopContext.clear();
    }

    @Test
    void list_returns200() throws Exception
    {
        GoodsListVO row = new GoodsListVO();
        row.setGoodsId(1001L);
        row.setGoodsSn("G001");
        row.setGoodsName("复合肥");
        when(goodsService.selectSellerList(eq(101L), any())).thenReturn(Collections.singletonList(row));

        mockMvc.perform(get("/agri/seller/goods/list"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200))
                .andExpect(jsonPath("$.rows[0].goodsSn").value("G001"));
    }

    @Test
    void detail_returns200() throws Exception
    {
        GoodsDetailVO vo = new GoodsDetailVO();
        vo.setGoodsId(1001L);
        vo.setGoodsStatus(GoodsConstants.STATUS_REJECTED);
        vo.setRejectReason("资料不全");
        when(goodsService.selectSellerDetail(101L, 1001L)).thenReturn(vo);

        mockMvc.perform(get("/agri/seller/goods/1001"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200))
                .andExpect(jsonPath("$.data.rejectReason").value("资料不全"));
    }

    @Test
    void add_returnsGoodsId() throws Exception
    {
        when(goodsService.insertSellerGoods(eq(101L), any(), eq("seller"))).thenReturn(1001L);

        GoodsSaveDTO body = buildSaveDto();
        mockMvc.perform(post("/agri/seller/goods")
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(body)))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200))
                .andExpect(jsonPath("$.data.goodsId").value(1001));
    }

    @Test
    void submit_returnsTargetStatus() throws Exception
    {
        Map<String, Object> data = new HashMap<>();
        data.put("goodsId", 1001L);
        data.put("goodsStatus", GoodsConstants.STATUS_PENDING);
        when(goodsService.submitGoods(101L, 1001L, "seller")).thenReturn(data);

        mockMvc.perform(put("/agri/seller/goods/1001/submit"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200))
                .andExpect(jsonPath("$.data.goodsStatus").value("1"));
    }

    @Test
    void submitBatch_batchFail() throws Exception
    {
        doThrow(new GoodsBatchOperationException(GoodsConstants.MSG_BATCH_FAIL,
                Collections.singletonList("商品不可提交上架：G003")))
                .when(goodsService).submitGoodsBatch(eq(101L), any(), eq("seller"));

        GoodsBatchIdsDTO body = new GoodsBatchIdsDTO();
        body.setGoodsIds(Arrays.asList(1001L, 1003L));
        mockMvc.perform(put("/agri/seller/goods/submit")
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(body)))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(500))
                .andExpect(jsonPath("$.data.reasons[0]").exists());
    }

    @Test
    void offShelf_success() throws Exception
    {
        mockMvc.perform(put("/agri/seller/goods/1003/offShelf"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200));
    }

    @Test
    void delete_success() throws Exception
    {
        mockMvc.perform(delete("/agri/seller/goods/1001"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200));
    }

    @Test
    void shopCategoryOptions_returnsList() throws Exception
    {
        when(shopGoodsCategoryFacade.listOptionsByShopId(101L, false))
                .thenReturn(Collections.singletonList(new ShopGoodsCategoryOptionVO(2005L, "店内 > 热销", "1")));

        mockMvc.perform(get("/agri/seller/goods/shopCategoryOptions"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200))
                .andExpect(jsonPath("$.data[0].label").value("店内 > 热销"));
    }

    @Test
    void categoryOptions_returnsPlatformCategories() throws Exception
    {
        when(platformCategoryService.selectPlatformLevel2Options())
                .thenReturn(Collections.singletonList(new CategoryLevel2OptionVO(1002L, "肥料 > 复合肥", "复合肥")));

        mockMvc.perform(get("/agri/seller/goods/categoryOptions"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200))
                .andExpect(jsonPath("$.data[0].label").value("肥料 > 复合肥"));
    }

    @Test
    void attrTemplateDetail_returnsPrefillData() throws Exception
    {
        AttrTemplateItemDTO attr = new AttrTemplateItemDTO();
        attr.setItemName("品牌");
        attr.setValues(Collections.singletonList("华为"));
        AttrTemplateDetailVO detail = new AttrTemplateDetailVO();
        detail.setTemplateId(4L);
        detail.setTemplateName("手机");
        detail.setAttributes(Collections.singletonList(attr));
        detail.setSpecs(Collections.emptyList());
        when(attrTemplateFacade.getDetailForGoods(4L, 101L)).thenReturn(detail);

        mockMvc.perform(get("/agri/seller/goods/attrTemplate/4"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200))
                .andExpect(jsonPath("$.data.templateName").value("手机"))
                .andExpect(jsonPath("$.data.attributes[0].itemName").value("品牌"));
    }

    @Test
    void attrTemplateOptions_returnsList() throws Exception
    {
        AttrTemplateOptionVO option = new AttrTemplateOptionVO();
        option.setTemplateId(4L);
        option.setTemplateName("手机");
        when(attrTemplateFacade.listOptionsByShopId(101L)).thenReturn(Collections.singletonList(option));

        mockMvc.perform(get("/agri/seller/goods/attrTemplateOptions"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200))
                .andExpect(jsonPath("$.data[0].templateName").value("手机"));
    }

    @Test
    void freightTemplateOptions_returnsList() throws Exception
    {
        FreightTemplateOptionVO option = new FreightTemplateOptionVO();
        option.setTemplateId(1L);
        option.setTemplateName("全国包邮");
        when(freightTemplateFacade.listOptionsByShopId(101L)).thenReturn(Collections.singletonList(option));

        mockMvc.perform(get("/agri/seller/goods/freightTemplateOptions"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200))
                .andExpect(jsonPath("$.data[0].templateName").value("全国包邮"));
    }

    @Test
    void freightDefault_returnsDefaultTemplate() throws Exception
    {
        FreightTemplateOptionVO option = new FreightTemplateOptionVO();
        option.setTemplateId(1L);
        option.setTemplateName("全国包邮");
        option.setFreeFlag("1");
        option.setIsDefault("1");
        when(freightTemplateFacade.getDefaultTemplate(101L)).thenReturn(option);

        mockMvc.perform(get("/agri/seller/goods/freightDefault"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200))
                .andExpect(jsonPath("$.data.freightType").value("2"))
                .andExpect(jsonPath("$.data.freightTemplateId").value(1));
    }

    @Test
    void freightTemplateDetail_returns200() throws Exception
    {
        FreightTemplateDetailVO detail = new FreightTemplateDetailVO();
        detail.setTemplateId(2L);
        detail.setTemplateName("江浙沪");
        detail.setShipTime("48小时内发货");
        when(freightTemplateFacade.getDetailForGoods(2L, 101L)).thenReturn(detail);

        mockMvc.perform(get("/agri/seller/goods/freightTemplate/2"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200))
                .andExpect(jsonPath("$.data.templateName").value("江浙沪"));
    }

    private GoodsSaveDTO buildSaveDto()
    {
        GoodsSaveDTO dto = new GoodsSaveDTO();
        dto.setCategoryId(1002L);
        dto.setGoodsName("复合肥 50kg");
        dto.setMainPic("/a.jpg");
        dto.setDetailContent("detail");
        dto.setSalePrice(new BigDecimal("128"));
        dto.setStock(100);
        dto.setFreightType("1");
        dto.setFixedFreightAmount(new BigDecimal("10"));
        return dto;
    }
}
