package cn.reghao.devops.web.admin.account.service.impl;

import cn.reghao.devops.web.admin.account.db.repository.MenuRepository;
import cn.reghao.devops.web.admin.account.db.repository.RoleRepository;
import cn.reghao.devops.web.admin.account.model.constant.MenuType;
import cn.reghao.devops.web.admin.account.model.vo.MenuTree;
import cn.reghao.devops.web.admin.account.service.MenuService;
import cn.reghao.jutil.jdk.result.Result;
import cn.reghao.jutil.jdk.result.ResultStatus;
import cn.reghao.devops.web.admin.account.model.dto.MenuDto;
import cn.reghao.devops.web.admin.account.model.po.Menu;
import cn.reghao.devops.web.admin.account.model.po.Role;
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 addMenu(Menu menu) {
        int pid = menu.getPid();
        int total = menuRepository.countByPid(pid);
        if (total >= 10) {
            return Result.result(ResultStatus.FAIL, "每个 dir 最多可包含 10 个 page");
        }

        int total1 = getParentCount(pid);
        String type = menu.getType();
        if (type.equals(MenuType.dir.name())) {
            if (total1 < 2) {
                menu.setUrl("#");
            } else {
                return Result.result(ResultStatus.FAIL, "当前父目录处于第二层, 只能添加 page menu, root -> level1 -> level2");
            }
        } else {
            String url = menu.getUrl();
            Result result1 = checkUrl(url);
            if (result1.getCode() != ResultStatus.SUCCESS.getCode()) {
                return result1;
            }
        }

        List<Menu> siblings = menuRepository.findByPid(menu.getPid());
        if (siblings.isEmpty()) {
            menu.setPos(1);
        } else {
            siblings.sort(Comparator.comparingInt(Menu::getPos));
            int max = siblings.get(siblings.size()-1).getPos();
            menu.setPos(max+1);
        }

        menuRepository.save(menu);
        return Result.result(ResultStatus.SUCCESS);
    }

    private Result checkUrl(String url) {
        Menu menu1 = menuRepository.findByUrl(url);
        if (menu1 != null) {
            return Result.result(ResultStatus.FAIL, String.format("url 为 %s 的 Menu 已存在", url));
        }

        return Result.success();
    }

    /**
     * 检查菜单的层级, Menu 最多只能有两个 parent, 即最多只能有三层
     *
     * @param
     * @return
     * @date 2021-07-15 上午11:15
     */
    private int getParentCount(int menuId) {
        int total = 1;
        if (menuId != 0) {
            // 2
            total++;
            Menu menu1 = menuRepository.getOne(menuId);
            int pid1 = menu1.getPid();
            if (pid1 != 0) {
                // 3
                total++;
                Menu menu2 = menuRepository.getOne(pid1);
                int pid2 = menu2.getPid();
                if (pid2 != 0) {
                    // 4
                    total++;
                }
            }
        }

        return total;
    }

    @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));
        }

        String url = menuDto.getUrl();
        Result result1 = checkUrl(url);
        if (result1.getCode() != ResultStatus.SUCCESS.getCode()) {
            return result1;
        }

        menuEntity.setName(menuDto.getName());
        menuEntity.setUrl(menuDto.getUrl());
        menuEntity.setIcon(menuDto.getIcon());
        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());

        roleRepository.saveAll(roleList);
        menuRepository.deleteAll(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> getMenusByStatus(Boolean enabled) {
        List<Menu> menuList = menuRepository.findAllByEnabled(enabled);
        return getSortedZTree(menuList, null);
    }

    @Override
    public List<MenuTree> getMenusByRole(Role role) {
        List<Menu> allMenus = menuRepository.findAllByEnabled(true);
        return getSortedZTree(allMenus, role);
    }

    @Override
    public List<MenuTree> getDirMenus() {
        List<Menu> allMenus = menuRepository.findByEnabledIsTrueAndType(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;
    }

    public void updateMenusStatus(boolean status, List<Integer> menuIds) {
        List<Menu> results = menuIds.stream()
                .map(menuId -> menuRepository.findById(menuId).orElse(null))
                .filter(Objects::nonNull)
                .collect(Collectors.toList());

        List<Menu> dirMenus = results.stream()
                .filter(menu -> menu.getType().equals(MenuType.dir.name()))
                .collect(Collectors.toList());
        List<Menu> subMenus = dirMenus.stream()
                .map(menu -> getSubMenus(menu.getId()))
                .flatMap(Collection::stream)
                .collect(Collectors.toList());
        results.addAll(subMenus);

        results.forEach(menu -> menu.setEnabled(status));
        menuRepository.saveAll(results);
    }

    public Map<Integer, String> getSortedChildGroupByPid(int pid) {
        List<Menu> menus = menuRepository.findByPid(pid);
        menus.sort(Comparator.comparingInt(Menu::getPos));
        Map<Integer, String> map = new HashMap<>();
        menus.forEach(menu -> map.put(menu.getPos(), menu.getName()));
        return map;
    }
}
