package com.haha.admin.controller;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.haha.common.vo.LoginVO;
import com.haha.common.vo.Result;
import com.haha.common.vo.UserVO;
import com.haha.entity.dto.ReplenisherBindDTO;
import com.haha.entity.dto.WechatLoginDTO;
import com.haha.service.ReplenisherService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
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;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

/**
 * 补货员登录控制器 - 完整单元测试
 * <p>
 * 覆盖微信静默登录和扫码绑定两个接口
 */
@ExtendWith(MockitoExtension.class)
@DisplayName("补货员登录控制器")
class ReplenisherLoginControllerTest {

    @Mock
    private ReplenisherService replenisherService;

    @InjectMocks
    private ReplenisherLoginController controller;

    private MockMvc mockMvc;
    private ObjectMapper objectMapper;

    @BeforeEach
    void setUp() {
        objectMapper = new ObjectMapper();
        mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
    }

    // ============================================================
    // 1. POST /replenisher/login/wechat - 微信静默登录
    // ============================================================

    @Nested
    @DisplayName("POST /replenisher/login/wechat - 微信静默登录")
    class WechatLoginTests {

        @Test
        @DisplayName("已绑定微信的补货员登录成功")
        void shouldLoginSuccessfully() throws Exception {
            WechatLoginDTO dto = new WechatLoginDTO();
            dto.setCode("test-wechat-code-123");

            UserVO userInfo = UserVO.builder()
                    .id(1L)
                    .nickname("测试补货员")
                    .phone("13800138000")
                    .build();

            LoginVO loginVO = LoginVO.builder()
                    .token("test-token-xxx")
                    .userInfo(userInfo)
                    .build();

            Result<LoginVO> successResult = Result.success("登录成功", loginVO);
            when(replenisherService.loginByWechat("test-wechat-code-123"))
                    .thenReturn(successResult);

            mockMvc.perform(post("/replenisher/login/wechat")
                            .contentType(MediaType.APPLICATION_JSON)
                            .content(objectMapper.writeValueAsString(dto)))
                    .andExpect(status().isOk())
                    .andExpect(jsonPath("$.code").value(200))
                    .andExpect(jsonPath("$.message").value("登录成功"))
                    .andExpect(jsonPath("$.data.token").value("test-token-xxx"))
                    .andExpect(jsonPath("$.data.userInfo.nickname").value("测试补货员"));

            verify(replenisherService).loginByWechat("test-wechat-code-123");
        }

        @Test
        @DisplayName("未绑定微信的补货员登录失败")
        void shouldFailWhenWechatNotBound() throws Exception {
            WechatLoginDTO dto = new WechatLoginDTO();
            dto.setCode("unbound-wechat-code");

            Result<LoginVO> errorResult = Result.error(400, "该微信未绑定补货员账号");
            when(replenisherService.loginByWechat("unbound-wechat-code"))
                    .thenReturn(errorResult);

            mockMvc.perform(post("/replenisher/login/wechat")
                            .contentType(MediaType.APPLICATION_JSON)
                            .content(objectMapper.writeValueAsString(dto)))
                    .andExpect(status().isOk())
                    .andExpect(jsonPath("$.code").value(400))
                    .andExpect(jsonPath("$.message").value("该微信未绑定补货员账号"));

            verify(replenisherService).loginByWechat("unbound-wechat-code");
        }
    }

    // ============================================================
    // 2. POST /replenisher/login/bind - 扫码绑定微信
    // ============================================================

    @Nested
    @DisplayName("POST /replenisher/login/bind - 扫码绑定微信")
    class BindWechatTests {

        @Test
        @DisplayName("绑定成功")
        void shouldBindSuccessfully() throws Exception {
            ReplenisherBindDTO dto = new ReplenisherBindDTO();
            dto.setBindingCode("A1B2-C3D4-E5F6-G7H8");
            dto.setCode("wechat-code-from-wx-login");

            UserVO userInfo = UserVO.builder()
                    .id(2L)
                    .nickname("新补货员")
                    .build();

            LoginVO loginVO = LoginVO.builder()
                    .token("bind-token-xxx")
                    .userInfo(userInfo)
                    .build();

            Result<LoginVO> successResult = Result.success("绑定成功", loginVO);
            when(replenisherService.bindWechat(eq("A1B2-C3D4-E5F6-G7H8"), eq("wechat-code-from-wx-login")))
                    .thenReturn(successResult);

            mockMvc.perform(post("/replenisher/login/bind")
                            .contentType(MediaType.APPLICATION_JSON)
                            .content(objectMapper.writeValueAsString(dto)))
                    .andExpect(status().isOk())
                    .andExpect(jsonPath("$.code").value(200))
                    .andExpect(jsonPath("$.message").value("绑定成功"))
                    .andExpect(jsonPath("$.data.token").value("bind-token-xxx"))
                    .andExpect(jsonPath("$.data.userInfo.nickname").value("新补货员"));

            verify(replenisherService).bindWechat("A1B2-C3D4-E5F6-G7H8", "wechat-code-from-wx-login");
        }

        @Test
        @DisplayName("绑定码无效")
        void shouldFailWhenBindingCodeInvalid() throws Exception {
            ReplenisherBindDTO dto = new ReplenisherBindDTO();
            dto.setBindingCode("INVALID-CODE");
            dto.setCode("wechat-code-xxx");

            Result<LoginVO> errorResult = Result.error(400, "绑定码无效或已过期");
            when(replenisherService.bindWechat(eq("INVALID-CODE"), eq("wechat-code-xxx")))
                    .thenReturn(errorResult);

            mockMvc.perform(post("/replenisher/login/bind")
                            .contentType(MediaType.APPLICATION_JSON)
                            .content(objectMapper.writeValueAsString(dto)))
                    .andExpect(status().isOk())
                    .andExpect(jsonPath("$.code").value(400))
                    .andExpect(jsonPath("$.message").value("绑定码无效或已过期"));

            verify(replenisherService).bindWechat("INVALID-CODE", "wechat-code-xxx");
        }

        @Test
        @DisplayName("绑定码已被使用")
        void shouldFailWhenBindingCodeAlreadyUsed() throws Exception {
            ReplenisherBindDTO dto = new ReplenisherBindDTO();
            dto.setBindingCode("USED-CODE-1234");
            dto.setCode("wechat-code-xxx");

            Result<LoginVO> errorResult = Result.error(400, "绑定码已被使用");
            when(replenisherService.bindWechat(eq("USED-CODE-1234"), eq("wechat-code-xxx")))
                    .thenReturn(errorResult);

            mockMvc.perform(post("/replenisher/login/bind")
                            .contentType(MediaType.APPLICATION_JSON)
                            .content(objectMapper.writeValueAsString(dto)))
                    .andExpect(status().isOk())
                    .andExpect(jsonPath("$.code").value(400))
                    .andExpect(jsonPath("$.message").value("绑定码已被使用"));

            verify(replenisherService).bindWechat("USED-CODE-1234", "wechat-code-xxx");
        }
    }
}
