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

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.never;
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.exception.ServiceException;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.system.service.ISysUserService;
import com.ruoyi.web.modules.account.constant.AccountManageConstants;
import com.ruoyi.web.modules.account.dto.AccountPasswordUpdateDTO;
import com.ruoyi.web.modules.account.dto.AccountProfileUpdateDTO;
import com.ruoyi.web.modules.account.service.impl.AccountProfileServiceImpl;
import com.ruoyi.web.modules.account.support.AccountManageSupport;
import com.ruoyi.web.modules.store.domain.BizMerchantAccount;
import com.ruoyi.web.modules.store.mapper.BizMerchantAccountMapper;

@ExtendWith(MockitoExtension.class)
class AccountProfileServiceImplTest
{
    @Mock
    private AccountManageSupport accountManageSupport;

    @Mock
    private BizMerchantAccountMapper merchantAccountMapper;

    @Mock
    private ISysUserService sysUserService;

    @InjectMocks
    private AccountProfileServiceImpl accountProfileService;

    @Test
    void updateProfile_merchantAdminMobileChangeNeedsConfirm()
    {
        AccountProfileUpdateDTO dto = buildProfile("13900139000", false);
        BizMerchantAccount binding = buildBinding(10L);
        SysUser user = buildUser(10L, "13800138000");

        doNothing().when(accountManageSupport).validateProfileUpdate(dto);
        when(accountManageSupport.requireSysUser(10L)).thenReturn(user);
        when(accountManageSupport.findMerchantAdminBinding(10L)).thenReturn(binding);
        when(accountManageSupport.resolveMobile(user, true)).thenReturn("13800138000");

        Map<String, Object> result = accountProfileService.updateProfile(dto, "admin");

        assertTrue((Boolean) result.get("needConfirm"));
        verify(merchantAccountMapper, never()).updateSysUserProfile(any(), any(), any(), any(), any(), any(), any());
    }

    @Test
    void updateProfile_nonMerchantMobileChangeDirectSuccess()
    {
        AccountProfileUpdateDTO dto = buildProfile("13900139000", false);
        SysUser user = buildUser(10L, "member001");
        user.setPhonenumber("13800138000");

        doNothing().when(accountManageSupport).validateProfileUpdate(dto);
        when(accountManageSupport.requireSysUser(10L)).thenReturn(user);
        when(accountManageSupport.findMerchantAdminBinding(10L)).thenReturn(null);
        when(accountManageSupport.resolveMobile(user, false)).thenReturn("13800138000");
        when(sysUserService.checkPhoneUnique(any(SysUser.class))).thenReturn(true);

        Map<String, Object> result = accountProfileService.updateProfile(dto, "admin");

        assertFalse((Boolean) result.get("needConfirm"));
        verify(merchantAccountMapper).updateSysUserProfile(
                eq(10L), isNull(), eq("张老板"), isNull(), eq("13900139000"), isNull(), eq("admin"));
    }

    @Test
    void updatePassword_encryptsPassword()
    {
        AccountPasswordUpdateDTO dto = new AccountPasswordUpdateDTO();
        dto.setUserId(10L);
        dto.setPassword("Merchant@123");
        dto.setConfirmPassword("Merchant@123");

        doNothing().when(accountManageSupport).validatePasswordUpdate(dto);
        when(accountManageSupport.requireSysUser(10L)).thenReturn(buildUser(10L, "13800138000"));
        when(accountManageSupport.findMerchantAdminBinding(10L)).thenReturn(null);

        accountProfileService.updatePassword(dto, "admin");

        org.mockito.ArgumentCaptor<String> captor = org.mockito.ArgumentCaptor.forClass(String.class);
        verify(merchantAccountMapper).updateSysUserProfile(
                eq(10L), isNull(), isNull(), isNull(), isNull(), captor.capture(), eq("admin"));
        assertTrue(SecurityUtils.matchesPassword("Merchant@123", captor.getValue()));
    }

    @Test
    void updateProfile_mobileDuplicateFails()
    {
        AccountProfileUpdateDTO dto = buildProfile("13900139000", true);
        BizMerchantAccount binding = buildBinding(10L);
        SysUser user = buildUser(10L, "13800138000");

        doNothing().when(accountManageSupport).validateProfileUpdate(dto);
        when(accountManageSupport.requireSysUser(10L)).thenReturn(user);
        when(accountManageSupport.findMerchantAdminBinding(10L)).thenReturn(binding);
        when(accountManageSupport.resolveMobile(user, true)).thenReturn("13800138000");
        when(sysUserService.checkUserNameUnique(any(SysUser.class))).thenReturn(false);

        ServiceException ex = assertThrows(ServiceException.class,
                () -> accountProfileService.updateProfile(dto, "admin"));
        assertEquals(AccountManageConstants.MSG_MOBILE_EXISTS, ex.getMessage());
    }

    private AccountProfileUpdateDTO buildProfile(String mobile, boolean confirm)
    {
        AccountProfileUpdateDTO dto = new AccountProfileUpdateDTO();
        dto.setUserId(10L);
        dto.setAccountName("张老板");
        dto.setMobile(mobile);
        dto.setConfirm(confirm);
        return dto;
    }

    private BizMerchantAccount buildBinding(Long userId)
    {
        BizMerchantAccount binding = new BizMerchantAccount();
        binding.setAccountId(userId);
        return binding;
    }

    private SysUser buildUser(Long userId, String userName)
    {
        SysUser user = new SysUser();
        user.setUserId(userId);
        user.setUserName(userName);
        user.setDelFlag("0");
        return user;
    }
}
