package com.sckw.system.service;

import java.util.Date;

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

import cn.hutool.core.collection.CollectionUtil;
import com.sckw.core.exception.SystemException;
import com.sckw.core.model.constant.Global;
import com.sckw.core.model.enums.ClientTypeEnum;
import com.sckw.core.model.enums.MenuTypeEnum;
import com.sckw.core.model.enums.SystemTypeEnum;
import com.sckw.core.utils.BeanUtils;
import com.sckw.core.utils.CollectionUtils;
import com.sckw.core.utils.IdWorker;
import com.sckw.core.utils.StringUtils;
import com.sckw.core.web.constant.HttpStatus;
import com.sckw.core.web.context.LoginUserHolder;
import com.sckw.system.dao.*;
import com.sckw.system.model.*;
import com.sckw.system.model.pojo.FindMenuTreePojo;
import com.sckw.system.model.vo.req.*;
import com.sckw.system.model.vo.res.*;
import net.sf.jsqlparser.expression.CollateExpression;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * 菜单service接口
 *
 * @author zk
 * @date 2023-05-31
 */
@Service
public class KwsMenuService {

    @Autowired
    KwsMenuDao kwsMenuDao;

    @Autowired
    KwsMenuRightsDao kwsMenuRightsDao;

    @Autowired
    KwsUserDao kwsUserDao;

    @Autowired
    KwsUserRoleDao kwsUserRoleDao;

    @Autowired
    KwsEnterpriseDao kwsEnterpriseDao;

    @Autowired
    KwsMenuMappingDao kwsMenuMappingDao;

    @Autowired
    KwsEntTypeDao kwsEntTypeDao;

    /**
     * @param params 实体
     * @desc: 添加新纪录
     * @author: czh
     * @date: 2023/7/7
     */
    @Transactional(rollbackFor = {})
    public void add(KwsMenu params) {
        Long parentId = params.getParentId();
        KwsMenu parent = null;
        if (!Objects.isNull(parentId) && parentId.compareTo(0L) != 0) {
            parent = kwsMenuDao.selectByKey(parentId);
            if (Objects.isNull(parent)) {
                throw new SystemException(HttpStatus.QUERY_FAIL_CODE, HttpStatus.PARENT_MENU_NOT_EXISTS);
            }
            params.setLevel(parent.getLevel() + 1);
        } else {
            params.setLevel(1);
            params.setParentId(0L);
        }

        //新增按钮时，有重复的序号，则把后面的都加1
        //按钮继承父菜单的using_roles
        if (params.getType() == MenuTypeEnum.BUTTON.getCode()) {
            if (!Objects.isNull(params.getSort())) {
                moveSort(params);
            }
            params.setUsingRoles(parent.getUsingRoles());
        }

        //新增菜单时，如果已经有同级菜单，则序号为最大的序号+1
        if (params.getType() == MenuTypeEnum.DIRECTORY.getCode()) {
            KwsMenu kwsMenuParam = new KwsMenu();
            kwsMenuParam.setType(params.getType());
            kwsMenuParam.setParentId(params.getParentId());
            List<KwsMenu> select = kwsMenuDao.select(kwsMenuParam);
            int sort = 1;
            if (!CollectionUtils.isEmpty(select)) {
                sort = select.get(select.size() - 1).getSort() + 1;
            }
            params.setSort(sort);
        }

        long menuId = new IdWorker(1L).nextId();
        params.setId(menuId);
        if (kwsMenuDao.insert(params) <= 0) {
            throw new SystemException(HttpStatus.CRUD_FAIL_CODE, HttpStatus.INSERT_FAIL);
        }

        List<FindEntListResVo> list = kwsEnterpriseDao.findList(new FindListReqVo());
        if (CollectionUtils.isNotEmpty(list)) {
            List<Long> entIds = list.stream().map(FindEntListResVo::getId).distinct().toList();
            List<KwsMenuRights> kwsMenuRightsList = new ArrayList<>();
            Date date = new Date();
            for (Long entId : entIds) {
                KwsMenuRights kwsMenuRights = new KwsMenuRights();
                kwsMenuRights.setEntId(entId);
                kwsMenuRights.setMenuId(menuId);
                kwsMenuRights.setId(new IdWorker(1L).nextId());
                kwsMenuRights.setDelFlag(Global.NO);
                kwsMenuRights.setStatus(Global.NO);
                kwsMenuRights.setCreateBy(LoginUserHolder.getUserId());
                kwsMenuRights.setUpdateBy(LoginUserHolder.getUserId());
                kwsMenuRights.setCreateTime(date);
                kwsMenuRights.setUpdateTime(date);
                kwsMenuRightsList.add(kwsMenuRights);
            }

            if (kwsMenuRightsDao.saveBatch(kwsMenuRightsList) < kwsMenuRightsList.size()) {
                throw new SystemException(HttpStatus.CRUD_FAIL_CODE, HttpStatus.INSERT_FAIL);
            }
        }

    }

    /**
     * @param params 需要新增的菜单
     * @desc: 菜单序号排序
     * @author: czh
     * @date: 2023/6/29
     */
    private void moveSort(KwsMenu params) {
        KwsMenu param = new KwsMenu();
        param.setParentId(params.getParentId());
        List<KwsMenu> list = kwsMenuDao.select(param);
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        list = list.stream().filter(item -> !Objects.isNull(item.getSort())).toList();
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        List<Integer> sortList = list.stream().map(KwsMenu::getSort).toList();
        int indexOf = sortList.indexOf(params.getSort());
        if (indexOf >= 0) {
            for (int n = indexOf; n < list.size(); n++) {
                KwsMenu kwsMenu = list.get(n);
                kwsMenu.setSort(kwsMenu.getSort() + 1);
                kwsMenuDao.update(kwsMenu);
                if (n + 1 < list.size() && list.get(n + 1).getSort() > kwsMenu.getSort()) {
                    break;
                }
            }
        }
    }

    /**
     * @param ids 主键di
     * @desc: 根据主键删除菜单
     * @author: czh
     * @date: 2023/6/25
     */
    public void deleteByKey(String ids) {
        List<Long> list = Arrays.stream(ids.split(Global.COMMA)).map(Long::parseLong).toList();
        /*1、删除菜单*/
        List<KwsMenu> kwsMenus = kwsMenuDao.selectByKeys(list);
        if (CollectionUtils.isEmpty(kwsMenus)) {
            return;
        }

        for (KwsMenu kwsMenu : kwsMenus) {
            kwsMenu.setDelFlag(Global.YES);
            if (kwsMenuDao.update(kwsMenu) <= 0) {
                throw new SystemException(HttpStatus.CRUD_FAIL_CODE, HttpStatus.DELETE_FAIL);
            }
        }

        /*2、删除菜单权限*/
        deleteMenuRightsByMenuIds(kwsMenus.stream().map(KwsMenu::getId).toList());
    }

    /**
     * @param menuIdList 菜单id集合
     * @desc: 删除菜单权限
     * @author: czh
     * @date: 2023/6/25
     */
    public void deleteMenuRightsByMenuIds(List<Long> menuIdList) {
        deleteMenuRights(kwsMenuRightsDao.selectByMenuIds(menuIdList));
    }

    /**
     * @param roleIdList 角色id集合
     * @desc: 删除菜单权限
     * @author: czh
     * @date: 2023/6/25
     */
    public void deleteMenuRightsByRoleIds(List<Long> roleIdList) {
        deleteMenuRights(kwsMenuRightsDao.selectByRoleIds(roleIdList));
    }

    private void deleteMenuRights(List<KwsMenuRights> kwsMenuRightsList) {
        if (CollectionUtils.isEmpty(kwsMenuRightsList)) {
            return;
        }

        for (KwsMenuRights kwsMenuRights : kwsMenuRightsList) {
            kwsMenuRights.setDelFlag(Global.YES);
            if (kwsMenuRightsDao.update(kwsMenuRights) <= 0) {
                throw new SystemException(HttpStatus.CRUD_FAIL_CODE, HttpStatus.DELETE_FAIL);
            }
        }
    }


    /**
     * @param params 更新记录
     * @desc: 更新记录
     * @author: czh
     * @date: 2023/7/3
     */
    public void update(KwsMenu params) {
        if (kwsMenuDao.update(params) <= 0) {
            throw new SystemException(HttpStatus.CRUD_FAIL_CODE, HttpStatus.UPDATE_FAIL);
        }
    }

    /**
     * @param key 主键
     * @return KwsMenu
     * @desc: 根据主键查询
     * @author: czh
     * @date: 2023/7/7
     */
    public MenuDetailResVo selectByKey(Long key) {
        KwsMenu kwsMenu = kwsMenuDao.selectByKey(key);
        //前端特殊处理，当父id为0时，设成null
        if (kwsMenu.getParentId().compareTo(0L) == 0) {
            kwsMenu.setParentId(null);
        }

        MenuDetailResVo menuDetailResVo = new MenuDetailResVo();
        BeanUtils.copyProperties(kwsMenu, menuDetailResVo);
        return menuDetailResVo;
    }

    /**
     * @param reqVo 入参
     * @return KwsMenuResVo
     * @desc: 分页查询
     * @author: czh
     * @date: 2023/7/7
     */
    public List<FindMenuTreeResVo> findTree(FindMenuTreeReqVo reqVo) {
        FindMenuTreePojo findMenuTreePojo = new FindMenuTreePojo();
        BeanUtils.copyProperties(reqVo, findMenuTreePojo);

        //填充用户参数
        extracted(reqVo.getUserId(), reqVo.getClientType(), findMenuTreePojo);

        List<KwsMenuResVo> menuList = kwsMenuDao.findList(findMenuTreePojo);
        if (CollectionUtils.isEmpty(menuList)) {
            return Collections.emptyList();
        }

        //app菜单特殊处理
        List<KwsMenuResVo> finalList = new ArrayList<>();
        if (SystemTypeEnum.COMPANY.getCode().equals(LoginUserHolder.getSystemType()) &&
                ClientTypeEnum.app.getValue().equals(LoginUserHolder.getClientType()) &&
                Objects.equals(LoginUserHolder.getIsMain(), Global.NO)) {
            List<KwsMenuMapping> kwsMenuMappings = kwsMenuMappingDao.selectByMenuIds(menuList.stream().map(KwsMenuResVo::getId).toList());
            if (CollectionUtils.isEmpty(kwsMenuMappings)) {
                return Collections.emptyList();
            }

            List<Long> mapIds = kwsMenuMappings.stream().map(KwsMenuMapping::getMappingId).toList();
            List<KwsMenu> kwsMenus = kwsMenuDao.selectByKeys(mapIds);
            if (CollectionUtils.isEmpty(kwsMenus)) {
                return Collections.emptyList();
            }

            finalList = BeanUtils.copyToList(kwsMenus, KwsMenuResVo.class);
        } else {
            finalList = menuList;
        }

        List<KwsMenuResVo> rootList = new ArrayList<>();

        //获取根节点数据
        if (CollectionUtil.isNotEmpty(finalList)) {
            int level = finalList.get(0).getLevel();
            for (KwsMenuResVo kwsMenu : finalList) {
                if (kwsMenu.getLevel() == level) {
                    rootList.add(kwsMenu);
                }
            }
        }

        //遍历，找到二级机构（根机构的id和所有机构中的pid比较）
        for (KwsMenuResVo kwsMenu : rootList) {
            List<KwsMenuResVo> child = getChild(kwsMenu.getId(), finalList);
            kwsMenu.setChildren(child);
        }
        return BeanUtils.copyToList(rootList, FindMenuTreeResVo.class);
    }

    public List<KwsMenuResVo> findList(FindMenuTreePojo findMenuTreePojo) {
        return kwsMenuDao.findList(findMenuTreePojo);
    }

    private void extracted(Long userId, String clientType, FindMenuTreePojo findMenuTreePojo) {
        findMenuTreePojo.setSystemType(LoginUserHolder.getSystemType());
        findMenuTreePojo.setIsMain(LoginUserHolder.getIsMain());
        //运营端的管理员不做过滤
        if (Objects.equals(LoginUserHolder.getSystemType(), SystemTypeEnum.MANAGE.getCode()) && Objects.equals(LoginUserHolder.getIsMain(), Global.YES)) {
            return;
        }

        if (Objects.equals(LoginUserHolder.getIsMain(), Global.YES)) {
            Long entId = LoginUserHolder.getEntId();
            List<KwsEntType> listByEntId = kwsEntTypeDao.findListByEntId(entId);
            findMenuTreePojo.setEntTypeList(listByEntId.stream().map(KwsEntType::getType).map(String::valueOf).toList());
            findMenuTreePojo.setEntId(LoginUserHolder.getEntId());
            return;
        }

        if (Objects.isNull(userId)) {
            userId = LoginUserHolder.getUserId();
        }
        KwsUser kwsUser = kwsUserDao.selectByKey(userId);
        if (Objects.isNull(kwsUser)) {
            throw new SystemException(HttpStatus.QUERY_FAIL_CODE, HttpStatus.ACCOUNT_NOT_EXISTS);
        }

        List<KwsUserRole> allByUserId = kwsUserRoleDao.findAllByUserId(userId);
        if (CollectionUtils.isEmpty(allByUserId)) {
            throw new SystemException(HttpStatus.QUERY_FAIL_CODE, HttpStatus.ROLE_NOT_EXISTS);
        }

        //特殊处理，员工账号先查pc端的菜单，后面会做菜单关联
        findMenuTreePojo.setClientType(Integer.valueOf(clientType));
        findMenuTreePojo.setRoleIds(allByUserId.stream().map(KwsUserRole::getRoleId).toList());
    }

    /**
     * 递归获取下级菜单
     *
     * @param parentId 父级机构id
     * @param deptList 所有机构
     * @return KwsMenuResVo
     */
    public List<KwsMenuResVo> getChild(Long parentId, List<KwsMenuResVo> deptList) {
        List<KwsMenuResVo> childList = new ArrayList<>();
        for (KwsMenuResVo kwsMenu : deptList) {
            if (parentId.equals(kwsMenu.getParentId())) {
                //获取当前菜单id的所有子列表
                childList.add(kwsMenu);
            }
        }
        for (KwsMenuResVo kwsMenu : childList) {
            //将该id的子菜单进行遍历，通过递归调用，获取每一个子菜单的子菜单数据
            List<KwsMenuResVo> child = getChild(kwsMenu.getId(), deptList);
            kwsMenu.setChildren(child);
        }
        if (childList.size() == 0) {
            //子菜单的长度为0,返回null,null不会被阿里的FastJson包解析
            return null;
        }
        return childList;
    }

    /**
     * @param reqVo 菜单ID多个用","隔开
     * @desc: 角色分配菜单
     * @author: czh
     * @date: 2023/6/13
     */
    public void roleBindMenu(RoleBindMenuReqVo reqVo) {
        List<Long> menuIdList = Arrays.stream(reqVo.getMenuIds().split(Global.COMMA)).map(Long::parseLong).collect(Collectors.toList());
        List<Long> allMenus = getAllMenus(menuIdList, new ArrayList<>());
        allMenus = allMenus.stream().distinct().toList();
        Date date = new Date();
        List<KwsMenuRights> list = new ArrayList<>();
        for (Long menuId : allMenus) {
            KwsMenuRights kwsMenuRights = new KwsMenuRights();
            kwsMenuRights.setEntId(reqVo.getEntId());
            kwsMenuRights.setMenuId(menuId);
            kwsMenuRights.setRoleId(reqVo.getRoleId());
            kwsMenuRights.setId(new IdWorker(1L).nextId());
            kwsMenuRights.setRemark(reqVo.getRemark());
            kwsMenuRights.setStatus(Global.NO);
            kwsMenuRights.setCreateBy(LoginUserHolder.getUserId());
            kwsMenuRights.setCreateTime(date);
            kwsMenuRights.setUpdateBy(LoginUserHolder.getUserId());
            kwsMenuRights.setUpdateTime(date);
            kwsMenuRights.setDelFlag(Global.NO);
            list.add(kwsMenuRights);
        }

        if (kwsMenuRightsDao.saveBatch(list) < list.size()) {
            throw new SystemException(HttpStatus.CRUD_FAIL_CODE, HttpStatus.INSERT_FAIL);
        }
    }

    public List<Long> getAllMenus(List<Long> menuIdList, List<Long> list) {
        List<KwsMenu> menuList = kwsMenuDao.selectByKeys(menuIdList);
        List<KwsMenu> childList = menuList.stream().filter(item -> !item.getParentId().equals(0L)).toList();
        if (CollectionUtils.isEmpty(childList)) {
            list.addAll(menuList.stream().map(KwsMenu::getId).toList());
            return list;
        }

        List<Long> longs = childList.stream().map(KwsMenu::getId).toList();
        for (KwsMenu kwsMenu : menuList) {
            if (!longs.contains(kwsMenu.getId())) {
                list.add(kwsMenu.getId());
            }
        }

        list.addAll(childList.stream().map(KwsMenu::getId).toList());
        return getAllMenus(childList.stream().map(KwsMenu::getParentId).collect(Collectors.toList()), list);
    }


    public List<KwsMenu> selectAll() {
        return kwsMenuDao.selectAll();
    }

    @Transactional(rollbackFor = {})
    public void moveMenu(MoveMenuReqVo reqVo) {
        KwsMenu kwsMenu = kwsMenuDao.selectByKey(reqVo.getId());
        if (Objects.isNull(kwsMenu)) {
            throw new SystemException(HttpStatus.QUERY_FAIL_CODE, HttpStatus.MENU_NOT_EXISTS);
        }

        //查出要被换位置的按钮
        KwsMenu targetMenu = getTargetMenu(reqVo.getMoveType(), kwsMenu);
        if (Objects.isNull(targetMenu)) {
            return;
        }

        //修改要换位置的按钮
        kwsMenu.setSort(kwsMenu.getSort() + reqVo.getMoveType());
        if (kwsMenuDao.update(kwsMenu) <= 0) {
            throw new SystemException(HttpStatus.CRUD_FAIL_CODE, HttpStatus.UPDATE_FAIL);
        }

        //更新被换位置的按钮
        targetMenu.setSort(targetMenu.getSort() - reqVo.getMoveType());
        if (kwsMenuDao.update(targetMenu) <= 0) {
            throw new SystemException(HttpStatus.CRUD_FAIL_CODE, HttpStatus.UPDATE_FAIL);
        }
    }


    private KwsMenu getTargetMenu(Integer moveType, KwsMenu kwsMenu) {
        KwsMenu kwsMenuParam = new KwsMenu();
        kwsMenuParam.setType(kwsMenu.getType());
        kwsMenuParam.setParentId(kwsMenu.getParentId());
        kwsMenuParam.setSort(kwsMenu.getSort() + moveType);
        kwsMenuParam.setDelFlag(Global.NO);
        List<KwsMenu> targetMenuList = kwsMenuDao.select(kwsMenuParam);
        return CollectionUtils.isEmpty(targetMenuList) ? null : targetMenuList.get(0);
    }


    public List<QueryChildMenuResVo> queryChildMenu(QueryChildMenuReqVo reqVo) {
        KwsMenu kwsMenuParam = new KwsMenu();
        kwsMenuParam.setParentId(reqVo.getId());
        kwsMenuParam.setType(reqVo.getType());
        kwsMenuParam.setName(reqVo.getName());
        kwsMenuParam.setPerms(reqVo.getPerms());
        List<KwsMenu> select = kwsMenuDao.queryChildMenu(kwsMenuParam);
        if (CollectionUtils.isEmpty(select)) {
            return Collections.emptyList();
        }
        return BeanUtils.copyToList(select, QueryChildMenuResVo.class);
    }


    /**
     * @param reqVo 入参
     * @desc: 保存菜单映射关系
     * @author: czh
     * @date: 2023/9/5
     */
    @Transactional(rollbackFor = {})
    public void saveMapping(SaveMappingReqVo reqVo) {
        Long menuId = reqVo.getMenuId();
        List<Long> mappingIdList = reqVo.getMappingIdList();
        List<KwsMenuMapping> kwsMenuMappings = kwsMenuMappingDao.selectByMenuId(menuId);
        Date date = new Date();
        if (CollectionUtils.isNotEmpty(kwsMenuMappings)) {
            for (KwsMenuMapping kwsMenuMapping : kwsMenuMappings) {
                kwsMenuMapping.setDelFlag(Global.YES);
                kwsMenuMapping.setUpdateBy(LoginUserHolder.getUserId());
                kwsMenuMapping.setUpdateTime(date);
                if (kwsMenuMappingDao.updateById(kwsMenuMapping) <= 0) {
                    throw new SystemException(HttpStatus.CRUD_FAIL_CODE, HttpStatus.UPDATE_FAIL);
                }
            }
        }

        List<KwsMenuMapping> list = new ArrayList<>();
        for (Long id : mappingIdList) {
            KwsMenuMapping kwsMenuMapping = new KwsMenuMapping();
            kwsMenuMapping.setMenuId(menuId);
            kwsMenuMapping.setMappingId(id);
            kwsMenuMapping.setId(new IdWorker(1L).nextId());
            kwsMenuMapping.setRemark(reqVo.getRemark());
            kwsMenuMapping.setStatus(Global.NO);
            kwsMenuMapping.setCreateBy(LoginUserHolder.getUserId());
            kwsMenuMapping.setCreateTime(date);
            kwsMenuMapping.setUpdateBy(LoginUserHolder.getUserId());
            kwsMenuMapping.setUpdateTime(date);
            kwsMenuMapping.setDelFlag(Global.NO);
            list.add(kwsMenuMapping);
        }
        kwsMenuMappingDao.saveBatch(list);
    }


    /**
     * @param id 菜单id
     * @return HttpResult
     * @desc: 根据菜单查映射关系
     * @author: czh
     * @date: 2023/6/13
     */
    public List<QueryMappingResVo> queryMapping(Long id) {
        List<KwsMenuMapping> kwsMenuMappings = kwsMenuMappingDao.selectByMenuId(id);
        if (CollectionUtils.isEmpty(kwsMenuMappings)) {
            return Collections.emptyList();
        }

        List<KwsMenu> kwsMenus = kwsMenuDao.selectByKeys(kwsMenuMappings.stream().map(KwsMenuMapping::getMappingId).toList());
        if (CollectionUtils.isEmpty(kwsMenus)) {
            return Collections.emptyList();
        }

        Map<Long, KwsMenu> collect = kwsMenus.stream().collect(Collectors.toMap(KwsMenu::getId, Function.identity(), (oldValue, newValue) -> newValue));

        List<QueryMappingResVo> list = new ArrayList<>();
        for (KwsMenuMapping kwsMenuMapping : kwsMenuMappings) {
            KwsMenu kwsMenus1 = collect.get(kwsMenuMapping.getMappingId());
            QueryMappingResVo queryMappingResVo = new QueryMappingResVo();
            queryMappingResVo.setName(kwsMenus1.getName());
            queryMappingResVo.setMappingMenuId(kwsMenuMapping.getMappingId());
            list.add(queryMappingResVo);
        }
        return list;
    }

    public List<Long> test(String ids) {
        return getAllMenus(StringUtils.splitStrToList(ids, Long.class), new ArrayList<>());
    }
}
