package org.nl.system.service.menu.impl;

import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.nl.common.domain.constant.DictConstantPool;
import org.nl.common.domain.query.PageQuery;
import org.nl.common.exception.BadRequestException;
import org.nl.common.utils.SecurityUtils;
import org.nl.common.domain.vo.MenuMetaVo;
import org.nl.common.domain.vo.MenuVo;
import org.nl.common.utils.CopyUtil;
import org.nl.config.IdUtil;
import org.nl.system.service.dict.dao.Dict;
import org.nl.system.service.dict.dao.mapper.SysDictMapper;
import org.nl.system.service.lucene.LuceneExecuteLogService;
import org.nl.system.service.menu.dto.MenuDto;
import org.nl.system.service.menu.ISysMenuService;
import org.nl.system.service.menu.dao.SysMenu;
import org.nl.system.service.menu.dao.mapper.SysMenuMapper;
import org.nl.system.service.menu.dto.MenuQuery;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 菜单表 服务实现类
 * </p>
 *
 * @author generator
 * @since 2022-12-15
 */
@Service
@Slf4j
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> implements ISysMenuService {

    @Autowired
    private SysMenuMapper baseMapper;

    @Autowired
    private SysDictMapper sysDictMapper;

    @Autowired
    private LuceneExecuteLogService luceneExecuteLogService;

    @Override
    public List<MenuDto> queryAll(Map<String, Object> param) {
//       父节点标识
        String pid = MapUtil.getStr(param, "pid");
        return getMenus(pid).stream().map(menu -> this.doToDto(menu)).collect(Collectors.toList());
    }


    @Override
    public List query(MenuQuery query, PageQuery page) {
        if (StringUtils.isNotEmpty(query.getBlurry())){
            query.setPid(null);
        }
        Page<SysMenu> menuPage = this.page(page.build(), query.build());
        List<MenuDto> collect = menuPage.getRecords().stream().map(menu -> this.doToDto(menu)).collect(Collectors.toList());
        return collect;
    }

    @Override
    public List<MenuDto> getSuperior(MenuDto menuDto, List<SysMenu> menus) {
        if (menuDto.getPid() == null) {
            menus.addAll(this.findByPidIsNull());
            return menus.stream().map(menu -> this.doToDto(menu)).collect(Collectors.toList());
        }
        menus.addAll(baseMapper.findByPid(menuDto.getPid()));

        return getSuperior(this.doToDto(findById(menuDto.getPid())), menus);
    }

    @Override
    public SysMenu findById(String id) {
        return baseMapper.selectById(id);
    }

    @Override
    public List<SysMenu> findByPid(String pid) {
        return baseMapper.findByPid(pid);
    }

    @Override
    public List<SysMenu> findByPidIsNull() {
        return baseMapper.findByPidIsNull();
    }

    @Override
    public Set<SysMenu> getChildMenus(List<SysMenu> menuList, Set<SysMenu> menuSet) {
        for (SysMenu menu : menuList) {
            menuSet.add(menu);
            List<SysMenu> menus = this.findByPid(menu.getMenu_id());
            if (menus != null && menus.size() != 0) {
                getChildMenus(menus, menuSet);
            }
        }
        return menuSet;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void create(SysMenu resources) {

        if (resources.getIframe()) {
            String http = "http://", https = "https://";
            if (!(resources.getPath().toLowerCase().startsWith(http) || resources.getPath().toLowerCase().startsWith(https))) {
                throw new BadRequestException("外链必须以http://或者https://开头");
            }
        }
        resources.setMenu_id(IdUtil.getStringId());
        if (resources.getPid().equals("0")) {
            resources.setPid(null);
            addSystemTypeDict(resources);
        }

        baseMapper.insert(resources);
        // 计算子节点数目
        resources.setSub_count(0);
        // 更新父节点菜单数目
        updateSubCnt(resources.getPid());
        updateRootSystemType(resources);
    }

    private void addSystemTypeDict(SysMenu resources) {
        Dict dict = sysDictMapper.selectOne(new QueryWrapper<Dict>().eq("code", DictConstantPool.DICT_SYS_CODE).orderByDesc("value").last("limit 1"));
        Integer currentType = dict!=null?Integer.valueOf(dict.getValue())+1:1;
        Dict currentSysType = new Dict();
        currentSysType.setDict_id(IdUtil.getStringId());
        currentSysType.setCode(DictConstantPool.DICT_SYS_CODE);
        currentSysType.setName(DictConstantPool.DICT_SYS_NAME);
        currentSysType.setLabel(resources.getTitle());
        currentSysType.setValue(String.valueOf(currentType));
        currentSysType.setPara1(resources.getMenu_id());
        sysDictMapper.insert(currentSysType);
        resources.setSystem_type(String.valueOf(currentType));
    }
    private void updateRootSystemType(SysMenu sysMenu) {
        String rootMenuId = this.findRootMenuId(sysMenu.getMenu_id());
        if (sysMenu.getMenu_id().equals(rootMenuId)){
            return;
        }
        SysMenu rootMenu = this.findById(rootMenuId);
        sysMenu.setSystem_type(rootMenu.getSystem_type());
        this.updateById(sysMenu);
    }
    private String findRootMenuId(String menuId) {
        SysMenu sysMenu = this.findById(menuId);
        if (StrUtil.isEmpty(sysMenu.getPid())) {
            return menuId;
        } else {
            return findRootMenuId(sysMenu.getPid());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(Set<SysMenu> menuSet) {
        List<String> pids = Lists.newArrayList();
        for (SysMenu menu : menuSet) {
            //解绑菜单
            baseMapper.untiedMenu(menu.getMenu_id());
            baseMapper.deleteById(menu.getMenu_id());
            String pid = menu.getPid();
            if (StringUtils.isEmpty(pid)){
                pids.add(pid);
            }
            updateSubCnt(pid);
        }
        if (!CollectionUtils.isEmpty(pids)){
            sysDictMapper.delete(new QueryWrapper<Dict>().in("para1", pids).eq("code", DictConstantPool.DICT_SYS_CODE));
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void update(SysMenu resources) {
        String menuId = resources.getMenu_id();
        SysMenu menu = baseMapper.selectById(menuId);
        String allChild = baseMapper.findAllChild(menuId); // 获取当前菜单的所有子菜单
        List<String> allChildIds = ObjectUtil.isNotEmpty(allChild)?Arrays.asList(allChild.split(",")):null;
        if (ObjectUtil.isNotEmpty(allChildIds) && allChildIds.contains(resources.getPid())){
            throw new BadRequestException("上级不能为自己或自己的下级");
        }
        if (resources.getIframe()) {
            String http = "http://", https = "https://";
            if (!(resources.getPath().toLowerCase().startsWith(http) || resources.getPath().toLowerCase().startsWith(https))) {
                throw new BadRequestException("外链必须以http://或者https://开头");
            }
        }

        if (resources.getPid().equals("0")) {
            resources.setPid(null);
            if (StringUtils.isNotEmpty(menu.getPid())){
                addSystemTypeDict(resources);
            }
        }else {
            resources.setSystem_type(this.findById(resources.getPid()).getSystem_type());
        }
        String oldPid = menu.getPid();
        String newPid = resources.getPid();
        // 记录的父节点ID
        if (oldPid == null && newPid != null){
            sysDictMapper.delete(new QueryWrapper<Dict>().eq("para1",menu.getMenu_id()));
        }
        menu.setTitle(resources.getTitle());
        menu.setComponent(resources.getComponent());
        menu.setPath(resources.getPath());
        menu.setIcon(resources.getIcon());
        menu.setIframe(resources.getIframe());
        menu.setPid(resources.getPid());
        menu.setMenu_sort(resources.getMenu_sort());
        menu.setCache(resources.getCache());
        menu.setHidden(resources.getHidden());
        menu.setComponent_name(resources.getComponent_name());
        menu.setPermission(resources.getPermission());
        menu.setType(resources.getType());
        baseMapper.updateById(menu);
        // 计算父级菜单节点数目
        updateSubCnt(oldPid);
        updateSubCnt(newPid);
        //更新SystemType
        if (!resources.getSystem_type().equals(menu.getSystem_type())){
            this.update(new UpdateWrapper<SysMenu>().set(DictConstantPool.DICT_SYS_CODE,resources.getSystem_type()).in("menu_id",allChildIds));
        }

    }

    /**
     * 计算菜单子节点数目
     *
     * @param menuId
     */
    private void updateSubCnt(String menuId) {
        if (menuId != null) {
            int count = baseMapper.findByPid(menuId).size();
            SysMenu sysMenu = baseMapper.selectById(menuId);
            if (ObjectUtil.isEmpty(sysMenu)) return;
            sysMenu.setSub_count(count);
            baseMapper.updateById(sysMenu);
        }
    }

    @Override
    public List<MenuDto> findByUser(String userId) {
        return baseMapper.findByUser(userId).stream().map(menu -> this.doToDto(menu)).collect(Collectors.toList());
    }

    @Override
    public List<MenuVo> buildMenus(List<MenuDto> menuDtos) {
        List<MenuVo> list = new LinkedList<>();
        menuDtos.forEach(menuDTO -> {
                    if (menuDTO != null) {
                        List<MenuDto> menuDtoList = menuDTO.getChildren();
                        MenuVo menuVo = new MenuVo();
                        menuVo.setName(ObjectUtil.isNotEmpty(menuDTO.getComponent_name()) ? menuDTO.getComponent_name() : menuDTO.getTitle());
                        // 一级目录需要加斜杠，不然会报警告
                        menuVo.setPath(ObjectUtil.isEmpty(menuDTO.getPid()) ? "/" + menuDTO.getPath() : menuDTO.getPath());
                        menuVo.setHidden(menuDTO.getHidden());
                        // 如果不是外链
                        if (!menuDTO.getIframe()) {
                            if (ObjectUtil.isEmpty(menuDTO.getPid())) {
                                menuVo.setComponent(StrUtil.isEmpty(menuDTO.getComponent()) ? "Layout" : menuDTO.getComponent());
                            } else if (!ObjectUtil.isEmpty(menuDTO.getPid()) && "0".equals(menuDTO.getType())) {
                                menuVo.setComponent(StrUtil.isEmpty(menuDTO.getComponent()) ? "ParentView" : menuDTO.getComponent());

                            } else if (!StrUtil.isEmpty(menuDTO.getComponent())) {
                                menuVo.setComponent(menuDTO.getComponent());
                            }
                        }
                        menuVo.setMeta(new MenuMetaVo(menuDTO.getTitle(), menuDTO.getIcon(), !menuDTO.getCache()));
                        if (menuDtoList != null && menuDtoList.size() != 0) {
                            menuVo.setAlwaysShow(true);
                            menuVo.setRedirect("noredirect");
                            menuVo.setChildren(buildMenus(menuDtoList));
                            // 处理是一级菜单并且没有子菜单的情况
                        } else if (ObjectUtil.isEmpty(menuDTO.getPid())) {
                            MenuVo menuVo1 = new MenuVo();
                            menuVo1.setMeta(menuVo.getMeta());
                            // 非外链
                            if (!menuDTO.getIframe()) {
                                menuVo1.setPath("index");
                                menuVo1.setName(menuVo.getName());
                                menuVo1.setComponent(menuVo.getComponent());
                            } else {
                                menuVo1.setPath(menuDTO.getPath());
                            }
                            menuVo.setName(null);
                            menuVo.setMeta(null);
                            menuVo.setComponent("Layout");
                            List<MenuVo> list1 = new ArrayList<>();
                            list1.add(menuVo1);
                            menuVo.setChildren(list1);
                        }
                        list.add(menuVo);
                    }
                }
        );
        return list;
    }

    @Override
    public List<MenuDto> buildTree(List<MenuDto> menuDtos) {
        List<MenuDto> trees = new ArrayList<>();
        Set<String> ids = new HashSet<>();
        for (MenuDto menuDTO : menuDtos) {
            if (menuDTO.getPid() == null) {
                trees.add(menuDTO);
            }
            for (MenuDto it : menuDtos) {
                if (menuDTO.getMenu_id().equals(it.getPid())) {
                    if (menuDTO.getChildren() == null) {
                        menuDTO.setChildren(new ArrayList<>());
                    }
                    menuDTO.getChildren().add(it);
                    ids.add(it.getMenu_id());
                }
            }
        }
        if (trees.size() == 0) {
            trees = menuDtos.stream().filter(s -> !ids.contains(s.getMenu_id())).collect(Collectors.toList());
        }
        return trees;
    }

    @Override
    public List<SysMenu> getMenus(String pid) {
        QueryWrapper<SysMenu> queryWrapper;
        if (pid != null && !"0".equals(pid)) {
            queryWrapper = new QueryWrapper<SysMenu>().eq("pid", pid);
        } else {
            queryWrapper = new QueryWrapper<SysMenu>().isNull("pid");
        }
        return baseMapper.selectList(queryWrapper.orderByAsc("menu_sort"));
    }

    @Override
    public MenuDto doToDto(SysMenu entity) {

        MenuDto menuDto = new MenuDto();
        if (ObjectUtil.isEmpty(entity)){
            return menuDto;
        }
        menuDto.setMenu_id(entity.getMenu_id());
        menuDto.setType(entity.getType());
        menuDto.setPermission(entity.getPermission());
        menuDto.setTitle(entity.getTitle());
        menuDto.setMenu_sort(entity.getMenu_sort());
        menuDto.setPath(entity.getPath());
        menuDto.setComponent(entity.getComponent());
        menuDto.setSystem_type(entity.getSystem_type());
        menuDto.setPid(entity.getPid());
        menuDto.setSub_count(entity.getSub_count());
        menuDto.setIframe(entity.getIframe());
        menuDto.setCache(entity.getCache());
        menuDto.setHidden(entity.getHidden());
        menuDto.setComponent_name(entity.getComponent_name());
        menuDto.setIcon(entity.getIcon());
        menuDto.setCreate_time(entity.getCreate_time());

        //构建前端需要的数据结构树
        Integer sub_count = entity.getSub_count();
        if (sub_count <= 0) {
            menuDto.setLeaf(true);
            menuDto.setHas_children(false);
        } else {
            menuDto.setLeaf(false);
            menuDto.setHas_children(true);
        }
        return menuDto;
    }

    @Override
    public List<MenuVo> buildMenus(String systemType) {
        Dict dict = sysDictMapper.selectOne(new QueryWrapper<Dict>().eq("code", DictConstantPool.DICT_SYS_CODE).eq("value", systemType));
        if (dict == null || StringUtils.isEmpty(dict.getPara1())){
            throw new BadRequestException("获取对应的系统菜单不存在");
        }
        String pid = dict.getPara1();
        List<SysMenu> menuDtoList = baseMapper.findSystemMenu(SecurityUtils.getCurrentUserId(),systemType);
        //移除系统级菜单
        menuDtoList.removeIf(a->a.getMenu_id().equals(pid));
        List<MenuDto> menuDtos = this.buildTree(CopyUtil.copyList(menuDtoList, MenuDto.class));
        return this.buildMenus(menuDtos,pid);
    }

    @Override
    public List<MenuVo> buildMenus(List<MenuDto> menuDtos, String pid) {
        List<MenuVo> list = new LinkedList<>();
        //剔除系统级菜单
        menuDtos.forEach(menuDTO -> {
                    if (menuDTO != null) {
                        List<MenuDto> menuDtoList = menuDTO.getChildren();
                        MenuVo menuVo = new MenuVo();
                        menuVo.setName(ObjectUtil.isNotEmpty(menuDTO.getComponent_name()) ? menuDTO.getComponent_name() : menuDTO.getTitle());
                        // 一级目录需要加斜杠，不然会报警告
                        menuVo.setPath(pid.equals(menuDTO.getPid())? "/" + menuDTO.getPath() : menuDTO.getPath());
                        menuVo.setHidden(menuDTO.getHidden());
                        // 如果不是外链
                        if (!menuDTO.getIframe()) {
                            if (pid.equals(menuDTO.getPid())) {
                                menuVo.setComponent(StrUtil.isEmpty(menuDTO.getComponent()) ? "Layout" : menuDTO.getComponent());
                            } else if (!pid.equals(menuDTO.getPid()) && "0".equals(menuDTO.getType())) {
                                menuVo.setComponent(StrUtil.isEmpty(menuDTO.getComponent()) ? "ParentView" : menuDTO.getComponent());

                            } else if (!StrUtil.isEmpty(menuDTO.getComponent())) {
                                menuVo.setComponent(menuDTO.getComponent());
                            }
                        }
                        menuVo.setMeta(new MenuMetaVo(menuDTO.getTitle(), menuDTO.getIcon(), !menuDTO.getCache()));
                        if (menuDtoList != null && menuDtoList.size() != 0) {
                            menuVo.setAlwaysShow(true);
                            menuVo.setRedirect("noredirect");
                            menuVo.setChildren(buildMenus(menuDtoList,pid));
                            // 处理是一级菜单并且没有子菜单的情况
                        } else if (StrUtil.isEmpty(menuDTO.getPid())) {
                            MenuVo menuVo1 = new MenuVo();
                            menuVo1.setMeta(menuVo.getMeta());
                            // 非外链
                            if (!menuDTO.getIframe()) {
                                menuVo1.setPath("index");
                                menuVo1.setName(menuVo.getName());
                                menuVo1.setComponent(menuVo.getComponent());
                            } else {
                                menuVo1.setPath(menuDTO.getPath());
                            }
                            menuVo.setName(null);
                            menuVo.setMeta(null);
                            menuVo.setComponent("Layout");
                            List<MenuVo> list1 = new ArrayList<>();
                            list1.add(menuVo1);
                            menuVo.setChildren(list1);
                        }
                        list.add(menuVo);
                    }
                }
        );
        return list;
    }

    @Override
    public List<Map> getMenusByRole(String roleId, String systemType, String category) {
        baseMapper.getMenusByRole(systemType,category);
        return null;
    }
}
