package com.sckw.system.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.sckw.contract.api.RemoteContractService;
import com.sckw.core.exception.SystemException;
import com.sckw.core.model.constant.Global;
import com.sckw.core.model.enums.ApprovalEnum;
import com.sckw.core.model.enums.ClientTypeEnum;
import com.sckw.core.model.enums.EntTypeEnum;
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.order.api.dubbo.TradeOrderInfoService;
import com.sckw.system.api.model.dto.res.EntTypeResDto;
import com.sckw.system.dao.*;
import com.sckw.system.dubbo.RemoteBaseService;
import com.sckw.system.model.*;
import com.sckw.system.model.pojo.FindEntListPojo;
import com.sckw.system.model.pojo.FindMenuTreePojo;
import com.sckw.system.model.pojo.FindPojoParam;
import com.sckw.system.model.vo.req.*;
import com.sckw.system.model.vo.res.*;
import com.sckw.system.util.MenuEntTypeForUsingRolesUtil;
import com.sckw.transport.api.dubbo.TransportRemoteService;
import org.apache.dubbo.config.annotation.DubboReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

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

    @Autowired
    KwsMenuDao kwsMenuDao;

    @Autowired
    private KwsMenuMpMapper kwsMenuMpMapper;

    @Autowired
    private KwsMenuRightsMpMapper kwsMenuRightsMpMapper;

    @Autowired
    KwsMenuRightsDao kwsMenuRightsDao;

    @Autowired
    KwsUserDao kwsUserDao;

    @Autowired
    KwsUserRoleDao kwsUserRoleDao;

    @Autowired
    KwsEnterpriseDao kwsEnterpriseDao;

    @Autowired
    KwsMenuMappingDao kwsMenuMappingDao;

    @Autowired
    KwsEntTypeDao kwsEntTypeDao;
    @DubboReference(version = "1.0.0", group = "design", check = false, timeout = 8000)
    private RemoteContractService remoteContractService;
    @DubboReference(version = "1.0.0", group = "design", check = false, timeout = 8000)
    private TradeOrderInfoService tradeOrderInfoService;
    @DubboReference(version = "1.0.0", group = "design", check = false, timeout = 8000)
    private TransportRemoteService transportRemoteService;

    @Autowired
    private RemoteBaseService remoteBaseService;

    /**
     * @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
     */

    /**
     * 查询 App 端菜单或按钮权限列表。
     * <p>
     * 核心逻辑：
     * 1. 解析客户端类型和权限查询类型。
     * 2. 填充查询条件对象（包括系统类型、角色ID、企业类型等）。
     * 3. 判断是否绕过权限校验（运营端主账号拥有所有权限）。
     * 4. 根据是否绕过权限，执行不同的菜单查询策略。
     * 5. 如果是企业用户且使用 App 端，应用菜单映射逻辑（App 菜单映射到 PC 菜单）。
     * 6. 对结果进行排序（先按层级，再按序号）。
     * 7. 如果请求包含按钮信息，则组装菜单及其子按钮；否则直接返回菜单列表。
     *
     * @param reqVo 查询请求参数，包含客户端类型、用户ID、权限查询类型等
     * @return 菜单或按钮权限项列表
     */
    public List<AppMenuPermItemResVo> queryAppMenuOrButtonPerms(AppMenuPermQueryReqVo reqVo) {
        // 解析客户端类型，去除空格并转为整数
        int clientType = Integer.parseInt(reqVo.getClientType().trim());
        // 获取权限查询类型（如：仅菜单、仅按钮、菜单+按钮）
        int permQueryType = reqVo.getPermQueryType();

        // 构建查询条件对象
        FindMenuTreePojo pojo = new FindMenuTreePojo();
        pojo.setClientType(clientType);
        // 填充用户相关的查询条件（如角色ID、企业类型、系统类型等）
        extracted(reqVo.getUserId(), reqVo.getClientType(), pojo);

        // 判断是否绕过权限校验：运营端系统类型且为主账号时，无需校验具体权限
        boolean bypassRights = Objects.equals(LoginUserHolder.getSystemType(), SystemTypeEnum.MANAGE.getCode())
                && Objects.equals(LoginUserHolder.getIsMain(), Global.YES);

        // 根据是否绕过权限，选择查询策略
        List<KwsMenu> menus = bypassRights
                ? selectMenusByMpWithoutRights(clientType, permQueryType, pojo) // 无权限限制查询
                : selectMenusByMpWithRights(clientType, permQueryType, pojo);   // 有权限限制查询


        // 对菜单列表进行排序：先按层级（level）升序，再按序号（sort）升序，null 值排在最后
//        menus.sort(Comparator.comparing(KwsMenu::getLevel, Comparator.nullsLast(Integer::compareTo))
//                .thenComparing(KwsMenu::getSort, Comparator.nullsLast(Integer::compareTo)));

        // 如果查询类型为“菜单带按钮”，则组装菜单及其嵌套的按钮列表
        if (permQueryType == AppMenuPermQueryReqVo.PERM_QUERY_MENU_WITH_BUTTONS) {
            return assembleMenusWithNestedButtons(menus, pojo);
        }
        return menus.stream().map(this::toAppMenuPermItemResVo).toList();
    }

    /**
     * 将菜单实体转为权限 VO，并对所有字段做空值默认填充，保证返回 JSON 中字段均有明确取值。
     */
    private AppMenuPermItemResVo toAppMenuPermItemResVo(KwsMenu source) {
        AppMenuPermItemResVo v = new AppMenuPermItemResVo();
        if (source != null) {
            BeanUtils.copyProperties(source, v);
        }
        //fillAppMenuPermVoDefaults(v);
        return v;
    }

    private void fillAppMenuPermVoDefaults(AppMenuPermItemResVo v) {
        if (v.getId() == null) {
            v.setId(0L);
        }
        if (v.getParentId() == null) {
            v.setParentId(0L);
        }
        if (v.getClientType() == null) {
            v.setClientType(0);
        }
        if (v.getPerms() == null) {
            v.setPerms("");
        }
        if (v.getName() == null) {
            v.setName("");
        }
        if (v.getType() == null) {
            v.setType(0);
        }
        if (v.getUrl() == null) {
            v.setUrl("");
        }
        if (v.getLinks() == null) {
            v.setLinks("");
        }
        if (v.getIcon() == null) {
            v.setIcon("");
        }
        if (v.getSort() == null) {
            v.setSort(0);
        }
        if (v.getLevel() == null) {
            v.setLevel(0);
        }
        if (v.getCustom() == null) {
            v.setCustom(0);
        }
        if (v.getIsDisplay() == null) {
            v.setIsDisplay(0);
        }
        if (v.getIsMain() == null) {
            v.setIsMain(0);
        }
        if (v.getUsingRoles() == null) {
            v.setUsingRoles("");
        }
        if (v.getRemark() == null) {
            v.setRemark("");
        }
        if (v.getStatus() == null) {
            v.setStatus(0);
        }
        if (v.getCreateBy() == null) {
            v.setCreateBy(0L);
        }
        if (v.getUpdateBy() == null) {
            v.setUpdateBy(0L);
        }
        if (v.getCreateTime() == null) {
            v.setCreateTime(new Date(0));
        }
        if (v.getUpdateTime() == null) {
            v.setUpdateTime(new Date(0));
        }
        if (v.getDelFlag() == null) {
            v.setDelFlag(0);
        }
        if (v.getButtons() == null) {
            v.setButtons(new ArrayList<>());
        }
    }

    /**
     * 在已确定的菜单（目录+菜单）集合上，用 MyBatis-Plus 批量查询 parent 在其 id 集合下的按钮并挂到每条菜单下。
     * <p>
     * 步骤：
     * 1. 提取所有菜单的 ID 作为父 ID 集合。
     * 2. 批量查询这些父 ID 下的所有按钮类型的菜单。
     * 3. 将按钮按父 ID 分组。
     * 4. 遍历菜单列表，为每个菜单设置其对应的子按钮列表。
     *
     * @param menus 菜单列表（不含按钮）
     * @param pojo  查询条件对象，用于过滤按钮的企业类型权限
     * @return 组装好子按钮的菜单权限项列表
     */
    private List<AppMenuPermItemResVo> assembleMenusWithNestedButtons(List<KwsMenu> menus, FindMenuTreePojo pojo) {
        // 如果菜单列表为空，直接返回空列表
        if (CollectionUtils.isEmpty(menus)) {
            return Collections.emptyList();
        }
        // 提取所有菜单的 ID，作为查询子按钮的父 ID 条件
        List<Long> parentIds = menus.stream().map(KwsMenu::getId).toList();
        // 获取客户端类型，确保查询的按钮与菜单属于同一客户端
        Integer btnClientType = menus.get(0).getClientType();

        // 构建按钮查询条件
        LambdaQueryWrapper<KwsMenu> bw = Wrappers.lambdaQuery(KwsMenu.class)
                .eq(KwsMenu::getDelFlag, Global.NO) // 未删除
                .eq(KwsMenu::getType, MenuTypeEnum.BUTTON.getCode()) // 仅限按钮类型
                .eq(KwsMenu::getClientType, btnClientType) // 匹配客户端类型
                .in(KwsMenu::getParentId, parentIds); // 父 ID 在菜单 ID 集合中
        // 追加企业类型过滤，确保按钮也符合当前用户的企业类型权限
        appendUsingRolesFilter(bw, pojo.getEntTypeList());
        // 按序号升序排列
        bw.orderByAsc(KwsMenu::getSort);

        // 执行查询，获取所有相关按钮
        List<KwsMenu> buttonRows = kwsMenuMpMapper.selectList(bw);
        // 将按钮按父 ID 分组，使用 LinkedHashMap 保持插入顺序
        Map<Long, List<KwsMenu>> buttonsByParent = buttonRows.stream()
                .collect(Collectors.groupingBy(KwsMenu::getParentId, LinkedHashMap::new, Collectors.toList()));

        List<AppMenuPermItemResVo> result = new ArrayList<>(menus.size());
        menus=menus.stream().filter(x->Objects.equals(x.getParentId(),0L) && Objects.equals(x.getType(),1)).collect(Collectors.toList());
        Long entId = LoginUserHolder.getEntId();
        List<KwsEntType> listByEntId = kwsEntTypeDao.findListByEntId(entId);
        List<String> entTypes =listByEntId.stream().map(KwsEntType::getType).map(String::valueOf).toList();
        for (KwsMenu m : menus) {
            if (Objects.equals(m.getType(), MenuTypeEnum.DIRECTORY.getCode())
                    && CollectionUtils.isNotEmpty(entTypes)
                    && !MenuEntTypeForUsingRolesUtil.usingRolesMatchesAnyEntType(m.getUsingRoles(), entTypes)) {
                continue;
            }
            AppMenuPermItemResVo vo = toAppMenuPermItemResVo(m);
            List<KwsMenu> children = buttonsByParent.getOrDefault(m.getId(), Collections.emptyList());
            List<AppMenuPermItemResVo> btnVos = new ArrayList<>(children.size());
            for (KwsMenu c : children) {
                // type 为按钮（2）时，在已登录企业类型非空的前提下，要求 using_roles 与任一企业类型匹配，否则不下发
                if (Objects.equals(c.getType(), MenuTypeEnum.BUTTON.getCode())
                        && CollectionUtils.isNotEmpty(entTypes)
                        && !MenuEntTypeForUsingRolesUtil.usingRolesMatchesAnyEntType(c.getUsingRoles(), entTypes)) {
                    continue;
                }
                btnVos.add(toAppMenuPermItemResVo(c));
            }
            vo.setButtons(btnVos);
            result.add(vo);
        }
        return result;
    }

    /**
     * 无需权限校验的菜单查询（通常用于运营端主账号）。
     * <p>
     * 直接根据客户端类型、权限查询类型和企业类型过滤菜单，不关联菜单权限表。
     *
     * @param clientType    客户端类型
     * @param permQueryType 权限查询类型（菜单或按钮）
     * @param pojo          查询条件对象，包含企业类型列表
     * @return 菜单列表
     */
    private List<KwsMenu> selectMenusByMpWithoutRights(int clientType, int permQueryType, FindMenuTreePojo pojo) {
        // 构建基础查询条件：未删除且匹配客户端类型
        LambdaQueryWrapper<KwsMenu> mw = Wrappers.lambdaQuery(KwsMenu.class)
                .eq(KwsMenu::getDelFlag, Global.NO)
                .eq(KwsMenu::getClientType, clientType);
        
        // 追加权限查询类型过滤（区分查询菜单/目录还是按钮）
        appendPermQueryType(mw, permQueryType);
        // 追加企业类型过滤（基于 using_roles 字段模糊匹配）
        appendUsingRolesFilter(mw, pojo.getEntTypeList());
        
        // 执行查询并返回结果
        return kwsMenuMpMapper.selectList(mw);
    }

    /**
     * 需要权限校验的菜单查询。
     * <p>
     * 首先从菜单权限表（kws_menu_rights）中查询当前用户/企业有权限的菜单ID集合，
     * 然后根据这些ID查询具体的菜单信息，并附加其他过滤条件。
     *
     * @param clientType    客户端类型
     * @param permQueryType 权限查询类型
     * @param pojo          查询条件对象，包含角色ID、企业ID、企业类型列表
     * @return 菜单列表
     */
    private List<KwsMenu> selectMenusByMpWithRights(int clientType, int permQueryType, FindMenuTreePojo pojo) {
        // 构建菜单权限查询条件
        LambdaQueryWrapper<KwsMenuRights> rw = Wrappers.lambdaQuery(KwsMenuRights.class)
                .eq(KwsMenuRights::getDelFlag, Global.NO) // 未删除
                .select(KwsMenuRights::getMenuId); // 只查询菜单ID字段，优化性能
        
        // 如果有角色ID限制，则加入 in 条件
        if (CollectionUtils.isNotEmpty(pojo.getRoleIds())) {
            rw.in(KwsMenuRights::getRoleId, pojo.getRoleIds());
        }
        // 如果有企业ID限制，则加入 eq 条件
        if (pojo.getEntId() != null) {
            rw.eq(KwsMenuRights::getEntId, pojo.getEntId());
        }
        
        // 执行查询，获取有权限的菜单ID行记录
        List<KwsMenuRights> rightsRows = kwsMenuRightsMpMapper.selectList(rw);
        
        // 提取菜单ID集合，去重
        Set<Long> menuIds = rightsRows.stream()
                .map(KwsMenuRights::getMenuId)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());
        
        // 如果没有权限ID，直接返回空列表
        if (menuIds.isEmpty()) {
            return Collections.emptyList();
        }
        
        // 构建菜单查询条件
        LambdaQueryWrapper<KwsMenu> mw = Wrappers.lambdaQuery(KwsMenu.class)
                .eq(KwsMenu::getDelFlag, Global.NO) // 未删除
                .eq(KwsMenu::getClientType, clientType) // 匹配客户端类型
                .in(KwsMenu::getId, menuIds); // 限制在有权重的菜单ID范围内
        
        // 追加权限查询类型过滤
        appendPermQueryType(mw, permQueryType);
        // 追加企业类型过滤
        appendUsingRolesFilter(mw, pojo.getEntTypeList());
        
        return kwsMenuMpMapper.selectList(mw);
    }

    /**
     * 根据权限查询类型，向查询条件中追加菜单类型过滤。
     * <p>
     * 如果查询类型为按钮，则只查询按钮类型；
     * 否则查询目录类型（type=0 或 type=DIRECTORY）。
     *
     * @param mw            菜单查询包装器
     * @param permQueryType 权限查询类型常量
     */
    private void appendPermQueryType(LambdaQueryWrapper<KwsMenu> mw, int permQueryType) {
        if (permQueryType == AppMenuPermQueryReqVo.PERM_QUERY_BUTTON) {
            // 仅查询按钮类型
            mw.eq(KwsMenu::getType, MenuTypeEnum.BUTTON.getCode());
        } else {
            // 查询目录或菜单类型（排除按钮）
            // 注意：这里兼容了 type=0 的历史数据或特定定义
            mw.and(w -> w.eq(KwsMenu::getType, 0).or().eq(KwsMenu::getType, MenuTypeEnum.DIRECTORY.getCode()));
        }
    }

    /**
     * 向查询条件中追加企业类型（using_roles）的模糊匹配过滤。
     * <p>
     * 多个企业类型之间使用 OR 连接，即只要 matching 其中一个类型即可。
     * SQL 示例: AND (using_roles LIKE '%type1%' OR using_roles LIKE '%type2%')
     *
     * @param mw           菜单查询包装器
     * @param entTypeList  企业类型列表
     */
    private void appendUsingRolesFilter(LambdaQueryWrapper<KwsMenu> mw, List<String> entTypeList) {
        // 如果企业类型列表为空，不进行过滤
        if (CollectionUtils.isEmpty(entTypeList)) {
            return;
        }
        // 构建 (like A or like B or ...) 的条件组
        mw.and(w -> {
            for (int i = 0; i < entTypeList.size(); i++) {
                String t = entTypeList.get(i);
                if (i == 0) {
                    // 第一个条件直接使用 like
                    w.like(KwsMenu::getUsingRoles, t);
                } else {
                    // 后续条件使用 or like
                    w.or().like(KwsMenu::getUsingRoles, t);
                }
            }
        });
    }



    public List<FindMenuTreeResVo> findTree(FindMenuTreeReqVo reqVo) {
        FindMenuTreePojo findMenuTreePojo = new FindMenuTreePojo();
        BeanUtils.copyProperties(reqVo, findMenuTreePojo);

        //填充用户参数
        extracted(reqVo.getUserId(), reqVo.getClientType(), findMenuTreePojo);
        List<KwsMenuResVo> menuList = new ArrayList<>();
        if (Objects.equals(LoginUserHolder.getSystemType(), SystemTypeEnum.COMPANY.getCode()) && org.apache.commons.lang3.StringUtils.equals(reqVo.getClientType(), String.valueOf(Global.NUMERICAL_THREE))) {
            menuList = kwsMenuDao.findList1(findMenuTreePojo);
        }else {
            menuList = kwsMenuDao.findList(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())) {
//            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 = menuList;
          //  finalList = BeanUtils.copyToList(kwsMenus, KwsMenuResVo.class);
        } else {
            finalList = menuList;
        }
        if (Objects.equals(LoginUserHolder.getSystemType(), SystemTypeEnum.COMPANY.getCode()) ) {
//            if (Boolean.TRUE.equals(reqVo.getIncludeButton())) {
//
//            }
            finalList = supplementButtons(finalList, findMenuTreePojo.getEntTypeList(),Integer.valueOf(reqVo.getClientType()));
        }


        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;
        }
        //pc端和app权限不做过滤，
        // 根据用户id查询角色id
        if (Objects.equals(LoginUserHolder.getSystemType(), SystemTypeEnum.COMPANY.getCode()) && org.apache.commons.lang3.StringUtils.equals(clientType, String.valueOf(Global.NUMERICAL_THREE)) && Objects.equals(LoginUserHolder.getIsMain(), Global.YES)) {
            return;
        }

        if (Objects.equals(LoginUserHolder.getSystemType(), SystemTypeEnum.COMPANY.getCode()) && org.apache.commons.lang3.StringUtils.equals(clientType, String.valueOf(Global.NUMERICAL_THREE))) {
            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);
            }
            //改为单一角色切换形式，获取当前激活的角色。
            findMenuTreePojo.setRoleIds(Collections.singletonList(LoginUserHolder.getCurrentRoleId()));
            return;
        }
        //特殊处理，后面会做菜单关联
        findMenuTreePojo.setClientType(clientType.equals(String.valueOf(Global.NUMERICAL_THREE)) ? Global.NUMERICAL_TWO : Integer.parseInt(clientType));
        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);
        }
        //改为单一角色切换形式，获取当前激活的角色。
        findMenuTreePojo.setRoleIds(Collections.singletonList(LoginUserHolder.getCurrentRoleId()));
    }

    /**
     * 补充按钮节点到菜单列表中
     * <p>
     * 查询所有菜单类型节点（type=1）的子按钮（type=2），
     * 合并到列表中以便递归构建包含按钮的完整树。
     */
    private List<KwsMenuResVo> supplementButtons(List<KwsMenuResVo> menuList, List<String> entTypeList, Integer clientType) {
        List<Long> menuTypeIds = menuList.stream()
                .filter(m -> Objects.equals(m.getType(), MenuTypeEnum.DIRECTORY.getCode()))
                .map(KwsMenuResVo::getId)
                .toList();
        if (CollectionUtils.isEmpty(menuTypeIds)) {
            return menuList;
        }

        LambdaQueryWrapper<KwsMenu> buttonWrapper = Wrappers.lambdaQuery(KwsMenu.class)
                .eq(KwsMenu::getDelFlag, Global.NO)
                .eq(KwsMenu::getType, MenuTypeEnum.BUTTON.getCode())
                .in(KwsMenu::getParentId, menuTypeIds)
                .eq(KwsMenu::getClientType, clientType) // 确保按钮与菜单属于同一客户端
                .orderByAsc(KwsMenu::getSort);
        appendUsingRolesFilter(buttonWrapper, entTypeList);

        List<KwsMenu> buttons = kwsMenuMpMapper.selectList(buttonWrapper);
        if (CollectionUtils.isEmpty(buttons)) {
            return menuList;
        }

        Set<Long> existingIds = menuList.stream().map(KwsMenuResVo::getId).collect(Collectors.toSet());
        List<KwsMenuResVo> result = new ArrayList<>(menuList);
        for (KwsMenu btn : buttons) {
            if (!existingIds.contains(btn.getId())) {
                KwsMenuResVo vo = new KwsMenuResVo();
                BeanUtils.copyProperties(btn, vo);
                result.add(vo);
            }
        }
        return result;
    }

    /**
     * 递归获取下级菜单
     *
     * @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<>());
    }

    /**
     * 查询
     *
     * @param reqVo
     * @return
     */
    public MenuTreeCountResVo list(FindMenuTreeReqVo reqVo) {
        List<FindMenuTreeResVo> tree = findTree(reqVo);
        long total = 0L;
        for (FindMenuTreeResVo findMenuTreeResVo : tree) {
            //平台运营端
            if (StrUtil.equals(findMenuTreeResVo.getName(), "企业客户")) {
                long sum = 0L;
                List<FindMenuTreeResVo> children = findMenuTreeResVo.getChildren();
                for (FindMenuTreeResVo child : children) {
                    if (StrUtil.equals(child.getName(), "入驻认证")) {
                        List<Long> authEntIdList = Objects.equals(LoginUserHolder.getSystemType(), SystemTypeEnum.MANAGE.getCode()) ? new ArrayList<>() : LoginUserHolder.getAuthEntIdList();
                        List<FindEntListPojo> list = kwsEnterpriseDao.findPojoAndManager(new FindPojoParam(), authEntIdList);
                        Map<Integer, List<FindEntListPojo>> collect = list.stream().collect(Collectors.groupingBy(FindEntListPojo::getApproval));
                        List<FindEntListPojo> a = collect.get(ApprovalEnum.PROCESS.getCode());
                        List<FindEntListPojo> b = collect.get(ApprovalEnum.REFRESH.getCode());
                        child.setCount((CollUtil.isNotEmpty(a) ? a.size() : 0L) + (CollUtil.isNotEmpty(b) ? b.size() : 0L));
                        sum = sum + child.getCount();
                        total = total + child.getCount();
                    }
                }
                findMenuTreeResVo.setCount(sum);
                continue;
            }
            //企业管理端
            if (StrUtil.equals(findMenuTreeResVo.getName(), "合同管理")) {
                List<FindMenuTreeResVo> children = findMenuTreeResVo.getChildren();
                long sum = 0L;
                if (CollUtil.isNotEmpty(children)) {
                    for (FindMenuTreeResVo child : children) {
                        if (StrUtil.equals(child.getName(), "贸易合同") && hasPerm(child.getChildren(), "tradeContract:sign")) {
                            Long entId = LoginUserHolder.getEntId();
                            Integer entType = remoteBaseService.queryEntType(entId);
                            Long aLong = remoteContractService.queryTradeCount(entId, entType);
                            if (Objects.isNull(aLong)) {
                                aLong = 0L;
                            }
                            child.setCount(aLong);
                            sum = sum + aLong;
                            total = total + aLong;
                            continue;
                        }
                        if (StrUtil.equals(child.getName(), "物流合同") && hasPerm(child.getChildren(), "logisticsContract:sign")) {
                            Long entId = LoginUserHolder.getEntId();
                            Integer entType = remoteBaseService.queryEntType(entId);
                            Long aLong = remoteContractService.queryLogisticsCount(entId, entType);
                            if (Objects.isNull(aLong)) {
                                aLong = 0L;
                            }
                            child.setCount(aLong);
                            sum = sum + aLong;
                            total = total + aLong;
                        }
                    }
                }
                findMenuTreeResVo.setCount(sum);
                continue;
            }
            if (StrUtil.equals(findMenuTreeResVo.getName(), "运营管理")) {
                List<FindMenuTreeResVo> children = findMenuTreeResVo.getChildren();
                long sum = 0L;
                if (CollUtil.isNotEmpty(children)) {
                    for (FindMenuTreeResVo child : children) {
                        if (StrUtil.equals(child.getName(), "贸易订单") && hasPerm(child.getChildren(), "tradeOrder:audit")) {
                            Long aLong = tradeOrderInfoService.queryTradeCount();
                            child.setCount(aLong);
                            sum = sum + aLong;
                            total = total + aLong;
                            continue;
                        }
                        if (StrUtil.equals(child.getName(), "物流订单") && hasPerm(child.getChildren(), "logisticsOrder:add")) {
                            Long entId = LoginUserHolder.getEntId();
                            Long aLong = 0L;
                            List<EntTypeResDto> entTypeResDtoList = remoteBaseService.queryEntTypeById(entId);
                            if (CollectionUtils.isNotEmpty(entTypeResDtoList)) {
                                EntTypeResDto entTypeResDto = entTypeResDtoList.get(0);
                                Integer type = entTypeResDto.getType();
                                if (Objects.nonNull(type) && !Objects.equals(type, 1)) {
                                    aLong = transportRemoteService.queryLogisticsCount(entId, type);
                                }
                            }
                            child.setCount(aLong);
                            sum = sum + aLong;
                            total = total + aLong;
                            continue;
                        }
                        if (StrUtil.equals(child.getName(), "物流运单") && hasPerm(child.getChildren(), "waybillList:verification")) {
                            Long aLong = transportRemoteService.queryWaybillCount();
                            child.setCount(aLong);
                            sum = sum + aLong;
                            total = total + aLong;
                        }

                    }

                }
                findMenuTreeResVo.setCount(sum);
            }
        }
        return new MenuTreeCountResVo(total, tree);
    }

    private boolean hasPerm(List<FindMenuTreeResVo> children, String perm) {
        if (CollUtil.isNotEmpty(children)) {
            return children.stream().anyMatch(d -> StrUtil.equals(d.getPerms(), perm));
        }
        return false;
    }
}
