package com.yfd.platform.system.service.impl;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yfd.platform.system.domain.SysMenu;
import com.yfd.platform.system.domain.SysRole;
import com.yfd.platform.system.mapper.SysMenuMapper;
import com.yfd.platform.system.mapper.SysRoleMapper;
import com.yfd.platform.system.service.ISysMenuService;
import com.yfd.platform.utils.FileUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ResourceUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.File;
import java.io.FileNotFoundException;
import java.sql.Timestamp;
import java.text.DecimalFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 菜单及按钮 服务实现类
 * </p>
 *
 * @author zhengsl
 * @since 2021-12-15
 */
@Service
@Transactional
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> implements ISysMenuService {

    @Resource
    private SysMenuMapper sysMenuMapper;

    @Resource
    private UserServiceImpl currentUser;

    @Resource
    private SysRoleMapper sysRoleMapper;

    //菜单图片路径
    @Value("${file-space.system}")
    private String sysetmPath;

    /***********************************
     * 用途说明：查询菜单及按钮树状图
     * 参数说明
     * systemcode     系统
     *isdisplay       是否显示
     * 返回值说明:  菜单结构树集合
     ***********************************/
    @Override
    public List<Map<String, Object>> getMenuButtonTree(String systemcode,
                                                       String name,
                                                       String isdisplay) {
        List<Map<String, Object>> listMap=null;
        if(StrUtil.isEmpty(name)){//不带名称查询，返回树结构
            QueryWrapper<SysMenu> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("parentid", "0").eq("systemcode", systemcode).orderByAsc("orderno");
            listMap = this.listMaps(queryWrapper);

            for (int i = 0; i < listMap.size(); i++) {
                //查询下一子集
                List<Map<String, Object>> childList = child(listMap.get(i).get(
                        "id").toString(), systemcode, name, null, null);
                listMap.get(i).put("children", childList);   //添加新列 子集
            }
        }else{  //根据菜单名称查询，直接返回类别
            QueryWrapper<SysMenu> queryWrapper = new QueryWrapper<>();
            queryWrapper.like("name", name).eq("systemcode", systemcode).orderByAsc("name");
            listMap = this.listMaps(queryWrapper);
        }



        return listMap;
    }

    /***********************************
     * 用途说明：获取菜单结构树（不含按钮）
     * 参数说明
     *  systemcode      系统
     *  isdisplay       是否显示
     * 返回值说明:  菜单结构树集合
     ***********************************/
    @Override
    public List<Map<String, Object>> getMenuTree(String systemcode,
                                                 String name,
                                                 String isdisplay) {
        QueryWrapper<SysMenu> queryWrapper = new QueryWrapper<>();
        if (StrUtil.isNotEmpty(isdisplay)) {
            queryWrapper.eq("isdisplay", isdisplay);
        } else {
            queryWrapper.eq("isdisplay", 1);
        }

        //根据系统 ,类型不为2 显示，序号 正序排序
        queryWrapper.eq("parentid", "0").eq("systemcode", systemcode).ne(
                "type", "2").orderByAsc("orderno");
        List<Map<String, Object>> listMap = this.listMaps(queryWrapper);
        for (int i = 0; i < listMap.size(); i++) {
            List<Map<String, Object>> childList = child(listMap.get(i).get(
                    "id").toString(), systemcode, name, isdisplay, "2");//查询下一子集
            listMap.get(i).put("children", childList);   //添加新列 子集
        }
        return listMap;
    }

    /***********************************
     * 用途说明：查询菜单及按钮树状图
     * 参数说明
     * parentid     上级id
     *systemcode    系统
     * isdisplay    是否显示
     * type         按钮
     * 返回值说明:  菜单结构树集合
     ***********************************/
    private List<Map<String, Object>> child(String parentid,
                                            String systemcode, String name,
                                            String isdisplay, String type) {
        List<Map<String, Object>> listMap = new ArrayList<>();
        QueryWrapper<SysMenu> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("parentid", parentid).eq("systemcode", systemcode);
        //根据上级id 系统 查询
        if (StrUtil.isNotEmpty(type)) {
            queryWrapper.ne("type", type);
        }

        if (StrUtil.isNotEmpty(name)) {      //根据菜单名称查询
            queryWrapper.like("name", name);
        }
        listMap = this.listMaps(queryWrapper.orderByAsc("orderno"));
        if (listMap.size() > 0) {              //判断是否存在子集
            for (int i = 0; i < listMap.size(); i++) {             //遍历表数据
                List<Map<String, Object>> childList =
                        child(listMap.get(i).get("id").toString(), systemcode
                                , name, isdisplay, type); //循环获取下一子集
                listMap.get(i).put("children", childList); //添加新列 子集
            }
        }

        return listMap;
    }

    /***********************************
     * 用途说明：新增菜单及按钮
     * 参数说明
     * sysMenu        菜单或按钮表对象
     * 返回值说明:   是否添加成功提示
     ***********************************/
    @Override
    public boolean addMenu(SysMenu sysMenu) {
        String parentId = sysMenu.getParentid();
        QueryWrapper<SysMenu> queryWrapper = new QueryWrapper<>();
        //根据上级id 查询到总数 并累加
        int orderno = this.count(queryWrapper.eq("parentid",
                parentId)) + 1;
        // 生成排序号
        // 生成编号
        QueryWrapper<SysMenu> queryMaxCode = new QueryWrapper<>();
        queryMaxCode.eq("parentid",
                parentId);
        // 查询最大的编号
        List<Object> maxList = this.listObjs(queryMaxCode.select("max(code) " +
                "code").eq("systemcode", sysMenu.getSystemcode()));
        SysMenu parentMenu = sysMenuMapper.selectById(parentId);
        // 最大编号转换成int类型
        String maxCode = maxList.size() > 0 ? maxList.get(0).toString() : "0";
        int max = ObjectUtil.isEmpty(maxList) ? 0 :
                Integer.parseInt(maxCode);
        DecimalFormat df;
        if ("0".equals(sysMenu.getParentid())) {
            df = new DecimalFormat("00");
        } else if (parentMenu.getCode().length() == 2) {
            df = new DecimalFormat("0000");
        } else {
            df = new DecimalFormat("000000");
        }
        //DecimalFormat df = new DecimalFormat("00");
        //int parentCode = Integer.parseInt(parentMenu.getCode());
        String parentCode = "";
        if (parentMenu != null) {
            parentCode = parentMenu.getCode();
        }
        // 生成的新编号 年月日+4位编号
        String code;
        if (max > 0) {
            code = df.format(max + 1);
        } else {
            max = max + 1;
            if (StrUtil.isBlank(parentCode)) {
                parentCode = "0" + max;
            } else {
                int i = Integer.parseInt(parentCode);
                parentCode = i + "0" + max;
            }

            int format = Integer.parseInt(parentCode);
            code = df.format(format);
        }

        // 判断是否显示字段 是否填写 为空
        if (StrUtil.isEmpty(sysMenu.getIsdisplay())) {
            // 默认设置成 1 显示
            sysMenu.setIsdisplay("1");
        }
        // 判断是否填写父级id 为空 默认设置成 0
        if (StrUtil.isEmpty(sysMenu.getParentid())) {
            sysMenu.setParentid("0");
        }
        // 添加编号
        sysMenu.setCode(code);
        // 添加排序号
        sysMenu.setOrderno(orderno);
        // 添加最近修改人
        sysMenu.setLastmodifier(currentUser.getUsername());
        // 添加最近修改时间
        sysMenu.setLastmodifydate(new Timestamp(System.currentTimeMillis()));
        return this.save(sysMenu);
    }

    /***********************************
     * 用途说明：上传单个图标
     * 参数说明
     * id          上传图标id
     * icon        图标
     * 返回值说明:   是否上传成功
     ***********************************/
    @Override
    public boolean uploadIcon(String id, MultipartFile icon) {
        //根据id查询
        SysMenu sysMenu = this.getById(id);
        //图片路径
        String iconPath = sysetmPath + "menu" + File.separator;
        String iconname =
                IdUtil.fastSimpleUUID() + "." + FileUtil.getExtensionName(icon.getOriginalFilename());
        //上传图标并获取图标名称 (图片改为png格式)
        String filename =
                FileUtil.upload(icon, iconPath, iconname).getName();
        //更新图标名称
        sysMenu.setIcon(filename);
        //添加最近修改人
        sysMenu.setLastmodifier(currentUser.getUsername());
        //添加最近修改时间
        sysMenu.setLastmodifydate(new Timestamp(System.currentTimeMillis()));
        //更新数据
        boolean isOk = this.updateById(sysMenu);
        return isOk;
    }

    /***********************************
     * 用途说明：根据id删除单个图标
     * 参数说明
     * id          删除图标id
     * icon        图标名称
     * 返回值说明:   是否删除成功
     ***********************************/
    @Override
    public boolean deleteIcon(String id) {
        //根据id查询
        SysMenu sysMenu = this.getById(id);
        //图片路径
        String iconname =
                System.getProperty("user.dir") + "\\src\\main" +
                        "\\resources\\static\\icon" + File.separator + sysMenu.getIcon();
        //更新图标名称
        sysMenu.setIcon("");
        //添加最近修改人
        sysMenu.setLastmodifier(currentUser.getUsername());
        //添加最近修改时间
        sysMenu.setLastmodifydate(new Timestamp(System.currentTimeMillis()));
        //更新数据
        boolean isOk = this.updateById(sysMenu);
        //更新成功 删除图片
        if (isOk == true) {
            FileUtil.del(iconname);
        }
        return isOk;
    }

    /***********************************
     * 用途说明：菜单及按钮序号排序
     * 参数说明
     * parentid    上级id
     * id
     * orderno      更改后序号
     * 返回值说明:   是否更新成功
     ***********************************/
    @Override
    public boolean moveOrderno(String parentid, String id, int orderno) {
        boolean ok = true;
        SysMenu sysMenu = this.getById(id);       //根据id查询原顺序号
        if (sysMenu.getOrderno() > orderno) {
            ok = sysMenuMapper.upMoveOrderno(parentid, sysMenu.getOrderno(),
                    orderno);    //根据 父级id 小于原序号  大于等于更改序号
        } else {
            ok = sysMenuMapper.downMoveOrderno(parentid, sysMenu.getOrderno()
                    , orderno);   //根据 父级id 大于原序号 小于等于更改序号
        }
        UpdateWrapper<SysMenu> updateWrapper = new UpdateWrapper<>();
        ok = ok && this.update(updateWrapper.eq("id", id).set("orderno",
                orderno));  //根据 id修改序号
        return ok;
    }

    /***********************************
     * 用途说明：根据id删除菜单或按钮
     * 参数说明
     * id        删除列的id
     * 返回值说明:   是否删除成功
     ***********************************/
    @Override
    public boolean deleteById(String id) {
        //根据id查询
        SysMenu sysMenu = this.getById(id);
        //图片路径
        String iconname =
                sysetmPath + "menu" + File.separator + sysMenu.getIcon();
        //删除图标
        new File(iconname).delete();
        //根据id删除
        boolean isOk = this.removeById(id);
        //删除成功同步更新表数据
        if (isOk) {
            //1 创建list集合，用于封装所有删除目录或菜单id值
            List<String> idList = new ArrayList<>();
            this.selectPermissionChildById(id, idList);
            if (idList.size() > 0) {
                sysMenuMapper.deleteBatchIds(idList);
            }
            QueryWrapper<SysMenu> queryWrapper = new QueryWrapper<>();
            //根据上级id 查询 根据 orderno 正序排序
            queryWrapper.eq("parentid", sysMenu.getParentid()).orderByAsc(
                    "orderno");
            List<SysMenu> list = this.list(queryWrapper);
            for (int i = 0; i < list.size(); i++) {
                SysMenu menu = list.get(i);
                //更新序列号
                menu.setOrderno(i + 1);
            }
            //更新表数据
            this.updateBatchById(list);
        }
        return isOk;
    }

    //2 根据当前菜单id，查询菜单里面子菜单id，封装到list集合
    private void selectPermissionChildById(String id, List<String> idList) {
        //查询菜单里面子菜单id
        QueryWrapper<SysMenu> wrapper = new QueryWrapper<>();
        wrapper.eq("parentid", id);
        wrapper.select("id");
        List<SysMenu> childIdList = baseMapper.selectList(wrapper);
        //把childIdList里面菜单id值获取出来，封装idList里面，做递归查询
        childIdList.stream().forEach(item -> {
            //封装idList里面
            idList.add(item.getId());
            //递归查询
            this.selectPermissionChildById(item.getId(), idList);
        });
    }

    /**********************************
     * 用途说明: 调换菜单或按钮的位置
     * 参数说明 upperId 选中的菜单Id
     * 参数说明 belowId 切换的菜单Id
     * 返回值说明: boolean
     ***********************************/
    @Override
    @Transactional
    public boolean changeOderNoById(String fromId, String toId) {
        SysMenu fromSysMenu = this.getById(fromId);
        SysMenu toSysMenu = this.getById(toId);
        // 如果菜单或按钮不存在拖动失败
        if (fromSysMenu == null || toSysMenu == null) {
            return false;
        }
        Integer fromOrderNo = fromSysMenu.getOrderno();
        Integer toOrderNo = toSysMenu.getOrderno();
        // 如果菜单或按钮的顺序号不存在拖动失败
        if (fromOrderNo == null || toOrderNo == null) {
            return false;
        }
        fromSysMenu.setOrderno(toOrderNo);
        toSysMenu.setOrderno(fromOrderNo);
        boolean fromBool = this.updateById(fromSysMenu);
        boolean toBool = this.updateById(toSysMenu);
        return fromBool && toBool;
    }

    /**********************************
     * 用途说明:   根据用户id获取菜单树
     * 参数说明 id 用户id
     * 返回值说明: 返回菜单树
     ***********************************/
    @Override
    public List<Map<String, Object>> getMenuTree(String id) {
        // 根据id获取菜单
        //List<SysMenu> sysMenus = sysMenuMapper.selectMenuByUserId(id);
        List<Map<String, Object>> list;
        if (StrUtil.isBlank(id)) {
            LambdaQueryWrapper<SysMenu> queryWrapper =
                    new LambdaQueryWrapper<>();
            list = this.listMaps(queryWrapper.eq(SysMenu::getIsdisplay, "1").ne(SysMenu::getType, "2").eq(SysMenu::getSystemcode, "1").orderByAsc(SysMenu::getOrderno));
        } else {
            list = sysMenuMapper.selectMenuByUserId(id);
        }
        // 将菜单转换成树
        List<Map<String, Object>> sysMenus = buildTreeLeft(list);
        return sysMenus;
    }

    /***********************************
     * 用途说明：权限分配
     * 参数说明
     *  systemcode      系统
     *  name            名称
     *  isdisplay       是否显示
     * 返回值说明:  菜单结构树集合
     ***********************************/
    @Override
    public List<Map<String, Object>> permissionAssignment(String roleId) {

        String code = sysMenuMapper.getSystemCodeById(roleId);
        if (code == null) {
            code = "1";
        }
        LambdaQueryWrapper<SysMenu> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SysMenu::getSystemcode, code).select(SysMenu::getId,
                SysMenu::getParentid, SysMenu::getName).orderByAsc
                (SysMenu::getOrderno);
        List<Map<String, Object>> listAll =
                sysMenuMapper.selectMaps(queryWrapper);
        List<String> listRole =
                sysMenuMapper.selectMenuByRoleId(roleId);
        for (Map<String, Object> map : listAll) {
            String id = (String) map.get("id");
            if (listRole.contains(id)) {
                map.put("checkinfo", true);
            } else {
                map.put("checkinfo", false);
            }

        }
        List<Map<String, Object>> listTree = buildTrees(listAll);
        return listTree;
    }

    // 另一种方法
    /*public List<Map<String, Object>> permissionAssignment(String roleId) {

        String code = sysMenuMapper.getSystemCodeById(roleId);
        if (code == null) {
            code = "1";
        }
        LambdaQueryWrapper<SysMenu> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SysMenu::getSystemcode, code).select(SysMenu::getId,
                SysMenu::getParentid, SysMenu::getName).orderByAsc
                (SysMenu::getOrderno);
        List<Map<String, Object>> listAll =
                sysMenuMapper.selectMaps(queryWrapper);
        *//*List<String> listRole =
                sysMenuMapper.selectMenuByRoleId(roleId);*//*
        SysRole sysRole = sysRoleMapper.selectById(roleId);
        String optscope = sysRole.getOptscope();
        // 将当前角色所对应权限id拆分
        String[] split = optscope.split(",");
        List<String> listRole = Arrays.asList(split);
        for (Map<String, Object> map : listAll) {
            String id = (String) map.get("id");
            if (listRole.contains(id)) {
                map.put("checkinfo", true);
            } else {
                map.put("checkinfo", false);
            }

        }
        List<Map<String, Object>> listTree = buildTrees(listAll);
        return listTree;
    }*/

    /***********************************
     * 用途说明：上传单个图标
     * 参数说明
     * icon        图标
     * 返回值说明:   是否上传成功
     ***********************************/
    @Override
    public String uploadIcon(MultipartFile icon) throws FileNotFoundException {


        //图片路径
        String iconPath = sysetmPath + "menu" + File.separator;
        String iconname =
                IdUtil.fastSimpleUUID() + "." + FileUtil.getExtensionName(icon.getOriginalFilename());
        //上传图标并获取图标名称 (图片改为png格式)
        String filename =
                FileUtil.upload(icon, iconPath, iconname).getName();
        return filename;
    }

    /**
     * 菜单集合递归生成树状菜单(List<SysMenu>)
     *
     * @param sysMenus 菜单对象
     * @return
     */
   /* public List<SysMenu> buildTree(List<SysMenu> sysMenus) {
        List<SysMenu> resultMenuList = new ArrayList<>();
        for (SysMenu sysMenu : sysMenus) {

            for (SysMenu menu : sysMenus) {
                if (menu.getParentid().equals(sysMenu.getId())) {
                    sysMenu.getChildren().add(menu);
                }
            }
            if ("0".equals(sysMenu.getParentid())) {
                resultMenuList.add(sysMenu);
            }
        }
        return resultMenuList;
    }*/

    /**
     * 菜单集合递归生成树状菜单(Map)（暂不使用该方法）
     *
     * @param sysMenus 菜单对象
     * @return
     */
    public List<Map<String, Object>> buildTree(List<Map<String, Object>> sysMenus) {
        List<Map<String, Object>> resultMenuList = new ArrayList<>();
        for (Map<String, Object> sysMenu : sysMenus) {

            List<Map<String, Object>> childrenList = new ArrayList<>();
            for (Map<String, Object> menu : sysMenus) {
                if (menu.get("parentid").equals(sysMenu.get("id"))) {
                    childrenList.add(menu);
                }
            }
            if ("0".equals(sysMenu.get("parentid"))) {
                if (childrenList.size() > 0) {
                    sysMenu.put("children", childrenList);
                }
                resultMenuList.add(sysMenu);
            }
        }
        return resultMenuList;
    }

    /**********************************
     * 用途说明: 左侧菜单树构建
     * 参数说明 sysMenus
     * 返回值说明: java.util.List<java.util.Map<java.lang.String,java.lang.Object>>
     ***********************************/
    public List<Map<String, Object>> buildTreeLeft(List<Map<String, Object>> sysMenus) {
        List<Map<String, Object>> resultMenuList = new ArrayList<>();
        for (Map<String, Object> sysMenu : sysMenus) {
            if ("0".equals(sysMenu.get("parentid"))) {
                resultMenuList.add(sysMenu);
            }
        }
        for (Map<String, Object> sysMenu : resultMenuList) {
            List<Map<String, Object>> menus = iterateMenusLeft(sysMenus,
                    (String) sysMenu.get("id"));
            if (menus.size() > 0) {
                sysMenu.put("children", menus);
            }
        }
        return resultMenuList;
    }

    /**
     * 左侧多级菜单查询方法
     *
     * @param menuVoList 不包含最高层次菜单的菜单集合
     * @param pid        父类id
     * @return
     */
    public List<Map<String, Object>> iterateMenusLeft(List<Map<String,
            Object>> menuVoList, String pid) {
        List<Map<String, Object>> result = new ArrayList<>();
        for (Map<String, Object> menu : menuVoList) {
            //获取菜单的id
            String menuid = (String) menu.get("id");
            //获取菜单的父id
            String parentid = (String) menu.get("parentid");
            if (StrUtil.isNotBlank(parentid)) {
                if (parentid.equals(pid)) {
                    //递归查询当前子菜单的子菜单
                    List<Map<String, Object>> iterateMenu =
                            iterateMenus(menuVoList, menuid);
                    if (iterateMenu.size() > 0) {
                        menu.put("children", iterateMenu);
                    }
                    result.add(menu);
                }
            }
        }
        return result;
    }

    /**********************************
     * 用途说明: 生成权菜单权限树
     * 参数说明 sysMenus
     * 返回值说明: java.util.List<java.util.Map<java.lang.String,java.lang.Object>>
     ***********************************/
    public List<Map<String, Object>> buildTrees(List<Map<String, Object>> sysMenus) {
        List<Map<String, Object>> resultMenuList = new ArrayList<>();
        for (Map<String, Object> sysMenu : sysMenus) {
            if ("0".equals(sysMenu.get("parentid"))) {
                resultMenuList.add(sysMenu);
            }
        }
        for (Map<String, Object> sysMenu : resultMenuList) {
            List<Map<String, Object>> menus = iterateMenus(sysMenus,
                    (String) sysMenu.get("id"));
            for (Map<String, Object> menu : menus) {
                if (!(boolean) menu.get("checkinfo")) {
                    sysMenu.put("checkinfo", false);
                    break;
                }
            }
            sysMenu.put("children", menus);
        }
        return resultMenuList;
    }

    /**
     * 多级菜单查询方法
     *
     * @param menuVoList 不包含最高层次菜单的菜单集合
     * @param pid        父类id
     * @return
     */
    public List<Map<String, Object>> iterateMenus(List<Map<String, Object>> menuVoList, String pid) {
        List<Map<String, Object>> result = new ArrayList<>();
        for (Map<String, Object> menu : menuVoList) {
            //获取菜单的id
            String menuid = (String) menu.get("id");
            //获取菜单的父id
            String parentid = (String) menu.get("parentid");
            if (StrUtil.isNotBlank(parentid)) {
                if (parentid.equals(pid)) {
                    //递归查询当前子菜单的子菜单
                    List<Map<String, Object>> iterateMenu =
                            iterateMenus(menuVoList, menuid);
                    for (Map<String, Object> map : iterateMenu) {
                        boolean checkinfo = (boolean) map.get("checkinfo");
                        if (!checkinfo) {
                            menu.put("checkinfo", false);
                        }
                    }
                    menu.put("children", iterateMenu);
                    result.add(menu);
                }
            }
        }
        return result;
    }

}
