package com.zsc.edu.gateway.modules.system.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zsc.edu.gateway.exception.ConstraintException;
import com.zsc.edu.gateway.framework.security.UserDetailsImpl;
import com.zsc.edu.gateway.modules.system.dto.MenuDto;
import com.zsc.edu.gateway.modules.system.entity.Menu;
import com.zsc.edu.gateway.modules.system.entity.RoleMenu;
import com.zsc.edu.gateway.modules.system.mapper.MenuMapper;
import com.zsc.edu.gateway.modules.system.repo.MenuRepository;
import com.zsc.edu.gateway.modules.system.repo.RoleMenuRepository;
import com.zsc.edu.gateway.modules.system.service.MenuService;
import com.zsc.edu.gateway.modules.system.utils.TreeUtil;
import com.zsc.edu.gateway.modules.system.vo.MenuVo;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * @author zhuang
 */
@AllArgsConstructor
@Service
public class MenuServiceImpl extends ServiceImpl<MenuRepository, Menu> implements MenuService {
    private MenuMapper mapper;
    private RoleMenuRepository roleMenuRepository;
    @Override
    public List<Menu> selectByRoleId(Long roleId) {
        return baseMapper.selectByRoleId(roleId);
    }

    @Override
    public List<Menu> selectByUserId(Long userId, Menu.Type type) {
        return baseMapper.selectByUserId(userId, type);
    }

    @Override
    @Transactional
    public Menu create(MenuDto dto) {
        if (baseMapper.exists(new LambdaQueryWrapper<Menu>().eq(Menu::getName, dto.getName()))) {
            throw new ConstraintException("该菜单名已存在!请检查输入表单是否出错!");
        }
        if (baseMapper.exists(new LambdaQueryWrapper<Menu>().eq(Menu::getPermissions, dto.getPermissions()))) {
            throw new ConstraintException("该权限已存在!请检查输入表单是否出错!");
        }
        if (baseMapper.selectOne(new LambdaQueryWrapper<Menu>().eq(Menu::getId, dto.getPid())) == null && dto.getPid() != null) {
            throw new ConstraintException("上级菜单不存在!请检查输入表单是否出错!");
        }
        Menu menu = mapper.toEntity(dto);
        baseMapper.insert(menu);
        return menu;
    }

    @Override
    @Transactional
    public Menu update(MenuDto dto, Long id) {
        Menu menu = baseMapper.selectById(id);
        if (menu == null) {
            throw new ConstraintException("菜单不存在!请检查输入ID是否正确!");
        }
        mapper.convert(dto, menu);
        baseMapper.updateById(menu);
        return menu;
    }

    @Override
    public Boolean delete(Long id) {
        LambdaQueryWrapper<RoleMenu> queryWrapper = new LambdaQueryWrapper<>();
        roleMenuRepository.delete(queryWrapper.eq(RoleMenu::getMenuId, id));
        return removeById(id);
    }

    @Override
    public List<MenuVo> getTree(UserDetailsImpl userDetails, String name) {
        if (Objects.equals(userDetails.getUsername(), "admin")) {
            List<MenuVo> menus = baseMapper.selectAll().stream().map(MenuVo::new).toList();
            return createTree(menus, name);
        }
        List<MenuVo> menuVos = selectByUserId(userDetails.getId(), Menu.Type.PAGE).stream().map(MenuVo::new).toList();
        return createTree(menuVos, name);
    }

    public List<MenuVo> createTree(List<MenuVo> menuVos, String name) {
        List<MenuVo> menuTrees = TreeUtil.makeTree(
                menuVos,
                menuVo -> menuVo.getPid() == null || menuVo.getPid() == -1L,
                (parent, child) -> parent.getId().equals(child.getPid()),
                MenuVo::setChildren
        );
        if (Objects.equals(name, "all")) {
            return menuTrees;
        }
        if (Objects.nonNull(name)) {
            Menu menu = baseMapper.selectOne(new LambdaQueryWrapper<Menu>().eq(Menu::getName, name));
            if (menu == null) {
                throw new ConstraintException("此名称不存在，请重新输入!");
            }
            List<MenuVo> menuTree = new ArrayList<>();
            TreeUtil.forLevelOrder(menuTrees, node -> {
                if (node.getId().equals(menu.getId())) {
                    menuTree.add(node);
                }
            }, MenuVo::getChildren);
            return menuTree;
        }
        return menuTrees;
    }

    @Override
    public Menu detail(Long id) {
        return baseMapper.selectById(id);
    }
}
