package com.simuwang.manage.service.impl.system;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.simuwang.base.common.exception.APIException;
import com.simuwang.base.common.support.MybatisPage;
import com.simuwang.base.common.support.command.BaseAddCmd;
import com.simuwang.base.common.support.command.BaseEditCmd;
import com.simuwang.base.common.support.command.BaseIdStatusCmd;
import com.simuwang.base.common.support.command.BaseIdsCmd;
import com.simuwang.base.common.util.TreeUtil;
import com.simuwang.base.components.UserAuthService;
import com.simuwang.base.mapper.system.SysMenuMapper;
import com.simuwang.base.pojo.dos.sys.SysMenuDO;
import com.simuwang.base.pojo.dto.sys.MenuAddCmd;
import com.simuwang.base.pojo.dto.sys.MenuEditCmd;
import com.simuwang.base.pojo.dto.sys.MenuTreeQuery;
import com.simuwang.base.pojo.vo.sys.SysMenuVO;
import com.simuwang.manage.dto.MenuTreeDTO;
import com.simuwang.manage.service.system.SysMenuService;
import com.simuwang.shiro.core.ShiroUser;
import com.simuwang.shiro.utils.UserUtils;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

@Service
public class SysMenuServiceImpl implements SysMenuService {
    private final SysMenuMapper mapper;
    private final UserAuthService userAuthService;

    public SysMenuServiceImpl(SysMenuMapper mapper, UserAuthService userAuthService) {
        this.mapper = mapper;
        this.userAuthService = userAuthService;
    }

    @Override
    public MybatisPage<SysMenuVO> convertPage(Page<SysMenuDO> page, QueryWrapper<SysMenuDO> wrapper) {
        throw new APIException("此接口菜单管理没有分页方法");
    }

    @Override
    public <C extends BaseAddCmd<SysMenuDO>> void insert(C command) {
        MenuAddCmd cmd = (MenuAddCmd) command;
        SysMenuDO entity = cmd.toEntity();
        this.mapper.insert(entity);
    }

    @Override
    public <C extends BaseEditCmd<SysMenuDO>> void update(C command) {
        MenuEditCmd cmd = (MenuEditCmd) command;
        SysMenuDO entity = cmd.toEntity();
        this.mapper.updateById(entity);
    }

    @Override
    public <C extends BaseIdStatusCmd> void updateStatus(C command) {
        SysMenuDO entity = new SysMenuDO();
        entity.setMenuId(command.getId());
        entity.setStatus(command.getStatus());
        this.mapper.updateById(entity);
    }

    @Override
    public <C extends BaseIdsCmd> void delete(C command) {
        List<Integer> ids = command.getIds();
        this.mapper.deleteByIds(ids);
    }

    @Override
    public List<MenuTreeDTO> listMenuTrees(MenuTreeQuery query) {
        // 先获取用户的所有菜单
        ShiroUser shiroUser = UserUtils.getLoginUser();
        Integer userId = shiroUser.getUserId();
        List<SysMenuDO> dataList = this.userAuthService.listUserMenuByUserId(userId);
        // 当有查询条件时
        if (query.getStatus() != null || StrUtil.isNotBlank(query.getKeyword())) {
            // 按条件过滤找到所有满足条件的菜单
            List<SysMenuDO> menuDos = ListUtil.toList(dataList);
            if (StrUtil.isNotBlank(query.getKeyword())) {
                menuDos = menuDos.stream().filter(e -> e.getMenuName().contains(query.getKeyword())).collect(Collectors.toList());
            }
            if (query.getStatus() != null) {
                menuDos = menuDos.stream().filter(e -> Objects.equals(e.getStatus(), query.getStatus())).collect(Collectors.toList());
            }
            if (CollUtil.isEmpty(menuDos)) {
                return ListUtil.empty();
            }
            // 从树结构中过滤不满足条件的数据
            List<Integer> menuIds = menuDos.stream().map(SysMenuDO::getMenuId).collect(Collectors.toList());
            dataList = this.findMenus(dataList, menuIds);
        }
        // 构建菜单树
        List<MenuTreeDTO> tempList = dataList.stream().map(MenuTreeDTO::new).collect(Collectors.toList());
        return TreeUtil.list2Tree(tempList, MenuTreeDTO::getId, MenuTreeDTO::getPid,
                MenuTreeDTO::getChildren, MenuTreeDTO::setChildren, 0, MenuTreeDTO::getSort);
    }

    /**
     * 从用户所有菜单中查找所有满足条件的菜单和其所有父级菜单列表
     *
     * @param dataList 用户所有菜单数据
     * @param menuIds  满足条件的所有菜单id
     * @return 满足条件的所有菜单id和其所有父级菜单id集合
     */
    private List<SysMenuDO> findMenus(List<SysMenuDO> dataList, List<Integer> menuIds) {
        List<SysMenuDO> resultList = ListUtil.list(false);
        for (Integer menuId : menuIds) {
            List<SysMenuDO> tempList = ListUtil.list(false);
            this.recursionMenus(dataList, menuId, tempList);
            CollUtil.addAllIfNotContains(resultList, tempList);
        }
        return resultList;
    }

    /**
     * 递归找当前菜单和所有父级菜单
     *
     * @param dataList   用户所有菜单列表
     * @param menuId     要查找的菜单id
     * @param resultList 查找后存储的数据集合
     */
    private void recursionMenus(List<SysMenuDO> dataList, Integer menuId, List<SysMenuDO> resultList) {
        SysMenuDO result = dataList.stream().filter(e -> Objects.equals(menuId, e.getMenuId())).findFirst().orElse(null);
        if (result == null) {
            return;
        }
        resultList.add(result);
        Integer parentId = result.getParentId();
        this.recursionMenus(dataList, parentId, resultList);
    }
}
