package com.ruoyi.web.modules.account.service;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.Map;
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 com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.framework.web.service.TokenService;
import com.ruoyi.framework.web.service.UserDetailsServiceImpl;
import com.ruoyi.system.service.ISysUserService;
import com.ruoyi.web.modules.account.constant.MemberConstants;
import com.ruoyi.web.modules.account.domain.BizMember;
import com.ruoyi.web.modules.account.dto.MemberChangePasswordDTO;
import com.ruoyi.web.modules.account.dto.MemberLoginDTO;
import com.ruoyi.web.modules.account.dto.MemberProfileUpdateDTO;
import com.ruoyi.web.modules.account.dto.MemberRegisterDTO;
import com.ruoyi.web.modules.account.mapper.BizMemberMapper;
import com.ruoyi.web.modules.account.service.impl.MemberAppServiceImpl;
import com.ruoyi.web.modules.account.vo.MemberProfileAppVO;
import com.ruoyi.web.modules.content.facade.IMallServiceAgreementFacade;
import com.ruoyi.web.modules.account.support.MemberAvatarSupport;
import com.ruoyi.web.modules.account.support.MemberRegistrationSupport;

/**
 * C 端会员 Service 单元测试（MEM-UT）
 */
@ExtendWith(MockitoExtension.class)
class MemberAppServiceImplTest
{
    @Mock
    private BizMemberMapper memberMapper;

    @Mock
    private IMemberSmsService smsService;

    @Mock
    private TokenService tokenService;

    @Mock
    private UserDetailsServiceImpl userDetailsService;

    @Mock
    private ISysUserService sysUserService;

    @Mock
    private IMallServiceAgreementFacade mallServiceAgreementFacade;

    @Mock
    private MemberRegistrationSupport memberRegistrationSupport;

    @Mock
    private MemberAvatarSupport memberAvatarSupport;

    @InjectMocks
    private MemberAppServiceImpl memberAppService;

    @org.junit.jupiter.api.BeforeEach
    void stubAvatar()
    {
        lenient().when(memberAvatarSupport.resolve(any())).thenAnswer(inv -> {
            String avatar = inv.getArgument(0);
            return avatar != null ? avatar : "https://example.com/default.png";
        });
    }

    @Test
    void register_passwordMismatch_rejected()
    {
        MemberRegisterDTO dto = registerDto();
        dto.setConfirmPassword("Other1234");

        ServiceException ex = assertThrows(ServiceException.class, () -> memberAppService.register(dto));
        assertEquals("两次输入的密码不一致", ex.getMessage());
    }

    @Test
    void register_duplicateMobile_rejected()
    {
        MemberRegisterDTO dto = registerDto();
        when(memberMapper.countByMobile(eq("17700001111"), eq(null))).thenReturn(1);

        assertThrows(ServiceException.class, () -> memberAppService.register(dto));
    }

    @Test
    void register_successWithoutSms()
    {
        MemberRegisterDTO dto = registerDto();
        dto.setAgreementAccepted(true);
        when(memberMapper.countByMobile(eq("17700001111"), eq(null))).thenReturn(0);
        when(memberRegistrationSupport.createMember(any(), eq("member"))).thenReturn(1001L);

        Long memberId = memberAppService.register(dto);

        assertEquals(1001L, memberId);
    }

    @Test
    void login_byMobile_success()
    {
        BizMember member = activeMember();
        member.setPassword(SecurityUtils.encryptPassword("Pass1234"));
        when(memberMapper.selectByMobile("17700001111")).thenReturn(member);
        SysUser user = sysUser(1L, "user_a001");
        when(sysUserService.selectUserById(1L)).thenReturn(user);
        LoginUser loginUser = new LoginUser(1L, null, user, null);
        when(userDetailsService.createLoginUser(user)).thenReturn(loginUser);
        when(tokenService.createToken(any(LoginUser.class))).thenReturn("jwt-token-abc");

        MemberLoginDTO dto = new MemberLoginDTO();
        dto.setAccount("17700001111");
        dto.setPassword("Pass1234");

        Map<String, Object> result = memberAppService.login(dto);

        assertEquals("jwt-token-abc", result.get("token"));
        assertEquals(1L, result.get("memberId"));
        verify(tokenService).createToken(any(LoginUser.class));
    }

    @Test
    void login_byMemberName_success()
    {
        BizMember member = activeMember();
        member.setMemberCode("user_a001");
        member.setPassword(SecurityUtils.encryptPassword("Pass1234"));
        when(memberMapper.selectByMemberCode("user_a001")).thenReturn(member);
        SysUser user = sysUser(1L, "user_a001");
        when(sysUserService.selectUserById(1L)).thenReturn(user);
        LoginUser loginUser = new LoginUser(1L, null, user, null);
        when(userDetailsService.createLoginUser(user)).thenReturn(loginUser);
        when(tokenService.createToken(any(LoginUser.class))).thenReturn("jwt-token-abc");

        MemberLoginDTO dto = new MemberLoginDTO();
        dto.setAccount("user_a001");
        dto.setPassword("Pass1234");

        Map<String, Object> result = memberAppService.login(dto);

        assertNotNull(result.get("token"));
        verify(memberMapper).selectByMemberCode("user_a001");
        verify(tokenService).createToken(any(LoginUser.class));
    }

    @Test
    void login_disabledMember_rejected()
    {
        BizMember member = activeMember();
        member.setStatus(MemberConstants.STATUS_DISABLED);
        member.setPassword(SecurityUtils.encryptPassword("Pass1234"));
        when(memberMapper.selectByMobile("17700001111")).thenReturn(member);

        MemberLoginDTO dto = new MemberLoginDTO();
        dto.setAccount("17700001111");
        dto.setPassword("Pass1234");

        ServiceException ex = assertThrows(ServiceException.class, () -> memberAppService.login(dto));
        assertEquals("账号已禁用，请联系客服", ex.getMessage());
    }

    @Test
    void getProfile_returnsVoWithSysUserFields()
    {
        BizMember member = activeMember();
        member.setNickName("买家A");
        member.setEmail("a@test.com");
        member.setSex("0");
        when(memberMapper.selectById(1L)).thenReturn(member);

        MemberProfileAppVO vo = memberAppService.getProfile(1L);

        assertEquals(1L, vo.getMemberId());
        assertEquals("买家A", vo.getNickName());
        assertEquals("a@test.com", vo.getEmail());
        assertEquals("0", vo.getSex());
    }

    @Test
    void updateProfile_updatesSysUserByMemberId()
    {
        when(memberMapper.selectById(1L)).thenReturn(activeMember());

        MemberProfileUpdateDTO dto = new MemberProfileUpdateDTO();
        dto.setNickName("新昵称");
        dto.setEmail("new@test.com");
        dto.setSex("1");

        memberAppService.updateProfile(1L, dto);

        verify(memberMapper).updateAccount(any(BizMember.class));
    }

    @Test
    void changePassword_wrongOldPassword_rejected()
    {
        BizMember member = activeMember();
        member.setPassword(SecurityUtils.encryptPassword("Pass1234"));
        when(memberMapper.selectById(1L)).thenReturn(member);

        MemberChangePasswordDTO dto = new MemberChangePasswordDTO();
        dto.setOldPassword("wrong");
        dto.setNewPassword("New12345");
        dto.setConfirmPassword("New12345");

        ServiceException ex = assertThrows(ServiceException.class, () -> memberAppService.changePassword(1L, dto));
        assertEquals("旧密码错误", ex.getMessage());
    }

    @Test
    void changePassword_success()
    {
        BizMember member = activeMember();
        member.setPassword(SecurityUtils.encryptPassword("Pass1234"));
        when(memberMapper.selectById(1L)).thenReturn(member);

        MemberChangePasswordDTO dto = new MemberChangePasswordDTO();
        dto.setOldPassword("Pass1234");
        dto.setNewPassword("New12345");
        dto.setConfirmPassword("New12345");

        memberAppService.changePassword(1L, dto);

        verify(memberMapper).updateAccount(any(BizMember.class));
    }

    private MemberRegisterDTO registerDto()
    {
        MemberRegisterDTO dto = new MemberRegisterDTO();
        dto.setMobile("17700001111");
        dto.setPassword("Pass1234");
        dto.setConfirmPassword("Pass1234");
        return dto;
    }

    private BizMember activeMember()
    {
        BizMember m = new BizMember();
        m.setMemberId(1L);
        m.setMobile("17700001111");
        m.setStatus(MemberConstants.STATUS_ENABLED);
        return m;
    }

    private SysUser sysUser(Long userId, String userName)
    {
        SysUser user = new SysUser();
        user.setUserId(userId);
        user.setUserName(userName);
        user.setStatus(MemberConstants.STATUS_ENABLED);
        return user;
    }
}
