package com.xy.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeNode;
import cn.hutool.core.lang.tree.TreeUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xy.dto.CommonIdDto;
import com.xy.dto.SysDeptDto;
import com.xy.dto.SysDeptListDto;
import com.xy.entity.*;
import com.xy.mapper.SysDeptMapper;
import com.xy.mapper.SysRoleMapper;
import com.xy.redis.RedisCache;
import com.xy.service.SysDeptRelationService;
import com.xy.service.SysDeptRoleService;
import com.xy.service.SysDeptService;
import com.xy.service.SysUserDeptService;
import com.xy.utils.LambdaUtils;
import com.xy.utils.R;
import com.xy.vo.SysDeptVo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;

import javax.validation.Valid;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.xy.utils.Beans.copy;

/***
 * 部门管理实现类
 * @author 谭斌
 * @date 2022/12/13 14:01
 */
@Api(tags = "部门管理")
@Service
@RequiredArgsConstructor
public class SysDeptServiceImpl extends ServiceImpl<SysDeptMapper, SysDept> implements SysDeptService {

    private final SysDeptRelationService sysDeptRelationService;
    private final SysDeptRoleService sysDeptRoleService;
    private final SysUserDeptService sysUserDeptService;
    private final SysRoleMapper sysRoleMapper;

    @ApiOperation(value = "新增、更新部门信息", notes = "新增、更新部门信息")
    @PostMapping("saveOrUpdate")
    @Transactional(rollbackFor = Exception.class)
    public R<Boolean> saveOrUpdate(@Valid @RequestBody SysDeptDto sysDeptDto) {
        Long id = sysDeptDto.getId();
        Long loginId = RedisCache.getLoginId(Long.class);
        if (loginId == null) {
            return R.fail("获取用户信息失败");
        }
        boolean isAdd = true;
        LambdaQueryWrapper<SysDept> lqw = new LambdaQueryWrapper<SysDept>()
                .eq(SysDept::getName, sysDeptDto.getCode())
                .eq(SysDept::getSysId, sysDeptDto.getSysId());
        if (id != null) {
            isAdd = false;
            lqw.ne(SysDept::getId, id);
        }
        long count = count(lqw);
        if (count > 0) {
            return R.fail("此部门已存在");
        }
        List<Long> roleIds = sysDeptDto.getRoleIds();
        SysDept sysDept = copy(SysDept.class, sysDeptDto).saveOrUpdate(loginId, sysDeptDto.getId());
        if (isAdd) {
            // 新建部门关系
            sysDeptRelationService.saveDeptRelation(sysDept);
            // 角色部门关系建立
            if (CollUtil.isNotEmpty(roleIds)) {
                sysDeptRoleService.saveDeptRole(sysDept.getId(), roleIds, sysDeptDto.getSysId());
            }
        } else {
            // 更新部门关系
            SysDeptRelation relation = new SysDeptRelation();
            relation.setAncestor(sysDept.getParentId());
            relation.setDescendant(sysDept.getId());
            sysDeptRelationService.updateDeptRelation(relation);
            // 角色部门关系更新
            if (CollUtil.isNotEmpty(roleIds)) {
                sysDeptRoleService.updateDeptRole(sysDept.getId(), roleIds, sysDeptDto.getSysId());
            }
            if (roleIds != null && roleIds.size() == 0) {
                // 部门角色清理
                sysDeptRoleService.removeDeptRoleById(sysDept.getId());

            }
        }
        return R.ok(saveOrUpdate(sysDept));
    }

    @ApiOperation(value = "根据ID获取部门信息", notes = "根据ID获取部门信息")
    @GetMapping("getById")
    public R<SysDeptVo> getById(@Valid @RequestParam("id") Long id) {
        SysDept sysDept = super.getById(id);
        SysDeptVo sysRoleVo = copy(SysDeptVo.class, sysDept);
        return R.ok(sysRoleVo);
    }

    @ApiOperation(value = "部门树", notes = "部门树")
    @PostMapping("tree/list")
    public R<List<Tree<Long>>> deptTreeList(@Valid @RequestBody SysDeptListDto sysDeptListDto) {
        Long sysId = sysDeptListDto.getSysId();
        LambdaQueryWrapper<SysDept> lambdaQueryWrapper = Wrappers.<SysDept>lambdaQuery()
                .eq(SysDept::getSysId, sysDeptListDto.getSysId());
        RedisCache.getDataAuth(sysId, lambdaQueryWrapper, SysDept::getCreateUser);
        List<Tree<Long>> deptTree = getDeptTree(this.list(lambdaQueryWrapper), 0L);
        return R.ok(deptTree);
    }

    @ApiOperation(value = "删除部门", notes = "删除部门")
    @PostMapping("deleteById")
    @Transactional(rollbackFor = Exception.class)
    public R<Boolean> deleteById(@Valid @RequestBody CommonIdDto commonIdDto) {
        Long id = commonIdDto.getId();
        // 级联删除部门
        List<Long> idList = sysDeptRelationService
                .list(Wrappers.<SysDeptRelation>query().lambda().eq(SysDeptRelation::getAncestor, id)).stream()
                .map(SysDeptRelation::getDescendant).collect(Collectors.toList());

        if (CollUtil.isNotEmpty(idList)) {
            this.removeByIds(idList);
        }
        // 删除部门级联关系
        sysDeptRelationService.removeDeptRelationById(id);
        // 部门角色关系移除
        sysDeptRoleService.removeDeptRoleById(id);
        // 业务用户部门关系 sys_user_dept
        sysUserDeptService.remove(Wrappers.<SysUserDept>lambdaQuery().in(SysUserDept::getDeptId, idList));

        return R.ok(Boolean.TRUE);
    }

    /**
     * 获得部门树
     *
     * @param deptList 部门集合
     * @param parentId 父级id
     * @return
     */
    private List<Tree<Long>> getDeptTree(List<SysDept> deptList, Long parentId) {
        List<TreeNode<Long>> collect = deptList.stream().filter(dept -> !dept.getId().equals(dept.getParentId()))
                .sorted(Comparator.comparingInt(SysDept::getSortNum)).map(dept -> {
                    TreeNode<Long> treeNode = new TreeNode();
                    treeNode.setId(dept.getId());
                    treeNode.setParentId(dept.getParentId());
                    treeNode.setName(dept.getName());
                    treeNode.setWeight(dept.getSortNum());
                    // 扩展属性
                    Map<String, Object> extra = new HashMap<>();
                    extra.put(LambdaUtils.getProperty(SysDept::getCreateTime), dept.getCreateTime());
                    extra.put(LambdaUtils.getProperty(SysDept::getUpdateTime), dept.getUpdateTime());
                    extra.put(LambdaUtils.getProperty(SysDept::getCode), dept.getCode());
                    // 部门角色
                    List<SysDeptRole> sysDeptRoles = sysDeptRoleService.list(Wrappers.<SysDeptRole>lambdaQuery().in(SysDeptRole::getDeptId, dept.getId()));
                    if (CollUtil.isNotEmpty(sysDeptRoles)) {
                        List<Long> deptRoleIds = sysDeptRoles.stream().map(SysDeptRole::getRoleId).distinct().collect(Collectors.toList());
                        List<SysRole> sysRoles = sysRoleMapper.selectList(new LambdaQueryWrapper<SysRole>().in(SysRole::getId, deptRoleIds));
                        if (CollUtil.isNotEmpty(sysRoles)) {
                            List<String> deptRoleNames = sysRoles.stream().map(SysRole::getName).distinct().collect(Collectors.toList());
                            extra.put("deptRoleNames", StrUtil.join(",", deptRoleNames));
                        }
                    }
                    treeNode.setExtra(extra);
                    return treeNode;
                }).collect(Collectors.toList());

        return TreeUtil.build(collect, parentId);
    }


    @ApiOperation(value = "获取部门关联角色ID(回显显用)", notes = "获取部门关联角色ID(回显显用)")
    @PostMapping("getRefRoleIds")
    public R<List<Long>> getRefRoleIds(@Valid @RequestBody SysDeptDto.QueryRoleDTO dto) {
        Long sysId = dto.getSysId();
        List<SysDeptRole> list = sysDeptRoleService.list(
                Wrappers.<SysDeptRole>lambdaQuery().
                        eq(SysDeptRole::getDeptId, dto.getId()).
                        eq(sysId != null, SysDeptRole::getSysId, sysId));
        List<Long> ids = list.stream().map(SysDeptRole::getRoleId).collect(Collectors.toList());
        return R.ok(ids);
    }


}
