package cn.reghao.bnt.web.admin.service.impl;

import cn.reghao.bnt.web.admin.model.constant.MenuType;
import cn.reghao.bnt.web.admin.model.constant.RoleType;
import cn.reghao.bnt.web.admin.model.dto.MenuDto;
import cn.reghao.bnt.web.admin.model.po.Menu;
import cn.reghao.bnt.web.admin.model.po.Role;
import cn.reghao.bnt.web.admin.db.repository.MenuRepository;
import cn.reghao.bnt.web.admin.db.repository.RoleRepository;
import cn.reghao.bnt.web.admin.model.vo.MenuTree;
import cn.reghao.bnt.web.admin.model.vo.ResourceTree;
import cn.reghao.bnt.web.admin.service.MenuService;
import cn.reghao.bnt.web.admin.service.UserContext;
import cn.reghao.jutil.jdk.web.result.Result;
import cn.reghao.jutil.jdk.web.result.ResultStatus;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

/**
 * @author reghao
 * @date 2021-05-17 17:16:19
 */
@Slf4j
@Service
public class MenuServiceImpl implements MenuService {
    private final MenuRepository menuRepository;
    private final RoleRepository roleRepository;

    public MenuServiceImpl(MenuRepository menuRepository, RoleRepository roleRepository) {
        this.menuRepository = menuRepository;
        this.roleRepository = roleRepository;
    }

    @Override
    public synchronized Result updateMenu(MenuDto menuDto) {
        int menuId = menuDto.getMenuId();
        Menu menuEntity = menuRepository.findById(menuId).orElse(null);
        if (menuEntity == null) {
            return Result.result(ResultStatus.FAIL, String.format("ID 为 %s 的 Menu 不存在", menuId));
        }

        menuEntity.setName(menuDto.getName());
        menuEntity.setIcon(menuDto.getIcon());
        menuEntity.setVisible(menuDto.getVisible() == 1);
        menuRepository.save(menuEntity);
        return Result.result(ResultStatus.SUCCESS);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public synchronized Result deleteMenu(Integer menuId) {
        if (menuId <= 5) {
            return Result.result(ResultStatus.FAIL, String.format("不能删除系统默认 Menu"));
        }

        Menu menu = menuRepository.findById(menuId).orElse(null);
        if (menu == null) {
            return Result.result(ResultStatus.FAIL, String.format("ID 为 %s 的 Menu 不存在", menuId));
        }

        List<Menu> deletedMenus = new ArrayList<>();
        deletedMenus.add(menu);
        String type = menu.getType();
        if (type.equals(MenuType.DIR.name())) {
            List<Menu> list1 = getSubMenus(menu.getId());
            deletedMenus.addAll(list1);
        }

        // 删除 Role 关联的 Menu
        List<Role> roleList = deletedMenus.stream()
                .map(menu1 -> {
                    Set<Role> roles = menu1.getRoles();
                    roles.forEach(role -> role.getMenus().remove(menu1));
                    return roles;
                })
                .flatMap(Collection::stream)
                .collect(Collectors.toList());

        deletedMenus.forEach(menu1 -> {
            menu1.setDeleted(true);
        });

        roleRepository.saveAll(roleList);
        // 逻辑删除
        menuRepository.saveAll(deletedMenus);
        return Result.result(ResultStatus.SUCCESS);
    }

    /**
     * 获取某个 dir menu 的所有子 menu(包括子 menu 的子 menu)
     *
     * @param
     * @return
     * @date 2024-08-01 09:23:42
     */
    private List<Menu> getSubMenus(int menuId) {
        List<Menu> list1 = menuRepository.findByPid(menuId);
        List<Menu> results = new ArrayList<>(list1);
        Map<String, List<Menu>> map =  list1.stream().collect(Collectors.groupingBy(Menu::getType));
        List<Menu> dirMenus = map.get(MenuType.DIR.name());
        if (dirMenus != null) {
            dirMenus.forEach(dirMenu -> {
                List<Menu> list2 = menuRepository.findByPid(dirMenu.getId());
                results.addAll(list2);
            });
        }

        return results;
    }

    @Override
    public List<MenuTree> getMenuTreeByRole(Role role) {
        List<Menu> allMenus = menuRepository.findAllByDeleted(false);
        return getSortedZTree(allMenus, role);
    }

    public List<ResourceTree> getMenuTreeByRole1(Role role) {
        List<Menu> allMenus = menuRepository.findAllByDeleted(false);
        Map<Integer, List<ResourceTree>> groupMap = allMenus.stream()
                .map(ResourceTree::new)
                .collect(Collectors.groupingBy(ResourceTree::getPid));

        int id = 0;
        List<ResourceTree> list = groupMap.remove(id);
        if (list != null) {
            for (ResourceTree resourceTree : list) {
                id = resourceTree.getId();
                List<ResourceTree> list1 = groupMap.remove(id);
                if (list1 != null) {
                    resourceTree.getChildren().addAll(list1);
                }
            }
        } else {
            list = new ArrayList<>();
        }

        return list.stream()
                .sorted(Comparator.comparingInt(ResourceTree::getPos))
                .collect(Collectors.toList());
    }

    @Override
    public List<MenuTree> getDirMenus() {
        List<Menu> allMenus = menuRepository.findByDeletedIsFalseAndType(MenuType.DIR.name());
        return getSortedZTree(allMenus, null);
    }

    private List<MenuTree> getSortedZTree(List<Menu> list, Role role) {
        Map<Integer, List<Menu>> map =  list.stream().collect(Collectors.groupingBy(Menu::getPid));
        List<MenuTree> menuTrees = new ArrayList<>();
        map.forEach((pid, menus) -> {
            menuTrees.addAll(menus.stream()
                    .sorted(Comparator.comparing(Menu::getPos))
                    .map(menu -> {
                        MenuTree menuTree = new MenuTree(menu);
                        Set<Role> roles = menu.getRoles();
                        if (roles.contains(role)) {
                            // 对应前端的 checked 复选框
                            menuTree.setGranted(true);
                        }
                        return menuTree;
                    })
                    .collect(Collectors.toList()));
        });

        return menuTrees;
    }

    @Transactional
    @Override
    public List<String> getRolesByUrl(String method, String requestUrl) {
        String type = MenuType.GET.name();
        if (method.equalsIgnoreCase("post")) {
            type = MenuType.POST.name();
        }

        String url = requestUrl.substring(1);
        Menu menu = menuRepository.findByDeletedIsFalseAndTypeAndUrl(type, url);
        if (menu == null) {
            return Collections.emptyList();
        }

        // Menu#getRoles
        List<String> list = menu.getRoles().stream()
                .map(Role::getName)
                .collect(Collectors.toList());
        if (list.isEmpty()) {
            return List.of(RoleType.ROLE_DEVOPS_ADMIN.name());
        }

        return list;
    }

    @Override
    public List<MenuTree> getSideMenus() {
        String userRole = UserContext.getUserRole();
        Set<String> roles = Set.of(userRole);
        List<Menu> menus = new ArrayList<>();
        // Role#getMenus
        roles.forEach(name -> {
            Role role = roleRepository.findByName(name);
            menus.addAll(role.getMenus().stream()
                    .filter(menu -> !menu.getDeleted() &&
                            menu.getVisible() && !menu.getType().equals(MenuType.POST.name()))
                    .collect(Collectors.toSet()));
        });

        List<MenuTree> menuTrees = menus.stream().map(MenuTree::new).collect(Collectors.toList());
        Map<Integer, MenuTree> allMap = menuTrees.stream().collect(Collectors.toMap(MenuTree::getId, k -> k));

        Set<Integer> set = new HashSet<>();
        menuTrees.forEach(menuTree -> {
            int pid = menuTree.getPid();
            MenuTree menuTree1 = allMap.get(pid);
            if (menuTree1 != null) {
                List<MenuTree> list = menuTree1.getChildren();
                if (list == null) {
                    list = new ArrayList<>();
                    menuTree1.setChildren(list);
                }

                menuTree1.getChildren().add(menuTree);
                menuTree1.getChildren().sort(Comparator.comparingInt(MenuTree::getPos));
                set.add(menuTree.getId());
            }
        });

        set.forEach(allMap::remove);
        List<MenuTree> results = new ArrayList<>(allMap.values());
        results.sort(Comparator.comparingInt(MenuTree::getPos));
        return results;
    }

    @Override
    public Menu getMenu(String method, String url) {
        return menuRepository.findByDeletedIsFalseAndTypeAndUrl(method.toLowerCase(Locale.ROOT), url.substring(1));
    }
}
