package com.kcim.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.kcim.common.constants.Constant;
import com.kcim.common.exception.CostException;
import com.kcim.common.util.BeanUtil;
import com.kcim.common.util.PageUtils;
import com.kcim.dao.mapper.MenuMapper;
import com.kcim.dao.model.Menu;
import com.kcim.dao.model.User;
import com.kcim.dao.model.dto.MenuEditDTO;
import com.kcim.dao.model.dto.MenuSaveDTO;
import com.kcim.vo.MenuVO;
import com.kcim.vo.NavVO;
import com.kcim.service.MenuService;
import com.kcim.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

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

@Slf4j
@Service("menuService")
public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements MenuService {

    private final UserService userService;

    public MenuServiceImpl(UserService userService) {
        this.userService = userService;
    }

    /**
     * 保存菜单
     *
     * @param menuSaveDTO {@link MenuSaveDTO}
     * @param user        {@linkplain User}
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void saveMenu(MenuSaveDTO menuSaveDTO, User user) {
        // 校验
        this.check(menuSaveDTO.getType(), menuSaveDTO.getParentId());

        Menu menu = BeanUtil.convertObj(menuSaveDTO, Menu.class);
        menu.setHospId(user.getHospId())
                .setModifyTime(System.currentTimeMillis())
                .setModifyUserId(user.getId());

        this.save(menu);
    }


    /**
     * 分页查询菜单列表
     *
     * @param page     页码
     * @param pageSize 每页数据大小
     * @param user     当前登录用户
     * @param name
     * @return 菜单伪列表
     */
    @Override
    public PageUtils selectList(Integer page, Integer pageSize, User user, String name) {
        List<Menu> list = this.list(
                new LambdaQueryWrapper<Menu>().eq(Menu::getHospId, user.getHospId())
                        .like(StrUtil.isNotBlank(name), Menu::getName, name)
                        .orderByAsc(Menu::getOrderNum)
        );

        List<MenuVO> menuVOS = list.stream().map(i -> {
            MenuVO menuVO = BeanUtil.convertObj(i, MenuVO.class);
            menuVO.setModifyTime(DateUtil.formatDateTime(DateUtil.date(i.getModifyTime())));
            menuVO.setModifyUserName(userService.getUsernameByIdAndHospId(i.getModifyUserId(), user.getHospId()));
            return menuVO;
        }).collect(Collectors.toList());

        List<MenuVO> roots = menuVOS.stream().filter(i -> i.getParentId() == 0).collect(Collectors.toList());
        List<MenuVO> treeVOs = new ArrayList<>();
        for (MenuVO i : roots) {
            List<MenuVO> menus = this.getMenuTree(i, menuVOS);
            treeVOs.addAll(menus);

        }

        return new PageUtils(treeVOs, 0, 0, 0);
    }

    /**
     * 编辑某个菜单
     *
     * @param menuDTO {@link MenuEditDTO}
     * @param user    {@link User}
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Throwable.class)
    public void updateMenu(MenuEditDTO menuDTO, User user) {
        this.check(menuDTO.getType(), menuDTO.getParentId());

        Menu byId = this.getById(menuDTO.getId());
        if (Objects.isNull(byId)) {
            throw new CostException(500, "选择的菜单已被移除");
        }
        BeanUtil.convertObj(menuDTO, byId);
        log.info("byId:{}", byId);
        this.updateById(byId);
    }

    /**
     * 递归遍历
     *
     * @param menuVO
     * @param menuVOS
     * @return
     */
    private List<MenuVO> getMenuTree(MenuVO menuVO, List<MenuVO> menuVOS) {
        List<MenuVO> list = new ArrayList<>();
        list.add(menuVO);
        for (MenuVO vo : menuVOS) {
            if (vo.getParentId().equals(menuVO.getMenuId())) {
                List<MenuVO> children = menuVO.getChildren();
                if (CollUtil.isEmpty(children)) {
                    children = new ArrayList<>();
                }
                children.add(vo);
                menuVO.setChildren(children);
                this.getMenuTree(vo, menuVOS);
            }
        }

        return list;
    }

    /**
     * 校验相关 菜单格式
     *
     * @param parentId 父级id
     * @param type     菜单类型
     */
    private void check(Integer type, Long parentId) {
        //上级菜单类型
        int parentType = Constant.MenuType.CATALOG.getValue();
        if (parentId != 0L) {
            Menu parentMenu = this.getById(parentId);
            parentType = parentMenu.getType();
        }

        // 如果当前是 目录、菜单
        if (type == Constant.MenuType.CATALOG.getValue() ||
                type == Constant.MenuType.MENU.getValue()) {
            if (parentType != Constant.MenuType.CATALOG.getValue()) {
                throw new CostException("上级菜单只能为目录类型");
            }
            return;
        }

        // 如果当前是按钮
        if (type == Constant.MenuType.BUTTON.getValue()) {
            if (parentType != Constant.MenuType.MENU.getValue()) {
                throw new CostException("上级菜单只能为菜单类型");
            }
        }
    }

    /**
     * 获取当前登录人的菜单列表
     *
     * @param user {@link User}
     * @return
     */
    @Override
    public List<NavVO> getNav(User user) {
        List<Menu> list = this.list(new LambdaQueryWrapper<Menu>().eq(Menu::getHospId, user.getHospId()).ne(Menu::getType, 2).orderByAsc(Menu::getOrderNum));
        list.removeIf(i -> i.getType() != 0);
        // 用户具体不同的是菜单不同
        List<Menu> menus = baseMapper.getNav(user.getId(), user.getHospId());
        list.addAll(menus);
        List<NavVO> navVOS = BeanUtil.convertList(list, NavVO.class);
        List<NavVO> vos = navVOS.stream().filter(i -> i.getParentId() == 0)
                .peek(o -> {
                    List<NavVO> son = this.getSon(o, navVOS);
                    if (CollUtil.isNotEmpty(son)) {
                        o.setChildren(son);
                    }
                })
                .collect(Collectors.toList());
        // 移除目录下菜单为空的层级
        vos.removeIf(next -> CollUtil.isEmpty(next.getChildren()));
        return vos;
    }

    /**
     * 批量删除菜单
     *
     * @param idList 菜单Id的集合
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    public void deleteByIds(List<Long> idList) {
        this.removeByIds(idList);
    }

    private List<NavVO> getSon(NavVO parentNavVO, List<NavVO> list) {
        return list.stream().filter(o -> parentNavVO.getMenuId().equals(o.getParentId()))
                .peek(i -> {
                    List<NavVO> son = this.getSon(i, list);
                    if (CollUtil.isNotEmpty(son)) {
                        i.setChildren(son);
                    }
                })
                .collect(Collectors.toList());
    }

}