package com.agileboot.system.role.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.agileboot.common.core.enums.common.StatusEnum;
import com.agileboot.common.core.exception.BizException;
import com.agileboot.common.core.exception.error.ErrorCode;
import com.agileboot.common.mybatis.core.page.PageR;
import com.agileboot.system.menu.service.ISysMenuService;
import com.agileboot.system.role.dto.*;
import com.agileboot.system.role.entity.SysRole;
import com.agileboot.system.role.entity.SysRoleMenu;
import com.agileboot.system.role.mapper.SysRoleMapper;
import com.agileboot.system.role.service.ISysRoleMenuService;
import com.agileboot.system.role.service.ISysRoleService;
import com.agileboot.system.role.vo.RoleVO;
import com.agileboot.system.user.dto.UserInfo;
import com.agileboot.system.user.entity.SysUser;
import com.agileboot.system.user.service.ISysUserService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.base.Preconditions;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

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

@Service
@RequiredArgsConstructor
public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> implements ISysRoleService {

    private final ISysMenuService menuService;
    private final ISysRoleMenuService roleMenuService;
    private final ISysUserService userService;

    @Override
    public PageR<RoleVO> getRoleList(RoleQuery query) {
        Page<SysRole> page = this.page(query.toPage(), query.toQueryWrapper());
        IPage<RoleVO> convert = page.convert(RoleVO::new);
        return new PageR<>(convert);
    }

    @Override
    public RoleVO getRoleInfo(Long roleId) {
        SysRole role = this.getById(roleId);
        RoleVO roleVO = new RoleVO(role);
        List<Long> selectedDeptList = Arrays.stream(StringUtils.split(role.getDeptIdSet(), ","))
                .filter(StringUtils::isNotEmpty).map(Long::parseLong).collect(Collectors.toList());
        roleVO.setSelectedDeptList(selectedDeptList);
        roleVO.setSelectedMenuList(menuService.getMenuIdsByRoleId(roleId));
        return roleVO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addRole(AddRoleDTO addDTO) {
        SysRole entity = new SysRole();
        BeanUtil.copyProperties(addDTO, entity);
        this.checkRoleNameUnique(null, addDTO.getRoleName());
        this.checkRoleKeyUnique(null, addDTO.getRoleKey());

        super.save(entity);
        roleMenuService.saveMenus(entity.getRoleId(), addDTO.getMenuIds());
    }

    @Override
    public void deleteRole(List<Long> roleIds) {
        if (roleIds == null) {
            return;
        }
        List<SysRole> list = super.list(Wrappers.lambdaQuery(SysRole.class).in(SysRole::getRoleId, roleIds));
        List<Long> collect = list.stream().map(SysRole::getRoleId).toList();
        roleIds.removeAll(collect);
        if (!roleIds.isEmpty()) {
            throw new BizException(ErrorCode.Business.COMMON_OBJECT_NOT_FOUND, roleIds.get(0), "角色");
        }
        List<SysUser> assignedUser = userService.assignedToUsersByRoleIds(collect);
        Map<Long, String> roleIdNameMap = list.stream().collect(Collectors.toMap(SysRole::getRoleId, SysRole::getRoleName));
        for (SysUser sysUser : assignedUser) {
            throw new BizException(ErrorCode.Business.ROLE_ALREADY_ASSIGN_TO_USER, roleIdNameMap.get(sysUser.getRoleId()));
        }

        // 清空之前的角色菜单关联
        roleMenuService.cleanOldMenus(collect);
        super.baseMapper.deleteAbsoluteByIds(collect);
    }

//    @Override
//    @Transactional(rollbackFor = Exception.class)
//    public void updateRole(UpdateRoleDTO updateDTO) {
//        LambdaQueryWrapper<SysRole> roleLqw = new LambdaQueryWrapper<>();
//        roleLqw.eq(SysRole::getRoleId, updateDTO.getRoleId());
//        roleLqw.eq(SysRole::getStatus, StatusEnum.ENABLE.getValue());
//        SysRole sysRole = getOne(roleLqw);
//        Preconditions.checkArgument(Objects.nonNull(sysRole), "角色不存在");
//        LambdaQueryWrapper<SysRoleMenu> roleMenuLqw = new LambdaQueryWrapper<>();
//        roleMenuLqw.eq(SysRoleMenu::getRoleId, updateDTO.getRoleId());
//        //角色对应的菜单
//        LambdaUpdateWrapper<SysRoleMenu> roleMenuLuw = new LambdaUpdateWrapper<>();
//        roleMenuLuw.eq(SysRoleMenu::getRoleId, updateDTO.getRoleId());
//        roleMenuService.cleanOldMenus(List.of(updateDTO.getRoleId()));//清空之前的角色菜单关联
//        List<Long> menuIds = updateDTO.getMenuIds();//新的菜单列表
//        if (!CollectionUtils.isEmpty(menuIds)) {
//            roleMenuService.saveMenus(updateDTO.getRoleId(), menuIds);
//        }
//        BeanUtils.copyProperties(updateDTO, sysRole, "roleId");
//        updateById(sysRole);
//
//    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateRole(UpdateRoleDTO updateDTO) {
        SysRole byId = super.getById(updateDTO.getRoleId());
        if (byId == null) {
            throw new BizException(ErrorCode.Business.COMMON_OBJECT_NOT_FOUND, updateDTO.getRoleId(), "角色");
        }
//        List<SysRoleMenu> roleMenus = roleMenuService.listById(updateDTO.getRoleId());
        List<Long> menuIds = updateDTO.getMenuIds();
//        List<Long> menuIds = roleMenus.stream().map(SysRoleMenu::getMenuId).toList();

//        List<Long> deptIds = StrUtil.split(byId.getDeptIdSet(), ",").stream().map(Convert::toLong).toList();

        BeanUtils.copyProperties(updateDTO, byId, "roleId");
        this.checkRoleKeyUnique(byId.getRoleId(), byId.getRoleKey());
        this.checkRoleNameUnique(byId.getRoleId(), byId.getRoleName());
        // 清空之前的角色菜单关联
        roleMenuService.cleanOldMenus(List.of(byId.getRoleId()));
        boolean b = roleMenuService.saveMenus(byId.getRoleId(), menuIds);
        if (!b) {
            throw new BizException("修改角色失败");
        }
        super.updateById(byId);
    }

    @Override
    public void updateDataScope(UpdateDataScopeDTO updateDTO) {
        SysRole byId = super.getById(updateDTO.getRoleId());
        if (byId == null) {
            throw new BizException(ErrorCode.Business.COMMON_OBJECT_NOT_FOUND, updateDTO.getRoleId(), "角色");
        }

        byId.setDeptIdSet(this.generateDeptIdSet(updateDTO.getDeptIds()));
        byId.setDataScope(updateDTO.getDataScope());
        super.updateById(byId);
    }

    @Override
    public void updateStatus(UpdateStatusDTO updateDTO) {
        SysRole byId = super.getById(updateDTO.getRoleId());
        if (byId == null) {
            throw new BizException(ErrorCode.Business.COMMON_OBJECT_NOT_FOUND, updateDTO.getRoleId(), "角色");
        }

        byId.setStatus(updateDTO.getStatus());
        super.updateById(byId);
    }

    @Override
    public PageR<UserInfo> getAllocatedUserList(AllocatedRoleQuery query) {
        Page<SysUser> page = userService.getUserListByRole(query);
        IPage<UserInfo> convert = page.convert(UserInfo::new);
        return new PageR<>(convert);
    }

    @Override
    public PageR<UserInfo> getUnallocatedUserList(UnallocatedRoleQuery query) {
        Page<SysUser> page = userService.getUserListByRole(query);
        IPage<UserInfo> convert = page.convert(UserInfo::new);
        return new PageR<>(convert);
    }

    @Override
    public void deleteRoleOfUserByUserIds(List<Long> userIds) {
        if (CollectionUtils.isEmpty(userIds)) {
            return;
        }
        userService.deleteBatchRoleId(userIds);
    }

    @Override
    public void addRoleOfUserByByUserIds(Long roleId, List<Long> userIds) {
        if (CollectionUtils.isEmpty(userIds)) {
            return;
        }
        SysRole byId = super.getById(roleId);
        if (byId == null) {
            throw new BizException(ErrorCode.Business.COMMON_OBJECT_NOT_FOUND, roleId, "角色");
        }
        if (StatusEnum.DISABLE.getValue().equals(byId.getStatus())) {
            throw new BizException(ErrorCode.Business.ROLE_IS_NOT_AVAILABLE, byId.getRoleName());
        }

        userService.updateBatchRoleId(roleId, userIds);
    }


    //------------------------------
    private void checkRoleNameUnique(Long roleId, String roleName) {
        LambdaQueryWrapper<SysRole> queryWrapper = Wrappers.lambdaQuery(SysRole.class)
                .ne(roleId != null, SysRole::getRoleId, roleId)
                .eq(SysRole::getRoleName, roleName);
        if (super.exists(queryWrapper)) {
            throw new BizException(ErrorCode.Business.ROLE_NAME_IS_NOT_UNIQUE, roleName);
        }
    }

    private void checkRoleKeyUnique(Long roleId, String roleKey) {
        LambdaQueryWrapper<SysRole> queryWrapper = Wrappers.lambdaQuery(SysRole.class)
                .ne(roleId != null, SysRole::getRoleId, roleId)
                .eq(SysRole::getRoleKey, roleKey);
        if (super.exists(queryWrapper)) {
            throw new BizException(ErrorCode.Business.ROLE_KEY_IS_NOT_UNIQUE, roleKey);
        }
    }

    private String generateDeptIdSet(List<Long> deptIds) {
        if (deptIds == null) {
            return "";
        }

        if (deptIds.size() > new HashSet<>(deptIds).size()) {
            throw new BizException(ErrorCode.Business.ROLE_DATA_SCOPE_DUPLICATED_DEPT);
        }

        return StrUtil.join(",", deptIds);
    }

}
