package com.qmth.paper.library.common.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.reflect.TypeToken;
import com.google.gson.Gson;
import com.qmth.paper.library.common.bean.dto.MenuDto;
import com.qmth.paper.library.common.bean.dto.MenuPrivilegeDto;
import com.qmth.paper.library.common.bean.dto.PrivilegeDto;
import com.qmth.paper.library.common.contant.SystemConstant;
import com.qmth.paper.library.common.entity.SysPrivilege;
import com.qmth.paper.library.common.entity.SysRolePrivilege;
import com.qmth.paper.library.common.entity.TSchoolPrivilege;
import com.qmth.paper.library.common.enums.ExceptionResultEnum;
import com.qmth.paper.library.common.enums.PrivilegeEnum;
import com.qmth.paper.library.common.enums.PrivilegePropertyEnum;
import com.qmth.paper.library.common.mapper.SysPrivilegeMapper;
import com.qmth.paper.library.common.service.SysPrivilegeService;
import com.qmth.paper.library.common.service.SysRolePrivilegeService;
import com.qmth.paper.library.common.service.TSchoolPrivilegeService;
import com.qmth.paper.library.common.util.JacksonUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.LinkedMultiValueMap;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 菜单权限表 服务实现类
 * </p>
 *
 * @author xf
 * @since 2021-03-23
 */
@Service
public class SysPrivilegeServiceImpl extends ServiceImpl<SysPrivilegeMapper, SysPrivilege> implements SysPrivilegeService {
    private final static Logger log = LoggerFactory.getLogger(SysPrivilegeServiceImpl.class);

    @Autowired
    private SysRolePrivilegeService sysRolePrivilegeService;

    @Resource
    SysPrivilegeMapper sysPrivilegeMapper;

    @Resource
    TSchoolPrivilegeService tSchoolPrivilegeService;

    /**
     * 获取所有权限
     *
     * @return
     */
    @Override
    public List<PrivilegeDto> listPrivilegeTreeNew() {
        Long schoolId = SystemConstant.getHeadOrUserSchoolId();
        List<TSchoolPrivilege> tSchoolPrivilegeList = tSchoolPrivilegeService.findBySchoolId(schoolId);
        Set<Long> customPrivilegeIds = null;
        if (Objects.nonNull(tSchoolPrivilegeList) && !tSchoolPrivilegeList.isEmpty()) {
            customPrivilegeIds = tSchoolPrivilegeList.stream().map(TSchoolPrivilege::getPrivilegeId).collect(Collectors.toSet());
        }
        QueryWrapper<SysPrivilege> sysPrivilegeQueryWrapper = new QueryWrapper<>();
        sysPrivilegeQueryWrapper.lambda()
                .ne(SysPrivilege::getType, PrivilegeEnum.URL)
                .eq(SysPrivilege::getEnable, true)
                .eq(SysPrivilege::getDefaultAuth, true);
        List<SysPrivilege> sysPrivilegeList = this.list(sysPrivilegeQueryWrapper);
        if (Objects.nonNull(customPrivilegeIds) && !customPrivilegeIds.isEmpty()) {
            sysPrivilegeQueryWrapper = new QueryWrapper<>();
            sysPrivilegeQueryWrapper.lambda().in(SysPrivilege::getId, customPrivilegeIds);
            List<SysPrivilege> sysPrivileges = this.list(sysPrivilegeQueryWrapper);
            sysPrivilegeList.addAll(sysPrivileges);
        }
        List<PrivilegeDto> list = (List<PrivilegeDto>) this.getMenuTreeCommon(sysPrivilegeList);
        Collections.sort(list);
        return list;
    }

    @Override
    public Long savePrivilege(SysPrivilege privilege) {
        QueryWrapper<SysPrivilege> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(SysPrivilege::getParentId, privilege.getParentId()).eq(SysPrivilege::getName, privilege.getName());
        SysPrivilege sysPrivilege = this.getOne(queryWrapper);

        // 新增
        if (privilege.getId() == null) {
            if (sysPrivilege != null) {
                throw ExceptionResultEnum.ERROR.exception("名称已存在");
            }
            privilege.setId(SystemConstant.getDbUuid());
//            privilege.setCreateTime(System.currentTimeMillis());
            this.save(privilege);
        }
        // 修改
        else {
            if (sysPrivilege != null && !Objects.equals(privilege.getId(), sysPrivilege.getId())) {
                throw ExceptionResultEnum.ERROR.exception("名称已存在");
            }
            this.updateById(privilege);
        }
        return privilege.getId();
    }

    @Override
    public List<String> getRolePrivileges(Long roleId) {
        QueryWrapper<SysRolePrivilege> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(SysRolePrivilege::getRoleId, roleId);
        List<SysRolePrivilege> sysRolePrivileges = sysRolePrivilegeService.list(queryWrapper);
        List<String> list = null;
        if (sysRolePrivileges != null && sysRolePrivileges.size() > 0) {
            list = sysRolePrivileges.stream().map(m -> String.valueOf(m.getPrivilegeId())).collect(Collectors.toList());
        }
        return list;
    }

    @Override
    public List<PrivilegeDto> listPrivilegeTreeAuth() {
        QueryWrapper<SysPrivilege> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(SysPrivilege::getProperty, PrivilegePropertyEnum.AUTH.name())
                .or().isNull(SysPrivilege::getProperty)
                .orderByAsc(SysPrivilege::getParentId, SysPrivilege::getSequence);
        List<SysPrivilege> orgList = this.list(queryWrapper);
        Map<Long, PrivilegeDto> map = new LinkedHashMap<>();
        for (SysPrivilege privilege : orgList) {
            PrivilegeDto privilegeDto = new PrivilegeDto();
            privilegeDto.setId(privilege.getId());
            privilegeDto.setName(privilege.getName());
            privilegeDto.setUrl(privilege.getUrl());
            privilegeDto.setType(privilege.getType());
            privilegeDto.setParentId(privilege.getParentId());
            privilegeDto.setSequence(privilege.getSequence());
            map.put(privilege.getId(), privilegeDto);
        }

        Iterator<Long> iterator = map.keySet().iterator();
        Set<Long> deleteKeys = new HashSet<>();
        while (iterator.hasNext()) {
            Long parentId = iterator.next();
            if (map.get(parentId).getParentId() != null) {
                map.get(map.get(parentId).getParentId()).getChildren().add(map.get(parentId));
                deleteKeys.add(parentId);
            }
        }
        for (Long key : deleteKeys) {
            map.remove(key);
        }

        return new ArrayList<>(map.values());
    }

    @Override
    public List<SysPrivilege> getClientUrlByUserId(Long userId) {
        return this.baseMapper.getClientUrlByUserId(userId);
    }

    /**
     * 递归树查询(同级及以上)
     *
     * @param privilegeId
     * @return
     */
    @Override
    public List<SysPrivilege> findByConnectByParentId(Long privilegeId) {
        return sysPrivilegeMapper.findByConnectByParentId(privilegeId);
    }

    /**
     * 获取菜单树公用
     *
     * @param sysPrivilegeList
     * @return
     */
    @Override
    public Collection<?> getMenuTreeCommon(Collection<?> sysPrivilegeList) {
        LinkedMultiValueMap<Long, MenuDto> linkedMultiValueMap = new LinkedMultiValueMap<>();
        Map<Long, PrivilegeDto> map = new LinkedHashMap<>();
        Gson gson = new Gson();
        List<MenuDto> menuDtoList = gson.fromJson(JacksonUtil.parseJson(sysPrivilegeList), new TypeToken<List<MenuDto>>() {
        }.getType());
        for (MenuDto m : menuDtoList) {
            if (Objects.isNull(m.getParentId()) || m.getType() == PrivilegeEnum.MENU) {
                PrivilegeDto privilegeDto = new PrivilegeDto();
                privilegeDto.setId(m.getId());
                privilegeDto.setName(m.getName());
                privilegeDto.setUrl(m.getUrl());
                privilegeDto.setType(m.getType());
                privilegeDto.setParentId(m.getParentId());
                privilegeDto.setSequence(m.getSequence());
                map.put(m.getId(), privilegeDto);
            } else {
                if (!linkedMultiValueMap.containsKey(m.getParentId())) {
                    linkedMultiValueMap.add(m.getParentId(), m);
                } else {
                    List<MenuDto> menuDtos = linkedMultiValueMap.get(m.getParentId());
                    menuDtos.add(m);
                    linkedMultiValueMap.put(m.getParentId(), menuDtos);
                }
            }
        }

        Set<Long> deleteKeys = new HashSet<>();
        map.forEach((k, v) -> {
            List<MenuDto> menuDtos = linkedMultiValueMap.get(k);
            List<MenuDto> buttons = null, links = null, lists = null, conditions = null;
            if (Objects.nonNull(menuDtos)) {
                for (MenuDto menuDto : menuDtos) {
                    if (v.getId().longValue() == menuDto.getParentId().longValue() && menuDto.getType() == PrivilegeEnum.BUTTON) {
                        buttons = Objects.isNull(buttons) ? new ArrayList<>() : buttons;
                        buttons.add(gson.fromJson(gson.toJson(menuDto), MenuPrivilegeDto.class));
                        v.setButtons(buttons);
                    } else if (v.getId().longValue() == menuDto.getParentId().longValue() && menuDto.getType() == PrivilegeEnum.LINK) {
                        links = Objects.isNull(links) ? new ArrayList<>() : links;
                        links.add(gson.fromJson(gson.toJson(menuDto), MenuPrivilegeDto.class));
                        v.setLinks(links);
                    } else if (v.getId().longValue() == menuDto.getParentId().longValue() && menuDto.getType() == PrivilegeEnum.LIST) {
                        lists = Objects.isNull(lists) ? new ArrayList<>() : lists;
                        lists.add(gson.fromJson(gson.toJson(menuDto), MenuPrivilegeDto.class));
                        v.setLists(lists);
                    } else if (v.getId().longValue() == menuDto.getParentId().longValue() && menuDto.getType() == PrivilegeEnum.CONDITION) {
                        conditions = Objects.isNull(conditions) ? new ArrayList<>() : conditions;
                        conditions.add(gson.fromJson(gson.toJson(menuDto), MenuPrivilegeDto.class));
                        v.setConditions(conditions);
                    }
                }
            }
            if (v.getParentId() > 0 && v.getType() == PrivilegeEnum.MENU) {
                map.get(v.getParentId()).getChildren().add(v);
                deleteKeys.add(k);
            }
            if (Objects.nonNull(map.get(v.getParentId())) && !CollectionUtils.isEmpty(map.get(v.getParentId()).getChildren())) {
                Collections.sort(map.get(v.getParentId()).getChildren());
            }
            if (!CollectionUtils.isEmpty(v.getButtons())) {
                Collections.sort(v.getButtons());
            }
            if (!CollectionUtils.isEmpty(v.getLinks())) {
                Collections.sort(v.getLinks());
            }
            if (!CollectionUtils.isEmpty(v.getLists())) {
                Collections.sort(v.getLists());
            }
            if (!CollectionUtils.isEmpty(v.getConditions())) {
                Collections.sort(v.getConditions());
            }
        });
        for (Long key : deleteKeys) {
            map.remove(key);
        }
        return new ArrayList<>(map.values());
    }

    /**
     * 自定义菜单公用
     *
     * @return
     */
    @Override
    public List<PrivilegeDto> commonCustomList() {
        QueryWrapper<SysPrivilege> sysPrivilegeQueryWrapper = new QueryWrapper<>();
        sysPrivilegeQueryWrapper.lambda()
                .eq(SysPrivilege::getEnable, true)
                .eq(SysPrivilege::getDefaultAuth, false)
                .ne(SysPrivilege::getType, PrivilegeEnum.URL)
                .notIn(SysPrivilege::getId, SystemConstant.MENU_MANAGE);
        List<SysPrivilege> sysPrivilegeList = this.list(sysPrivilegeQueryWrapper);
        List<PrivilegeDto> privilegeDtoList = null;
        if (Objects.nonNull(sysPrivilegeList) && sysPrivilegeList.size() > 0) {
            Set<Long> privilegeIds = new HashSet<>();
            Map<Long, SysPrivilege> sysPrivilegeMap = new HashMap<>();
            for (SysPrivilege s : sysPrivilegeList) {
                if (!Objects.equals(s.getType(), PrivilegeEnum.MENU)) {
                    privilegeIds.add(s.getId());
                }
                sysPrivilegeMap.put(s.getId(), s);
            }
            for (Long l : privilegeIds) {
                List<SysPrivilege> sysPrivileges = this.findByConnectByParentId(l);
                for (SysPrivilege s : sysPrivileges) {
                    sysPrivilegeMap.put(s.getId(), s);
                }
            }
            privilegeDtoList = (List<PrivilegeDto>) this.getMenuTreeCommon(new ArrayList<>(sysPrivilegeMap.values()));
            Collections.sort(privilegeDtoList);
        }
        return privilegeDtoList;
    }

    @Override
    public boolean remove(Long id) {
        QueryWrapper<SysRolePrivilege> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(SysRolePrivilege::getPrivilegeId, id);
        List<SysRolePrivilege> sysRolePrivileges = sysRolePrivilegeService.list(queryWrapper);
        if (sysRolePrivileges != null && sysRolePrivileges.size() > 0) {
            throw ExceptionResultEnum.ERROR.exception("权限已绑定，不能删除");
        }
        return this.removeById(id);
    }
}
