package cn.reghao.devops.web.admin.account.service.impl;

import cn.reghao.devops.web.admin.account.db.repository.RoleRepository;
import cn.reghao.devops.web.admin.account.db.repository.UserRepository;
import cn.reghao.devops.web.admin.account.model.dto.RoleDto;
import cn.reghao.devops.web.admin.account.model.po.Menu;
import cn.reghao.devops.web.admin.account.model.po.Role;
import cn.reghao.devops.web.admin.account.model.po.User;
import cn.reghao.devops.web.admin.account.model.po.UserAuthority;
import cn.reghao.devops.web.admin.account.model.vo.RoleVO;
import cn.reghao.devops.web.admin.account.service.RoleService;
import cn.reghao.jutil.jdk.result.Result;
import cn.reghao.jutil.jdk.result.ResultStatus;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;

import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.SetJoin;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author reghao
 * @date 2020-06-19 16:36:53
 */
@Slf4j
@Service
public class RoleServiceImpl implements RoleService {
    private final RoleRepository roleRepository;
    private final UserRepository userRepository;

    public RoleServiceImpl(RoleRepository roleRepository, UserRepository userRepository) {
        this.roleRepository = roleRepository;
        this.userRepository = userRepository;
    }

    @Override
    public void addOrUpdate(RoleDto roleDto) {
        String name = String.format("ROLE_%s", roleDto.getName().toUpperCase(Locale.ROOT));
        Role role = roleRepository.findByName(name);
        if (role != null) {
            role.setDescription(roleDto.getDescription());
        } else {
            role = new Role(name, roleDto.getDescription());
        }

        roleRepository.save(role);
    }

    @Override
    public Result deleteRole(Integer roleId) {
        if (roleId == 1) {
            return Result.result(ResultStatus.FAIL, String.format("不能删除系统默认 Role"));
        }

        Role role = roleRepository.getOne(roleId);
        List<User> users = getRoleUsers(roleId);
        if (!users.isEmpty()) {
            return Result.result(ResultStatus.FAIL, String.format("还有用户分配有本角色"));
        }

        role.getMenus().clear();
        roleRepository.delete(role);
        return Result.success();
    }

    @Override
    public void setRoleMenus(Integer roleId, Set<Menu> menus) {
        Role role = roleRepository.getOne(roleId);
        role.setMenus(menus);
        roleRepository.save(role);
    }

    @Override
    public Role getByName(String name) {
        return roleRepository.findByName(name);
    }

    @Override
    public Page<RoleVO> getByPage(PageRequest pageRequest, String name) {
        Page<Role> rolePage;
        if (name != null) {
            Specification<Role> specification = (root, query, cb) -> {
                String likeQuery = String.format("%%%s%%", name);
                Predicate predicate = cb.like(root.get("name"), likeQuery);
                return cb.and(predicate);
            };

            pageRequest = PageRequest.of(0, 100);
            rolePage = roleRepository.findAll(specification, pageRequest);
        } else {
            rolePage = roleRepository.findAll(pageRequest);
        }

        List<RoleVO> list = rolePage.getContent().stream().map(RoleVO::new).collect(Collectors.toList());
        return new PageImpl<>(list, pageRequest, rolePage.getTotalElements());
    }

    @Override
    public RoleVO getRoleVOById(Integer roleId) {
        Role role = roleRepository.findById(roleId).orElse(null);
        return role != null ? new RoleVO(role) : null;
    }

    @Override
    public Set<RoleVO> getAllRoles() {
        PageRequest pageRequest = PageRequest.of(0, 100);
        return roleRepository.findAll(pageRequest).stream().map(RoleVO::new).collect(Collectors.toSet());
    }

    /**
     * 获取拥有角色的用户
     * SQL 语句
     * select u.*
     * from role r
     * inner join user_role ur
     * inner join `user` u
     * on r.title=ur.role and ur.user_id=u.id and r.title='ROLE_FRONTEND'
     *
     * @param
     * @return
     * @date 2023-03-02 17:54:48
     */
    public List<User> getRoleUsers(Integer roleId) {
        Role role = roleRepository.findById(roleId).orElse(null);
        if (role == null) {
            return Collections.emptyList();
        }

        String name = role.getName();
        Specification<User> specification = ((root, query, cb) -> {
            SetJoin<User, String> setJoin = root.joinSet("role");
            // User 中 Set 的元素是原始类型
            Predicate predicate = setJoin.as(String.class).in(name);
            // User 中的 Set 的元素是某个对象, fieldName 表示对象的某个字段名
            //Predicate predicate = cb.equal(setJoin.get("fieldName"), title);
            return cb.and(predicate);
        });

        PageRequest pageRequest = PageRequest.of(0, 100);
        Page<User> page = userRepository.findAll(specification, pageRequest);
        return page.getContent();
    }

    @Override
    public Set<RoleVO> getUserRoles(Integer userId) {
        User user = userRepository.findById(userId).orElse(null);
        if (user == null) {
            return Collections.emptySet();
        }

        Set<UserAuthority> set = user.getAuthorities().stream()
                .map(grantedAuthority -> new UserAuthority(grantedAuthority.getAuthority()))
                .collect(Collectors.toSet());

        List<String> roles = set.stream()
                .map(UserAuthority::getAuthority)
                .collect(Collectors.toList());
        Specification<Role> spec = ((root, query, criteriaBuilder) -> root.get("name").in(roles));
        return roleRepository.findAll(spec).stream().map(RoleVO::new).collect(Collectors.toSet());
    }
}
