package com.gitee.pifeng.monitoring.ui.business.web.service.impl;

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.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gitee.pifeng.monitoring.common.exception.NotFoundUserException;
import com.gitee.pifeng.monitoring.ui.business.web.dao.IMonitorRoleDao;
import com.gitee.pifeng.monitoring.ui.business.web.dao.IMonitorUserDao;
import com.gitee.pifeng.monitoring.ui.business.web.entity.MonitorRole;
import com.gitee.pifeng.monitoring.ui.business.web.entity.MonitorUser;
import com.gitee.pifeng.monitoring.ui.business.web.realm.MonitorUserRealm;
import com.gitee.pifeng.monitoring.ui.business.web.service.IMonitorUserService;
import com.gitee.pifeng.monitoring.ui.business.web.vo.LayUiAdminResultVo;
import com.gitee.pifeng.monitoring.ui.business.web.vo.MonitorUserVo;
import com.gitee.pifeng.monitoring.ui.constant.WebResponseConstants;
import com.gitee.pifeng.monitoring.ui.util.SpringSecurityUtils;
import com.google.common.collect.Lists;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.cas.authentication.CasAssertionAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedCredentialsNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * <p>
 * 监控用户服务实现类
 * </p>
 *
 * @author 皮锋
 * @custom.date 2020/7/1 17:39
 */
@Service
public class MonitorUserServiceImpl extends ServiceImpl<IMonitorUserDao, MonitorUser> implements IMonitorUserService {

    /**
     * 监控用户数据访问对象
     */
    @Autowired
    private IMonitorUserDao monitorUserDao;

    /**
     * 监控用户角色数据访问对象
     */
    @Autowired
    private IMonitorRoleDao monitorRoleDao;

    /**
     * session注册信息
     */
    @Autowired
    private SessionRegistry sessionRegistry;

    /**
     * <p>
     * 获取用户权限信息
     * </p>
     *
     * @param monitorUser 监控用户
     * @return 用户权限信息
     * @author 皮锋
     * @custom.date 2021/10/9 9:25
     */
    @Override
    public List<GrantedAuthority> getGrantedAuthorities(MonitorUser monitorUser) {
        // 根据角色ID在数据库中查询角色
        LambdaQueryWrapper<MonitorRole> roleQueryWrapper = new LambdaQueryWrapper<>();
        roleQueryWrapper.eq(MonitorRole::getId, monitorUser.getRoleId());
        List<MonitorRole> monitorRoles = this.monitorRoleDao.selectList(roleQueryWrapper);
        // 设置授权信息
        List<GrantedAuthority> grantedAuthorityList = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(monitorRoles)) {
            for (MonitorRole monitorRole : monitorRoles) {
                grantedAuthorityList.add(new SimpleGrantedAuthority(monitorRole.getRoleName()));
            }
        }
        return grantedAuthorityList;
    }

    /**
     * <p>
     * 根据账号获取用户
     * </p>
     *
     * @param account 账号
     * @return {@link UserDetails}
     * @author 皮锋
     * @custom.date 2020/7/5 14:05
     */
    @Override
    public UserDetails loadUserByUsername(String account) {
        // 根据username在数据库中查询用户
        LambdaQueryWrapper<MonitorUser> userQueryWrapper = new LambdaQueryWrapper<>();
        userQueryWrapper.eq(MonitorUser::getAccount, account);
        MonitorUser monitorUser = this.monitorUserDao.selectOne(userQueryWrapper);
        // 用户为空
        if (monitorUser == null) {
            throw new UsernameNotFoundException("用户不存在！");
        }
        // 获取用户权限信息
        List<GrantedAuthority> grantedAuthorityList = this.getGrantedAuthorities(monitorUser);
        return new MonitorUserRealm(monitorUser.getId(), monitorUser.getUsername(), account, monitorUser.getPassword(),
                monitorUser.getRoleId(), monitorUser.getRegisterTime(), monitorUser.getUpdateTime(), monitorUser.getEmail(),
                monitorUser.getRemarks(), grantedAuthorityList);
    }

    /**
     * <p>
     * 根据cas认证token获取用户
     * </p>
     *
     * @param token {@link CasAssertionAuthenticationToken}
     * @return {@link UserDetails}
     * @author 皮锋
     * @custom.date 2022/3/14 10:53
     */
    @Override
    public UserDetails loadUserDetails(CasAssertionAuthenticationToken token) throws UsernameNotFoundException {
        String account = token.getName();
        UserDetails userDetails;
        try {
            userDetails = this.loadUserByUsername(account);
        } catch (UsernameNotFoundException e) {
            // 第三方认证没找到用户，用默认用户 guest 登录
            userDetails = this.loadUserByUsername("guest");
        }
        return userDetails;
    }

    /**
     * <p>
     * 根据条件获取监控用户域
     * </p>
     *
     * @param ids 用户主键ID
     * @return {@link MonitorUserRealm}
     * @throws NotFoundUserException 找不到用户异常
     * @author 皮锋
     * @custom.date 2021/10/8 17:36
     */
    @Override
    public List<MonitorUserRealm> getMonitorUserRealms(List<Long> ids) throws NotFoundUserException {
        LambdaQueryWrapper<MonitorUser> userQueryWrapper = new LambdaQueryWrapper<>();
        if (CollectionUtils.isNotEmpty(ids)) {
            userQueryWrapper.in(MonitorUser::getId, ids);
        }
        List<MonitorUser> monitorUsers = this.monitorUserDao.selectList(userQueryWrapper);
        // 用户为空
        if (CollectionUtils.isEmpty(monitorUsers)) {
            throw new NotFoundUserException("用户不存在！");
        }
        List<MonitorUserRealm> monitorUserRealms = Lists.newArrayList();
        for (MonitorUser monitorUser : monitorUsers) {
            // 获取用户权限信息
            List<GrantedAuthority> grantedAuthorityList = this.getGrantedAuthorities(monitorUser);
            MonitorUserRealm monitorUserRealm = new MonitorUserRealm(monitorUser.getId(), monitorUser.getUsername(),
                    monitorUser.getAccount(), monitorUser.getPassword(), monitorUser.getRoleId(),
                    monitorUser.getRegisterTime(), monitorUser.getUpdateTime(), monitorUser.getEmail(),
                    monitorUser.getRemarks(), grantedAuthorityList);
            // 添加到list
            monitorUserRealms.add(monitorUserRealm);
        }
        return monitorUserRealms;
    }

    /**
     * <p>
     * 修改密码
     * </p>
     *
     * @param oldPassword 原始密码
     * @param password    新密码
     * @return LayUiAdmin响应对象：如果原始密码校验失败，LayUiAdminResultVo.data="verifyFail"；
     * 如果修改密码成功，LayUiAdminResultVo.data="success"；
     * 否则LayUiAdminResultVo.data="fail"。
     * @author 皮锋
     * @custom.date 2020/7/11 15:27
     */
    @Override
    public LayUiAdminResultVo updatePassword(String oldPassword, String password) {
        // 一.解密密码
        oldPassword = new String(Base64.getDecoder().decode(oldPassword), StandardCharsets.UTF_8);
        password = new String(Base64.getDecoder().decode(password), StandardCharsets.UTF_8);
        // 二.校验密码是否正确
        MonitorUserRealm monitorUserRealm = SpringSecurityUtils.getCurrentMonitorUserRealm();
        Long userId = Objects.requireNonNull(monitorUserRealm).getId();
        // 查询数据库
        MonitorUser monitorUser = this.monitorUserDao.selectById(userId);
        String dbPassword = monitorUser.getPassword();
        // 判断输入的原密码和加密后的密码是否一致
        BCryptPasswordEncoder bc = new BCryptPasswordEncoder();
        boolean verify = bc.matches(oldPassword, dbPassword);
        // 密码不正确
        if (!verify) {
            return LayUiAdminResultVo.ok(WebResponseConstants.VERIFY_FAIL);
        }
        // 三.修改密码
        // 加密密码
        String enPassword = bc.encode(password);
        // 更新数据库
        LambdaUpdateWrapper<MonitorUser> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        lambdaUpdateWrapper.eq(MonitorUser::getId, userId).set(MonitorUser::getPassword, enPassword);
        int result = this.monitorUserDao.update(null, lambdaUpdateWrapper);
        if (result == 1) {
            MonitorUserRealm realm = (MonitorUserRealm) this.loadUserByUsername(monitorUser.getAccount());
            // 更新springsecurity中当前用户
            SpringSecurityUtils.updateCurrentMonitorUserRealm(realm);
            return LayUiAdminResultVo.ok(WebResponseConstants.SUCCESS);
        }
        return LayUiAdminResultVo.ok(WebResponseConstants.FAIL);
    }

    /**
     * <p>
     * 修改当前用户信息
     * </p>
     *
     * @param monitorUserVo 用户信息
     * @return LayUiAdmin响应对象：如果修改用户信息成功，LayUiAdminResultVo.data="success"，否则LayUiAdminResultVo.data="fail"。
     * @author 皮锋
     * @custom.date 2020/7/13 18:58
     */
    @Override
    public LayUiAdminResultVo updateUser(MonitorUserVo monitorUserVo) {
        MonitorUser monitorUser = monitorUserVo.convertTo();
        // 设置更新时间
        monitorUser.setUpdateTime(new Date());
        // 把账号和角色设置成null，让这两个字段不更新
        monitorUser.setAccount(null);
        monitorUser.setRoleId(null);
        // 获取当前操作用户的信息
        MonitorUserRealm currentMonitorUserRealm = SpringSecurityUtils.getCurrentMonitorUserRealm();
        Long currentMonitorUserRealmId = Objects.requireNonNull(currentMonitorUserRealm).getId();
        String currentMonitorUserRealmAccount = Objects.requireNonNull(currentMonitorUserRealm).getUsername();
        Long currentMonitorUserRealmUsrnameRoleId = Objects.requireNonNull(currentMonitorUserRealm).getRoleId();
        // 这种情况下，可能是黑客通过修改用户信息进行强行提权，获取超级管理员角色的登录权限
        if (!Objects.equals(currentMonitorUserRealmId, monitorUserVo.getId()) ||
                !StringUtils.equals(currentMonitorUserRealmAccount, monitorUserVo.getAccount()) ||
                !Objects.equals(currentMonitorUserRealmUsrnameRoleId, monitorUserVo.getRoleId())) {
            throw new PreAuthenticatedCredentialsNotFoundException("身份信息不匹配！");
        }
        int result = this.monitorUserDao.updateById(monitorUser);
        if (result == 1) {
            // 更新springsecurity中当前用户
            MonitorUserRealm realm = (MonitorUserRealm) this.loadUserByUsername(monitorUserVo.getAccount());
            SpringSecurityUtils.updateCurrentMonitorUserRealm(realm);
            return LayUiAdminResultVo.ok(WebResponseConstants.SUCCESS);
        }
        return LayUiAdminResultVo.ok(WebResponseConstants.FAIL);
    }

    /**
     * <p>
     * 获取监控用户列表
     * </p>
     *
     * @param current  当前页
     * @param size     每页显示条数
     * @param account  账号
     * @param username 用户名
     * @param email    电子邮箱
     * @return 分页Page对象
     * @author 皮锋
     * @custom.date 2020/7/23 16:37
     */
    @Override
    public Page<MonitorUserVo> getMonitorUserList(Long current, Long size, String account, String username, String email) {
        // 查询数据库
        IPage<MonitorUser> ipage = new Page<>(current, size);
        LambdaQueryWrapper<MonitorUser> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        if (StringUtils.isNotBlank(account)) {
            lambdaQueryWrapper.eq(MonitorUser::getAccount, account);
        }
        if (StringUtils.isNotBlank(username)) {
            lambdaQueryWrapper.like(MonitorUser::getUsername, username);
        }
        if (StringUtils.isNotBlank(email)) {
            lambdaQueryWrapper.like(MonitorUser::getEmail, email);
        }
        IPage<MonitorUser> monitorUserPage = this.monitorUserDao.selectPage(ipage, lambdaQueryWrapper);
        List<MonitorRole> monitorRoles = this.monitorRoleDao.selectList(new LambdaQueryWrapper<>());
        List<MonitorUser> monitorUsers = monitorUserPage.getRecords();
        // 转换成监控用户表现层对象
        List<MonitorUserVo> monitorUserVos = new LinkedList<>();
        for (MonitorUser monitorUser : monitorUsers) {
            long roleId = monitorUser.getRoleId();
            // 赋予角色名
            for (MonitorRole monitorRole : monitorRoles) {
                long id = monitorRole.getId();
                String roleName = monitorRole.getRoleName();
                if (roleId == id) {
                    MonitorUserVo monitorUserVo = MonitorUserVo.builder().roleName(roleName).build().convertFor(monitorUser);
                    // 屏蔽密码
                    monitorUserVo.setPassword(null);
                    monitorUserVos.add(monitorUserVo);
                }
            }
        }
        // 设置返回对象
        Page<MonitorUserVo> monitorUserVoPage = new Page<>();
        monitorUserVoPage.setRecords(monitorUserVos);
        monitorUserVoPage.setTotal(monitorUserPage.getTotal());
        return monitorUserVoPage;
    }

    /**
     * <p>
     * 添加用户
     * </p>
     *
     * @param monitorUserVo 用户信息
     * @return layUiAdmin响应对象：如果数据库中已经有此账号，LayUiAdminResultVo.data="exist"；
     * 如果添加用户成功，LayUiAdminResultVo.data="success"，否则LayUiAdminResultVo.data="fail"。
     * @author 皮锋
     * @custom.date 2020/8/1 21:33
     */
    @Override
    public LayUiAdminResultVo saveUser(MonitorUserVo monitorUserVo) {
        // 判断账号是否已经存在
        LambdaQueryWrapper<MonitorUser> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(MonitorUser::getAccount, monitorUserVo.getAccount());
        MonitorUser dbUser = this.monitorUserDao.selectOne(lambdaQueryWrapper);
        // 数据库中已经有此账号
        if (dbUser != null) {
            return LayUiAdminResultVo.ok(WebResponseConstants.EXIST);
        }
        MonitorUser monitorUser = monitorUserVo.convertTo();
        monitorUser.setRegisterTime(new Date());
        BCryptPasswordEncoder bc = new BCryptPasswordEncoder();
        // 加密密码
        String enPassword = bc.encode(new String(Base64.getDecoder().decode(monitorUser.getPassword()), StandardCharsets.UTF_8));
        monitorUser.setPassword(enPassword);
        int result = this.monitorUserDao.insert(monitorUser);
        // 成功
        if (result == 1) {
            return LayUiAdminResultVo.ok(WebResponseConstants.SUCCESS);
        }
        return LayUiAdminResultVo.ok(WebResponseConstants.FAIL);
    }

    /**
     * <p>
     * 编辑用户
     * </p>
     *
     * @param monitorUserVo 用户信息
     * @return layUiAdmin响应对象：如果编辑用户成功，LayUiAdminResultVo.data="success"，否则LayUiAdminResultVo.data="fail"。
     * @author 皮锋
     * @custom.date 2020/8/2 20:44
     */
    @Override
    public LayUiAdminResultVo editUser(MonitorUserVo monitorUserVo) {
        MonitorUser monitorUser = monitorUserVo.convertTo();
        // 密码
        String password = new String(Base64.getDecoder().decode(monitorUser.getPassword()), StandardCharsets.UTF_8);
        if (StringUtils.isBlank(password)) {
            // mybatis-plus不会更新值为null字段
            monitorUser.setPassword(null);
        } else {
            BCryptPasswordEncoder bc = new BCryptPasswordEncoder();
            monitorUser.setPassword(bc.encode(password));
        }
        // mybatis-plus不会更新值为null字段，让用户无法越权修改到其它账号
        monitorUser.setAccount(null);
        monitorUser.setUpdateTime(new Date());
        int result = this.monitorUserDao.updateById(monitorUser);
        if (result == 1) {
            Long currentMonitorUserRealmId = Objects.requireNonNull(SpringSecurityUtils.getCurrentMonitorUserRealm()).getId();
            MonitorUserRealm realm = (MonitorUserRealm) this.loadUserByUsername(monitorUserVo.getAccount());
            // 如果修改的是当前用户
            if (Objects.equals(currentMonitorUserRealmId, monitorUser.getId())) {
                // 更新springsecurity中当前用户
                SpringSecurityUtils.updateCurrentMonitorUserRealm(realm);
            }
            // 如果修改的不是当前用户
            else {
                // 使这个用户的session过期，让用户强制下线
                SpringSecurityUtils.letUserSessionExpireNow(this.sessionRegistry, realm);
            }
            return LayUiAdminResultVo.ok(WebResponseConstants.SUCCESS);
        }
        return LayUiAdminResultVo.ok(WebResponseConstants.FAIL);
    }

    /**
     * <p>
     * 删除用户
     * </p>
     *
     * @param monitorUserVos 用户信息
     * @return layUiAdmin响应对象：如果删除用户成功，LayUiAdminResultVo.data="success"，否则LayUiAdminResultVo.data="fail"。
     * @throws NotFoundUserException 找不到用户异常
     * @author 皮锋
     * @custom.date 2020/8/2 17:35
     */
    @Override
    @Transactional(rollbackFor = Throwable.class)
    public LayUiAdminResultVo deleteUser(List<MonitorUserVo> monitorUserVos) throws NotFoundUserException {
        int size = monitorUserVos.size();
        List<Long> ids = Lists.newArrayList();
        for (MonitorUserVo monitorUserVo : monitorUserVos) {
            Long id = monitorUserVo.getId();
            ids.add(id);
        }
        List<MonitorUserRealm> monitorUserRealms = this.getMonitorUserRealms(ids);
        int result = this.monitorUserDao.deleteBatchIds(ids);
        if (size == result) {
            // 使这些用户的session过期，让用户强制下线
            SpringSecurityUtils.letUserSessionExpireNow(this.sessionRegistry, monitorUserRealms.toArray());
            return LayUiAdminResultVo.ok(WebResponseConstants.SUCCESS);
        }
        return LayUiAdminResultVo.ok(WebResponseConstants.FAIL);
    }

}
