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

import static org.junit.jupiter.api.Assertions.assertEquals;
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.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.Collections;
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.system.mapper.SysUserMapper;
import com.ruoyi.system.mapper.SysUserRoleMapper;
import com.ruoyi.system.service.ISysUserService;
import com.ruoyi.web.modules.account.constant.MerchantEmployeeConstants;
import com.ruoyi.web.modules.account.domain.MerchantEmployeeQuery;
import com.ruoyi.web.modules.account.domain.MerchantEmployeeRow;
import com.ruoyi.web.modules.account.dto.MerchantEmployeeCreateDTO;
import com.ruoyi.web.modules.account.dto.MerchantEmployeePasswordDTO;
import com.ruoyi.web.modules.account.mapper.MerchantEmployeeMapper;
import com.ruoyi.web.modules.account.service.impl.MerchantEmployeeServiceImpl;
import com.ruoyi.web.modules.account.support.MerchantEmployeeSupport;
import com.ruoyi.web.modules.account.vo.MerchantEmployeeListVO;
import com.ruoyi.web.modules.merchant.mapper.BizMerchantMapper;
import com.ruoyi.web.modules.store.facade.IShopGlobalConfigFacade;
import com.ruoyi.web.modules.store.mapper.BizMerchantAccountMapper;
import com.ruoyi.web.modules.store.mapper.BizShopMapper;

@ExtendWith(MockitoExtension.class)
class MerchantEmployeeServiceImplTest
{
    private static final Long MERCHANT_ID = 1L;

    private static final Long SHOP_ID = 101L;

    @Mock
    private MerchantEmployeeMapper merchantEmployeeMapper;

    @Mock
    private MerchantEmployeeSupport merchantEmployeeSupport;

    @Mock
    private BizMerchantAccountMapper merchantAccountMapper;

    @Mock
    private BizMerchantMapper merchantMapper;

    @Mock
    private BizShopMapper shopMapper;

    @Mock
    private ISysUserService sysUserService;

    @Mock
    private SysUserMapper sysUserMapper;

    @Mock
    private SysUserRoleMapper sysUserRoleMapper;

    @Mock
    private IShopGlobalConfigFacade shopGlobalConfigFacade;

    @InjectMocks
    private MerchantEmployeeServiceImpl merchantEmployeeService;

    @Test
    void selectEmployeeList_masksMobileAndMarksOperable()
    {
        MerchantEmployeeRow employee = new MerchantEmployeeRow();
        employee.setUserId(20L);
        employee.setUserName("13900002001");
        employee.setNickName("王小二");
        employee.setAccountType(MerchantEmployeeConstants.ACCOUNT_TYPE_EMPLOYEE);

        MerchantEmployeeQuery query = new MerchantEmployeeQuery();
        query.setMerchantId(MERCHANT_ID);
        query.setShopId(SHOP_ID);
        when(merchantEmployeeMapper.selectList(any(MerchantEmployeeQuery.class)))
                .thenReturn(Collections.singletonList(employee));
        when(merchantEmployeeSupport.resolveMobile(employee)).thenReturn("13900002001");

        MerchantEmployeeListVO vo = merchantEmployeeService.selectEmployeeList(query).get(0);

        assertEquals("139****2001", vo.getMobile());
        assertEquals(Boolean.TRUE, vo.getOperable());
    }

    @Test
    void fillListScope_setsMerchantAndShop()
    {
        MerchantEmployeeQuery query = new MerchantEmployeeQuery();
        when(merchantEmployeeSupport.requireMerchantId(10L)).thenReturn(MERCHANT_ID);
        when(merchantEmployeeSupport.requireCurrentShopId(MERCHANT_ID)).thenReturn(SHOP_ID);

        merchantEmployeeService.fillListScope(query, 10L);

        assertEquals(MERCHANT_ID, query.getMerchantId());
        assertEquals(SHOP_ID, query.getShopId());
    }

    @Test
    void createEmployee_insertsUserAndBinding()
    {
        MerchantEmployeeCreateDTO dto = buildCreateDto();
        when(merchantEmployeeSupport.requireMerchantId(10L)).thenReturn(MERCHANT_ID);
        when(merchantEmployeeSupport.requireCurrentShopId(MERCHANT_ID)).thenReturn(SHOP_ID);
        when(sysUserService.checkUserNameUnique(any(SysUser.class))).thenReturn(true);

        merchantEmployeeService.createEmployee(dto, 10L, "admin");

        verify(merchantEmployeeSupport).assertQuotaAvailable(MERCHANT_ID);
        verify(merchantEmployeeSupport).validateCreate(dto, SHOP_ID);
        verify(sysUserService).insertUser(any(SysUser.class));
        verify(merchantAccountMapper).insert(any());
    }

    @Test
    void deleteEmployees_logicDeletesBindingAndUser()
    {
        MerchantEmployeeRow row = new MerchantEmployeeRow();
        row.setUserId(20L);
        row.setAccountType(MerchantEmployeeConstants.ACCOUNT_TYPE_EMPLOYEE);

        when(merchantEmployeeSupport.requireMerchantId(10L)).thenReturn(MERCHANT_ID);
        when(merchantEmployeeSupport.requireCurrentShopId(MERCHANT_ID)).thenReturn(SHOP_ID);
        when(merchantEmployeeSupport.requireEmployeeRow(MERCHANT_ID, SHOP_ID, 20L)).thenReturn(row);

        merchantEmployeeService.deleteEmployees(new Long[] { 20L }, 10L, "admin");

        verify(merchantAccountMapper).logicDeleteEmployeeByUserId(20L, "admin");
        verify(sysUserRoleMapper).deleteUserRoleByUserId(20L);
        verify(sysUserMapper).deleteUserById(20L);
    }

    @Test
    void deleteEmployees_rejectsAdminRow()
    {
        when(merchantEmployeeSupport.requireMerchantId(10L)).thenReturn(MERCHANT_ID);
        when(merchantEmployeeSupport.requireCurrentShopId(MERCHANT_ID)).thenReturn(SHOP_ID);
        when(merchantEmployeeSupport.requireEmployeeRow(MERCHANT_ID, SHOP_ID, 10L))
                .thenThrow(new ServiceException(MerchantEmployeeConstants.MSG_CANNOT_OPERATE_ADMIN));

        assertThrows(ServiceException.class,
                () -> merchantEmployeeService.deleteEmployees(new Long[] { 10L }, 10L, "admin"));

        verify(merchantAccountMapper, never()).logicDeleteEmployeeByUserId(eq(10L), eq("admin"));
    }

    @Test
    void updatePassword_encryptsPassword()
    {
        MerchantEmployeePasswordDTO dto = new MerchantEmployeePasswordDTO();
        dto.setUserId(20L);
        dto.setPassword("Employee@123");
        dto.setConfirmPassword("Employee@123");

        MerchantEmployeeRow row = new MerchantEmployeeRow();
        row.setUserId(20L);
        row.setAccountType(MerchantEmployeeConstants.ACCOUNT_TYPE_EMPLOYEE);

        when(merchantEmployeeSupport.requireMerchantId(10L)).thenReturn(MERCHANT_ID);
        when(merchantEmployeeSupport.requireCurrentShopId(MERCHANT_ID)).thenReturn(SHOP_ID);
        when(merchantEmployeeSupport.requireEmployeeRow(MERCHANT_ID, SHOP_ID, 20L)).thenReturn(row);

        merchantEmployeeService.updatePassword(dto, 10L, "admin");

        verify(merchantAccountMapper).updateSysUserProfile(eq(20L), eq(null), eq(null), eq(null), eq(null),
                org.mockito.ArgumentMatchers.anyString(), eq("admin"));
    }

    private MerchantEmployeeCreateDTO buildCreateDto()
    {
        MerchantEmployeeCreateDTO dto = new MerchantEmployeeCreateDTO();
        dto.setEmployeeName("王小二");
        dto.setMobile("13900002001");
        dto.setEmail("wx@example.com");
        dto.setStatus(MerchantEmployeeConstants.STATUS_NORMAL);
        dto.setRoleIds(new Long[] { 201L });
        return dto;
    }
}
