package com.mortals.xhx.base.system.menu.service.impl;

import com.mortals.framework.common.code.YesNo;
import com.mortals.framework.exception.AppException;
import com.mortals.framework.model.Context;
import com.mortals.framework.model.OrderCol;
import com.mortals.framework.service.IUser;
import com.mortals.framework.service.impl.AbstractCRUDServiceImpl;
import com.mortals.framework.util.StringUtils;
import com.mortals.xhx.base.system.menu.dao.MenuDao;
import com.mortals.xhx.base.system.menu.model.MenuEntity;
import com.mortals.xhx.base.system.menu.model.MenuQuery;
import com.mortals.xhx.base.system.menu.model.MenuTreeSelect;
import com.mortals.xhx.base.system.menu.service.MenuService;
import com.mortals.xhx.common.code.SatusEnum;
import com.mortals.xhx.common.code.YesNoEnum;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import java.util.*;
import java.util.stream.Collectors;
/**
* MenuService
* 菜单信息 service实现
*
* @author zxfei
* @date 2023-04-10
*/
@Service("menuService")
public class MenuServiceImpl extends AbstractCRUDServiceImpl<MenuDao, MenuEntity, Long> implements MenuService {

    @Override
    public List<MenuEntity> findAllEnable() throws AppException {
        MenuQuery params = new MenuQuery();
        params.setStatus(YesNo.YES.getValue());
        params.setMenuType(0);//主菜单
        List<OrderCol> orderColList = new ArrayList<OrderCol>();
        orderColList.add(new OrderCol("parentId"));
        orderColList.add(new OrderCol("orderId"));
        params.setOrderColList(orderColList);
        return dao.getList(params);
    }

    @Override
    public List<MenuEntity> findTreeMenu(IUser user, Set<String> urls) throws AppException {
        Set<Long> authIds = new HashSet<>();
        Map<Long, MenuEntity> menuMap = new HashMap<Long, MenuEntity>();
        List<MenuEntity> userModuleList = this.findAllEnable();
        for (MenuEntity sysModule : userModuleList) {
            if (sysModule == null) {
                continue;
            }
            menuMap.put(sysModule.getId(), sysModule);
            if (!user.isAdmin() && urls.contains(StringUtils.trim(sysModule.getUrl()))) {
                authIds.add(sysModule.getId());
            }
        }
        if (!user.isAdmin()) {
            Long[] ids = authIds.toArray(new Long[authIds.size()]);
            for (Long id : ids) {
                MenuEntity menu = menuMap.get(id);
                while (menu != null) {
                    authIds.add(menu.getId());
                    menu = menuMap.get(menu.getParentId());
                }
            }
        }
        List<MenuEntity> outlookBar = new ArrayList<MenuEntity>();
        List<MenuEntity> norightList = new ArrayList<MenuEntity>();
        for (MenuEntity sysModule : userModuleList) {
            if (!user.isAdmin() && !authIds.contains(sysModule.getId())) {
                norightList.add(sysModule);
                continue;
            }
            if (sysModule.getParentId() == null || sysModule.getParentId().longValue() == 0) {
                outlookBar.add(sysModule);
                continue;
            }
            MenuEntity sysModuleParent = menuMap.get(sysModule.getParentId());
            if (sysModuleParent != null) {
                sysModuleParent.getChildList().add(sysModule);
            }
        }
        return outlookBar;
    }

    public List<MenuEntity> findTreeMenu() throws AppException {
        Map<Long, MenuEntity> menuMap = new HashMap<Long, MenuEntity>();
        List<MenuEntity> userModuleList = this.findAllEnable();
        for (MenuEntity sysModule : userModuleList) {
            if (sysModule == null) {
                continue;
            }
            menuMap.put(sysModule.getId(), sysModule);
        }
        List<MenuEntity> outlookBar = new ArrayList<MenuEntity>();
        for (MenuEntity sysModule : userModuleList) {
            if (sysModule.getParentId() == null || sysModule.getParentId().longValue() == 0) {
                outlookBar.add(sysModule);
                continue;
            }
            MenuEntity sysModuleParent = menuMap.get(sysModule.getParentId());
            if (sysModuleParent != null) {
                sysModuleParent.getChildList().add(sysModule);
            }
        }
        return outlookBar;
    }

    @Override
    public void upOrDown(Long id, Integer type) {

        MenuQuery query = new MenuQuery();
        query.setOrderColList(Arrays.asList(new OrderCol("orderId")));
        query.setOrderKind(OrderCol.ASCENDING);
        //判断移动的是一级菜单还是二级菜单
        MenuEntity menuEntity = this.get(id);
        if (menuEntity.getParentId() == 0) {
            //一级菜单
            query.setParentId(0L);
        } else {
            //二级菜单
            query.setParentId(menuEntity.getParentId());
        }

        List<MenuEntity> list = this.find(query);
        MenuEntity preEntity = null;
        MenuEntity curEntity = null;
        if (type == YesNoEnum.NO.getValue()) {
            //up
            for (int i = 0; i < list.size(); i++) {
                if (list.get(i).getId().equals(id) && i > 0) {
                    curEntity = list.get(i);
                    preEntity = list.get(i - 1);
                    break;
                }
            }

        } else {
            //down
            for (int i = 0; i < list.size(); i++) {
                if (list.get(i).getId().equals(id) && i + 1 < list.size()) {
                    curEntity = list.get(i);
                    preEntity = list.get(i + 1);
                    break;
                }
            }
        }

        if (preEntity != null && curEntity != null) {
            //交换
            int temp = preEntity.getOrderId();
            preEntity.setOrderId(curEntity.getOrderId());
            curEntity.setOrderId(temp);

            this.update(preEntity);
            this.update(curEntity);
        }

    }


    @Override
    protected void saveBefore(MenuEntity entity, Context context) throws AppException {

        MenuQuery query = new MenuQuery();
        query.setParentId(entity.getParentId());
        Comparator<Integer> comparator = Comparator.comparing(Integer::intValue);
        Optional<Integer> maxOptional = this.find(query).stream().map(item->{
            if(ObjectUtils.isEmpty(item.getOrderId())) return 1;
            return item.getOrderId();
        }).filter(f->f!=9999).max(comparator);
        maxOptional.ifPresent(e -> {
            e=e+1;
            entity.setOrderId(e);
        });


        MenuEntity parentMenuEntity = this.get(entity.getParentId());
        if (!ObjectUtils.isEmpty(parentMenuEntity) && SatusEnum.DISENABLE.getValue() == parentMenuEntity.getStatus()) {
            throw new AppException("菜单信息停用，不允许新增");
        }

        if (!ObjectUtils.isEmpty(parentMenuEntity)) {
            entity.setAncestors(parentMenuEntity.getAncestors() + "," + entity.getParentId());
        }

        super.saveBefore(entity, context);
    }

    @Override
    protected void updateBefore(MenuEntity entity, Context context) throws AppException {
        MenuEntity newParentEntity = this.get(entity.getParentId());
        MenuEntity oldEntity = this.get(entity.getId());
        if (!ObjectUtils.isEmpty(newParentEntity) && !ObjectUtils.isEmpty(oldEntity)) {
            String newAncestors = newParentEntity.getAncestors() + "," + newParentEntity.getId();
            String oldAncestors = oldEntity.getAncestors();
            entity.setAncestors(newAncestors);
            updateMenuChildren(entity.getId(), newAncestors, oldAncestors, context);
        }
        super.updateBefore(entity, context);
    }


    @Override
    protected void updateAfter(MenuEntity entity, Context context) throws AppException {
        if (SatusEnum.ENABLE.getValue() == entity.getStatus()) {
            entity= this.get(entity.getId());
            updateParentMenuStatus(entity, context);
        }
        super.updateAfter(entity, context);
    }

    /**
    * 修改该菜单信息的父级菜单信息状态
    *
    * @param menu 当前菜单信息
    */
    private void updateParentMenuStatus(MenuEntity menu, Context context) {

        MenuEntity menuEntity = this.get(menu.getParentId());
        if(!ObjectUtils.isEmpty(menuEntity)){
            menuEntity.setStatus(SatusEnum.ENABLE.getValue());
            menuEntity.setUpdateTime(new Date());
            menuEntity.setUpdateUser(context.getUser().getLoginName());
            MenuEntity condition = new MenuEntity();
            condition.setId(menuEntity.getId());
            this.updateBatch(menuEntity, condition, context);
        }

    }

    /**
    * 修改子元素关系
    *
    * @param menuId       被修改的菜单信息ID
    * @param newAncestors 新的父ID集合
    * @param oldAncestors 旧的父ID集合
    */
    public void updateMenuChildren(Long menuId, String newAncestors, String oldAncestors, Context context) {

        List<MenuEntity> children =  getDao().selectChildrenMenuById(menuId.toString());

        for (MenuEntity child : children) {
            child.setAncestors(child.getAncestors().replace(oldAncestors, newAncestors));
        }
        if (children.size() > 0) {
            this.updateAfter(children, context);
        }
    }


    @Override
    protected void removeBefore(Long[] ids, Context context) throws AppException {
        //有子节点 禁止删除
        if (hasChildByMenuId(ids[0])) {
            throw new AppException("存在下级菜单信息,不允许删除");
        }
        super.removeBefore(ids, context);
    }

    @Override
    public boolean hasChildByMenuId(Long menuId) {
        List<MenuEntity> list = this.find(new MenuQuery().parentId(menuId));
        return list.size() > 0 ? true : false;
    }

    @Override
    public List<MenuEntity> selectMenuList(MenuEntity menu) {
        return this.find(new MenuQuery());
    }

    @Override
    public List<MenuTreeSelect> buildMenuTreeSelect(List<MenuEntity> list) {
        List<MenuEntity> returnList = new ArrayList<>();
        List<Long> tempList = list.stream().map(MenuEntity::getId).collect(Collectors.toList());

        for (Iterator<MenuEntity> iterator = list.iterator(); iterator.hasNext(); ) {
            MenuEntity menuEntity = iterator.next();

            if (!tempList.contains(menuEntity.getParentId())) {
                recursionFn(list, menuEntity);
                returnList.add(menuEntity);
            }
            }
            if (returnList.isEmpty()) {
            returnList = list;
        }

        return returnList.stream().map(MenuTreeSelect::new).collect(Collectors.toList());
    }


    /**
    * 递归列表
    */
    private void recursionFn(List<MenuEntity> list, MenuEntity t) {
        // 得到子节点列表
        List<MenuEntity> childList = getChildList(list, t);
        t.setChildList(childList);
        for (MenuEntity tChild : childList) {
            if (hasChild(list, tChild)) {
                recursionFn(list, tChild);
            }
        }
    }

    /**
    * 判断是否有子节点
    */
    private boolean hasChild(List<MenuEntity> list, MenuEntity t) {
        return getChildList(list, t).size() > 0 ? true : false;
    }

    /**
    * 得到子节点列表
    */
    private List<MenuEntity> getChildList(List<MenuEntity> list, MenuEntity t) {
        return list.stream().map(item->{
            if (!ObjectUtils.isEmpty(item.getParentId()) && item.getParentId() == t.getId()) {
                return item;
            }
            return null;
            }).filter(f->f!=null).collect(Collectors.toList());
    }

    @Override
    public List<MenuTreeSelect> getListByParentId(Long parentId, Context context) {
        if (ObjectUtils.isEmpty(parentId)) {
            parentId = 0L;
        }
        //只做一层
        List<MenuTreeSelect> collect = this.find(new MenuQuery().parentId(parentId), context).stream().map(item -> new MenuTreeSelect(item)
        ).collect(Collectors.toList());
        if ("0".equals(parentId)) {
            return collect;
        }

        return collect;
    }

    @Override
    public Set<Long> getPathSetById(Long id, Context context) {
        Set<Long> idSet = new HashSet<>();
        //idSet.add(id);
        MenuEntity menuEntity = this.get(id, context);
        if(!ObjectUtils.isEmpty(menuEntity)){
           return extracted(context, idSet, menuEntity);
        }
        return idSet;
    }

    private Set<Long> extracted(Context context, Set<Long> idSet, MenuEntity menuEntity) {
        MenuEntity parentMenu = this.get(menuEntity.getParentId(), context);
        if(!ObjectUtils.isEmpty(parentMenu)){
            idSet.add(parentMenu.getId());
            return extracted(context,idSet,parentMenu);
        }
        return idSet;
    }

}