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

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
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.util.Collections;
import java.util.HashMap;
import java.util.Map;
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.merchant.constant.MerchantBindConstants;
import com.ruoyi.web.modules.merchant.dto.MerchantCertStatusDTO;
import com.ruoyi.web.modules.merchant.dto.MerchantCreateDTO;
import com.ruoyi.web.modules.merchant.service.IMerchantService;
import com.ruoyi.web.modules.merchant.vo.MerchantAdminUserOptionVO;
import com.ruoyi.web.modules.merchant.vo.MerchantDetailVO;
import com.ruoyi.web.modules.merchant.vo.MerchantListVO;
import com.ruoyi.web.modules.merchant.vo.MerchantMemberOptionVO;

@ExtendWith(MockitoExtension.class)
class MerchantControllerTest
{
    @Mock
    private IMerchantService merchantService;

    @Spy
    @InjectMocks
    private MerchantController controller;

    private MockMvc mockMvc;

    private final ObjectMapper objectMapper = new ObjectMapper();

    @BeforeEach
    void setUp() throws Exception
    {
        mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
        org.mockito.Mockito.lenient().doReturn("admin").when(controller).getUsername();
    }

    @Test
    void selectList_returns200() throws Exception
    {
        MerchantListVO vo = new MerchantListVO();
        vo.setMerchantId(1L);
        vo.setUnitName("张三农资");
        vo.setMerchantName("张三农资店");
        when(merchantService.selectSelectableList()).thenReturn(Collections.singletonList(vo));

        mockMvc.perform(get("/agri/merchant/selectList"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200))
                .andExpect(jsonPath("$.data[0].unitName").value("张三农资"))
                .andExpect(jsonPath("$.data[0].merchantName").value("张三农资店"));
    }

    @Test
    void getInfo_returns200() throws Exception
    {
        MerchantDetailVO vo = new MerchantDetailVO();
        vo.setMerchantId(1L);
        vo.setUnitName("某某公司");
        when(merchantService.selectMerchantDetail(1L)).thenReturn(vo);

        mockMvc.perform(get("/agri/merchant/1"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200))
                .andExpect(jsonPath("$.data.unitName").value("某某公司"));
    }

    @Test
    void adminUserOptions_returns200() throws Exception
    {
        MerchantAdminUserOptionVO option = new MerchantAdminUserOptionVO();
        option.setUserId(10L);
        option.setUserName("admin01");
        option.setNickName("管理员甲");
        when(merchantService.searchAdminUserOptions("admin")).thenReturn(Collections.singletonList(option));

        mockMvc.perform(get("/agri/merchant/adminUserOptions").param("keyword", "admin"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200))
                .andExpect(jsonPath("$.data[0].userName").value("admin01"))
                .andExpect(jsonPath("$.data[0].nickName").value("管理员甲"));
    }

    @Test
    void memberOptions_returns200() throws Exception
    {
        MerchantMemberOptionVO option = new MerchantMemberOptionVO();
        option.setMemberId(5L);
        option.setMemberCode("张三");
        option.setNickName("小张");
        when(merchantService.searchMemberOptions("张")).thenReturn(Collections.singletonList(option));

        mockMvc.perform(get("/agri/merchant/memberOptions").param("keyword", "张"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200))
                .andExpect(jsonPath("$.data[0].memberCode").value("张三"))
                .andExpect(jsonPath("$.data[0].nickName").value("小张"));
    }

    @Test
    void add_withBind_returns200() throws Exception
    {
        Map<String, Object> data = new HashMap<>();
        data.put("merchantId", 1L);
        when(merchantService.insertMerchant(any(MerchantCreateDTO.class), eq("admin"))).thenReturn(data);

        MerchantCreateDTO dto = new MerchantCreateDTO();
        dto.setMerchantType("1");
        dto.setPersonName("张三");
        dto.setBindType(MerchantBindConstants.BIND_SYS_USER);
        dto.setBindUserId(10L);

        mockMvc.perform(post("/agri/merchant")
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(dto)))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200))
                .andExpect(jsonPath("$.msg").value("请尽快在编辑中完善商户经营信息后再开设店铺"));
    }

    @Test
    void certStatus_needConfirm() throws Exception
    {
        Map<String, Object> data = new HashMap<>();
        data.put("needConfirm", true);
        data.put("confirmMessage", "冻结后不可新开店铺，是否继续？");
        when(merchantService.updateCertStatus(eq(1L), any(MerchantCertStatusDTO.class), eq("admin"))).thenReturn(data);

        MerchantCertStatusDTO dto = new MerchantCertStatusDTO();
        dto.setCertStatus("1");

        mockMvc.perform(put("/agri/merchant/1/certStatus")
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(dto)))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.data.needConfirm").value(true));
    }
}
