package com.ruoyi.web.modules.role.support;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

import com.ruoyi.web.modules.account.support.MerchantEmployeeSupport;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.ruoyi.common.core.domain.entity.SysMenu;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.mapper.SysMenuMapper;
import com.ruoyi.web.modules.role.constant.MerchantRoleConstants;
import com.ruoyi.web.modules.role.domain.MerchantRoleRow;
import com.ruoyi.web.modules.role.dto.MerchantRoleCreateDTO;
import com.ruoyi.web.modules.role.dto.MerchantRoleUpdateDTO;
import com.ruoyi.web.modules.role.mapper.MerchantRoleMapper;

/**
 * 角色管理校验与上下文
 */
@Component
public class MerchantRoleSupport
{
    @Autowired
    private MerchantEmployeeSupport merchantEmployeeSupport;

    @Autowired
    private MerchantRoleMapper merchantRoleMapper;

    @Autowired
    private SysMenuMapper sysMenuMapper;

    public Long requireMerchantId(Long operatorUserId)
    {
        return merchantEmployeeSupport.requireMerchantId(operatorUserId);
    }

    public Long requireCurrentShopId(Long merchantId)
    {
        return merchantEmployeeSupport.requireCurrentShopId(merchantId);
    }

    public MerchantRoleRow requireRoleRow(Long shopId, Long roleId)
    {
        MerchantRoleRow row = merchantRoleMapper.selectByRoleId(shopId, roleId);
        if (row == null)
        {
            throw new ServiceException(MerchantRoleConstants.MSG_NOT_FOUND);
        }
        return row;
    }

    public void assertRoleNameUnique(Long shopId, String roleName, Long excludeRoleId)
    {
        if (merchantRoleMapper.countRoleNameInShop(shopId, roleName, excludeRoleId) > 0)
        {
            throw new ServiceException(MerchantRoleConstants.MSG_ROLE_NAME_EXISTS);
        }
    }

    public void assertMenuIdsAssignable(Long[] menuIds)
    {
        if (menuIds == null || menuIds.length == 0)
        {
            throw new ServiceException(MerchantRoleConstants.MSG_MENU_REQUIRED);
        }
        Set<Long> allowed = listAssignableMenuIdSet();
        for (Long menuId : menuIds)
        {
            if (menuId == null || !allowed.contains(menuId))
            {
                throw new ServiceException(MerchantRoleConstants.MSG_MENU_INVALID);
            }
        }
    }

    public void assertDeletable(Long shopId, Long roleId)
    {
        if (merchantRoleMapper.countEmployeeBindByRole(shopId, roleId) > 0)
        {
            throw new ServiceException(MerchantRoleConstants.MSG_ROLE_BOUND);
        }
    }

    public void validateCreate(MerchantRoleCreateDTO dto, Long shopId)
    {
        if (dto == null)
        {
            throw new ServiceException(MerchantRoleConstants.MSG_ROLE_NAME_REQUIRED);
        }
        validateRoleName(dto.getRoleName());
        validateStatus(dto.getStatus());
        assertRoleNameUnique(shopId, dto.getRoleName(), null);
        assertMenuIdsAssignable(dto.getMenuIds());
    }

    public void validateUpdate(MerchantRoleUpdateDTO dto, Long shopId)
    {
        if (dto == null || dto.getRoleId() == null)
        {
            throw new ServiceException(MerchantRoleConstants.MSG_NOT_FOUND);
        }
        validateRoleName(dto.getRoleName());
        validateStatus(dto.getStatus());
        assertRoleNameUnique(shopId, dto.getRoleName(), dto.getRoleId());
        assertMenuIdsAssignable(dto.getMenuIds());
    }

    public String generateRoleKey(Long shopId)
    {
        return MerchantRoleConstants.ROLE_KEY_PREFIX + shopId + "_"
                + UUID.randomUUID().toString().replace("-", "").substring(0, 8);
    }

    public List<SysMenu> listAssignableMenus()
    {
        SysMenu query = new SysMenu();
        query.setStatus("0");
        List<SysMenu> allMenus = sysMenuMapper.selectMenuList(query);
        Set<Long> assignableIds = listAssignableMenuIdSet(allMenus);
        List<SysMenu> result = new ArrayList<>();
        for (SysMenu menu : allMenus)
        {
            if (assignableIds.contains(menu.getMenuId()))
            {
                result.add(menu);
            }
        }
        return result;
    }

    public Set<Long> listAssignableMenuIdSet()
    {
        SysMenu query = new SysMenu();
        query.setStatus("0");
        return listAssignableMenuIdSet(sysMenuMapper.selectMenuList(query));
    }

    private Set<Long> listAssignableMenuIdSet(List<SysMenu> allMenus)
    {
        Map<Long, SysMenu> menuMap = new HashMap<>();
        for (SysMenu menu : allMenus)
        {
            menuMap.put(menu.getMenuId(), menu);
        }
        Set<Long> assignableIds = new HashSet<>();
        for (SysMenu menu : allMenus)
        {
            if (isSellerMenu(menu))
            {
                assignableIds.add(menu.getMenuId());
                addAncestorMenuIds(menu, menuMap, assignableIds);
            }
        }
        return assignableIds;
    }

    private boolean isSellerMenu(SysMenu menu)
    {
        return menu.getPerms() != null && menu.getPerms().startsWith(MerchantRoleConstants.SELLER_PERM_PREFIX);
    }

    private void addAncestorMenuIds(SysMenu menu, Map<Long, SysMenu> menuMap, Set<Long> assignableIds)
    {
        Long parentId = menu.getParentId();
        while (parentId != null && parentId > 0)
        {
            SysMenu parent = menuMap.get(parentId);
            if (parent == null || assignableIds.contains(parentId))
            {
                break;
            }
            assignableIds.add(parentId);
            parentId = parent.getParentId();
        }
    }

    private void validateRoleName(String roleName)
    {
        if (StringUtils.isEmpty(roleName))
        {
            throw new ServiceException(MerchantRoleConstants.MSG_ROLE_NAME_REQUIRED);
        }
    }

    private void validateStatus(String status)
    {
        if (StringUtils.isEmpty(status))
        {
            throw new ServiceException(MerchantRoleConstants.MSG_STATUS_REQUIRED);
        }
        if (!MerchantRoleConstants.STATUS_NORMAL.equals(status)
                && !MerchantRoleConstants.STATUS_DISABLED.equals(status))
        {
            throw new ServiceException("状态参数无效");
        }
    }
}
