package com.njcn.gather.user.user.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.njcn.common.pojo.constant.PatternRegex;
import com.njcn.common.pojo.enums.common.DataStateEnum;
import com.njcn.common.pojo.exception.BusinessException;
import com.njcn.gather.user.pojo.constant.FunctionConst;
import com.njcn.gather.user.pojo.enums.UserResponseEnum;
import com.njcn.gather.user.user.mapper.SysFunctionMapper;
import com.njcn.gather.user.user.pojo.param.SysFunctionParam;
import com.njcn.gather.user.user.pojo.po.SysFunction;
import com.njcn.gather.user.user.pojo.vo.MenuVO;
import com.njcn.gather.user.user.pojo.vo.MetaVO;
import com.njcn.gather.user.user.service.ISysFunctionService;
import com.njcn.gather.user.user.service.ISysRoleFunctionService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

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

/**
 * @author caozehui
 * @date 2024-11-12
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class SysFunctionServiceImpl extends ServiceImpl<SysFunctionMapper, SysFunction> implements ISysFunctionService {

    private final ISysRoleFunctionService sysRoleFunctionService;

    @Override
    public List<SysFunction> getFunctionTreeByKeyword(String keyword) {
        List<SysFunction> functionTree = this.getFunctionTree(true);
        filterTreeByName(functionTree, keyword);
        return functionTree;
    }

    @Override
    @Transactional
    public boolean addFunction(SysFunctionParam functionParam) {
        functionParam.setName(functionParam.getName().trim());
        functionParam.setPath(functionParam.getPath().trim());
        functionParam.setComponent(functionParam.getComponent().trim());
        checkFunctionParam(functionParam, false);
        SysFunction function = new SysFunction();
        BeanUtil.copyProperties(functionParam, function);
        function.setState(DataStateEnum.ENABLE.getCode());
        if (Objects.equals(functionParam.getPid(), FunctionConst.FATHER_PID)) {
            function.setPids(FunctionConst.FATHER_PID);
        } else {
            SysFunction fatherFunction = this.lambdaQuery().eq(SysFunction::getId, functionParam.getPid()).one();
            if (Objects.equals(fatherFunction.getPid(), FunctionConst.FATHER_PID)) {
                function.setPids(functionParam.getPid());
            } else {
                String pidS = fatherFunction.getPids();
                function.setPids(pidS + "," + functionParam.getPid());
            }
        }
        return this.save(function);
    }

    @Override
    @Transactional
    public boolean updateFunction(SysFunctionParam.UpdateParam param) {
        param.setName(param.getName().trim());
        boolean result = false;
        param.setPath(param.getPath().trim());
        param.setComponent(param.getComponent().trim());
        checkFunctionParam(param, true);
        SysFunction oldFunction = this.lambdaQuery().eq(SysFunction::getId, param.getId()).eq(SysFunction::getState, DataStateEnum.ENABLE.getCode()).one();
        List<SysFunction> childrenList = this.lambdaQuery().eq(SysFunction::getPid, param.getId()).eq(SysFunction::getState, DataStateEnum.ENABLE.getCode()).list();
        if (oldFunction.getType().equals(FunctionConst.TYPE_MENU) && param.getType().equals(FunctionConst.TYPE_BUTTON) && !CollectionUtils.isEmpty(childrenList)) {
            throw new BusinessException(UserResponseEnum.EXISTS_CHILDREN_NOT_UPDATE);
        } else {
            SysFunction function = new SysFunction();
            BeanUtil.copyProperties(param, function);
            result = this.updateById(function);
        }
        return result;
    }

    @Override
    @Transactional
    public boolean deleteFunction(String id) {
        boolean result1 = false;
        sysRoleFunctionService.deleteRoleFunctionByFunctionIds(Collections.singletonList(id));
        List<SysFunction> childrenList = this.lambdaQuery().eq(SysFunction::getState, DataStateEnum.ENABLE.getCode()).eq(SysFunction::getPid, id).list();
        if (CollectionUtils.isEmpty(childrenList)) {
            result1 = this.lambdaUpdate().set(SysFunction::getState, DataStateEnum.DELETED.getCode()).in(SysFunction::getId, id).update();
        } else {
            throw new BusinessException(UserResponseEnum.EXISTS_CHILDREN_NOT_DELETE);
        }
        return result1;
    }

    @Override
    public List<SysFunction> getFunctionTree(boolean isContainButton) {
        LambdaQueryWrapper<SysFunction> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(SysFunction::getState, DataStateEnum.ENABLE.getCode());
        if (isContainButton) {
            wrapper.in(SysFunction::getType, FunctionConst.TYPE_MENU, FunctionConst.TYPE_BUTTON);
        } else {
            wrapper.in(SysFunction::getType, FunctionConst.TYPE_MENU);
        }
        List<SysFunction> allFunctions = this.list(wrapper);
        return allFunctions.stream().filter(fun -> Objects.equals(FunctionConst.FATHER_PID, fun.getPid())).peek(funS -> funS.setChildren(getChildrenList(funS, allFunctions))).sorted(Comparator.comparingInt(SysFunction::getSort)).collect(Collectors.toList());
    }

    @Override
    public List<MenuVO> getMenuByUserId(String userId) {
        List<MenuVO> menu = this.baseMapper.getMenuByUserId(userId);
        menu.stream().forEach(m -> {
            MetaVO meta = new MetaVO();
            meta.setIcon(m.getIcon());
            meta.setTitle(m.getName());
            meta.setIsLink("");
            meta.setHide(false);
            meta.setFull(false);
            meta.setAffix(false);
            if("home".equals(m.getCode())){
                meta.setAffix(true);
            }
            meta.setKeepAlive(true);
            m.setMeta(meta);
            m.setName(m.getCode());
        });
        return menu.stream().filter(fun -> Objects.equals(FunctionConst.FATHER_PID, fun.getPid())).peek(funS -> {
            List<SysFunction> childrenList = getChildrenList(funS, menu);
            if (ObjectUtil.isNull(childrenList) || childrenList.size() == 0) {
                funS.setRedirect(null);
            } else {
                funS.setRedirect(funS.getComponent());
                funS.setComponent(null);
            }
            funS.setChildren(childrenList);
        }).sorted(Comparator.comparingInt(MenuVO::getSort)).collect(Collectors.toList());
    }

    @Override
    public Map<String, List<String>> getButtonByUserId(String userId) {
        List<SysFunction> sysFunctions = this.baseMapper.getButtonByUserId(userId);

        Map<String, List<String>> buttonMap = new HashMap<>();
        sysFunctions.stream().collect(Collectors.groupingBy(SysFunction::getPid)).forEach((k, v) -> {
            SysFunction fatherFunction = this.getById(k);
            if (ObjectUtil.isNotNull(fatherFunction)) {
                buttonMap.put(fatherFunction.getCode(), v.stream().map(SysFunction::getCode).collect(Collectors.toList()));
            }
        });
        return buttonMap;
    }

    private <T extends SysFunction> List<SysFunction> getChildrenList(T currMenu, List<? extends SysFunction> categories) {
        return categories.stream().filter(o -> Objects.equals(o.getPid(), currMenu.getId())).peek(o -> o.setChildren(getChildrenList(o, categories))).sorted(Comparator.comparingInt(SysFunction::getSort)).collect(Collectors.toList());
    }

    /**
     * 校验参数，
     * 1.检查是否存在相同名称的菜单
     * 名称 && 路径做唯一判断
     */
    private void checkFunctionParam(SysFunctionParam functionParam, boolean isExcludeSelf) {
        if (functionParam.getType().equals(FunctionConst.TYPE_MENU)) {
            if (StrUtil.isBlank(functionParam.getComponent())) {
                throw new BusinessException(UserResponseEnum.COMPONENT_NOT_BLANK);
            }
            if (StrUtil.isBlank(functionParam.getPath()) || !Pattern.matches(PatternRegex.FUNCTION_PATH_REGEX, functionParam.getPath())) {
                throw new BusinessException(UserResponseEnum.FUNCTION_PATH_FORMAT_ERROR);
            }
            if(StrUtil.isBlank(functionParam.getComponent()) || !Pattern.matches(PatternRegex.FUNCTION_COMPONENT_REGEX, functionParam.getComponent())){
                throw new BusinessException(UserResponseEnum.FUNCTION_COMPONENT_FORMAT_ERROR);
            }
        }
        LambdaQueryWrapper<SysFunction> functionLambdaQueryWrapper = new LambdaQueryWrapper<>();
        // 同一个pid下，名称、编码、路径、组件地址不能重复
        functionLambdaQueryWrapper
                .eq(SysFunction::getPid, functionParam.getPid())
                .eq(SysFunction::getState, DataStateEnum.ENABLE.getCode())
                .and(obj -> obj.eq(SysFunction::getName, functionParam.getName()).or()
                        .eq(SysFunction::getCode, functionParam.getCode()).or()
                        .eq(StrUtil.isNotBlank(functionParam.getPath()), SysFunction::getPath, functionParam.getPath()).or()
                        .eq(StrUtil.isNotBlank(functionParam.getComponent()), SysFunction::getComponent, functionParam.getComponent())
                );
        //更新的时候，需排除当前记录
        if (isExcludeSelf) {
            if (functionParam instanceof SysFunctionParam.UpdateParam) {
                functionLambdaQueryWrapper.ne(SysFunction::getId, ((SysFunctionParam.UpdateParam) functionParam).getId());
            }
        }
        int countByAccount = this.count(functionLambdaQueryWrapper);
        //大于等于1个则表示重复
        if (countByAccount >= 1) {
            throw new BusinessException(UserResponseEnum.EXISTS_SAME_MENU_CHILDREN);
        }
    }

    private List<SysFunction> filterTreeByName(List<SysFunction> tree, String keyword) {
        if (CollectionUtils.isEmpty(tree) || !StrUtil.isNotBlank(keyword)) {
            return tree;
        }
        filter(tree, keyword);
        return tree;
    }

    private void filter(List<SysFunction> list, String keyword) {
        for (int i = list.size() - 1; i >= 0; i--) {
            SysFunction function = list.get(i);
            List<SysFunction> children = function.getChildren();
            if (!function.getName().contains(keyword)) {
                if (!CollectionUtils.isEmpty(children)) {
                    filter(children, keyword);
                }
                if (CollectionUtils.isEmpty(function.getChildren())) {
                    list.remove(i);
                }
            }
//            else {
//                if (!CollectionUtils.isEmpty(children)) {
//                    filter(children, keyword);
//                }
//            }
        }
    }
}