package com.ruoyi.system.user.service.impl;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ScheduledExecutorService;
import java.util.function.Function;
import java.util.stream.Collectors;

import javax.validation.Validator;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;

import com.github.pagehelper.PageHelper;
import com.ruoyi.bst.balanceLog.domain.BalanceLog;
import com.ruoyi.bst.balanceLog.domain.enums.BalanceLogBstType;
import com.ruoyi.bst.balanceLog.service.BalanceLogService;
import com.ruoyi.bst.realName.domain.RealName;
import com.ruoyi.bst.userApp.service.UserAppService;
import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.constants.ConfigKeys;
import com.ruoyi.common.core.domain.entity.Role;
import com.ruoyi.common.core.domain.entity.User;
import com.ruoyi.common.core.domain.vo.UserVO;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.core.redis.RedisLock;
import com.ruoyi.common.core.redis.enums.RedisLockKey;
import com.ruoyi.common.enums.UserType;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.MathUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.ServiceUtil;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.collection.CollectionUtils;
import com.ruoyi.system.config.service.ConfigService;
import com.ruoyi.system.dept.service.DeptService;
import com.ruoyi.system.post.domain.Post;
import com.ruoyi.system.post.mapper.PostMapper;
import com.ruoyi.system.role.mapper.RoleMapper;
import com.ruoyi.system.role.service.RoleService;
import com.ruoyi.system.user.domain.UserQuery;
import com.ruoyi.system.user.domain.enums.UserAuthType;
import com.ruoyi.system.user.domain.vo.UserNameVO;
import com.ruoyi.system.user.mapper.UserMapper;
import com.ruoyi.system.user.service.UserService;
import com.ruoyi.system.user.service.UserValidator;
import com.ruoyi.system.user.utils.UserUtil;
import com.ruoyi.system.userPost.domain.UserPost;
import com.ruoyi.system.userPost.mapper.UserPostMapper;
import com.ruoyi.system.userRole.domain.UserRole;
import com.ruoyi.system.userRole.mapper.UserRoleMapper;

/**
 * 用户 业务层处理
 *
 * @author ruoyi
 */
@Service
public class UserServiceImpl implements UserService
{

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private RoleMapper roleMapper;

    @Autowired
    private PostMapper postMapper;

    @Autowired
    private UserRoleMapper userRoleMapper;

    @Autowired
    private UserPostMapper userPostMapper;

    @Autowired
    private ConfigService configService;

    @Autowired
    private DeptService deptService;

    @Autowired
    protected Validator validator;

    @Autowired
    private ScheduledExecutorService scheduledExecutorService;

    @Autowired
    private TransactionTemplate transactionTemplate;

    @Autowired
    private UserValidator userValidator;

    @Autowired
    private RoleService roleService;

    @Autowired
    private RedisCache redisCache;

    @Autowired
    private BalanceLogService balanceLogService;

    @Autowired
    private UserAppService userAppService;

    @Autowired
    private RedisLock redisLock;

    /**
     * 根据条件分页查询用户列表
     *
     * @param user 用户信息
     * @return 用户信息集合信息
     */
    @Override
    public List<UserVO> selectUserListScope(UserQuery user) {
        return userMapper.selectUserList(user);
    }

    /**
     * 根据条件分页查询已分配用户角色列表
     *
     * @param user 用户信息
     * @return 用户信息集合信息
     */
    @Override
    public List<UserVO> selectAllocatedList(User user)
    {
        return userMapper.selectAllocatedList(user);
    }

    /**
     * 根据条件分页查询未分配用户角色列表
     *
     * @param user 用户信息
     * @return 用户信息集合信息
     */
    @Override
    public List<UserVO> selectUnallocatedList(User user)
    {
        return userMapper.selectUnallocatedList(user);
    }

    /**
     * 通过用户名查询用户
     *
     * @param userName 用户名
     * @return 用户对象信息
     */
    @Override
    public UserVO selectUserByUserName(String userName)
    {
        return userMapper.selectUserByUserName(userName);
    }

    /**
     * 通过用户ID查询用户
     *
     * @param userId 用户ID
     * @return 用户对象信息
     */
    @Override
    public UserVO selectUserById(Long userId)
    {
        return userMapper.selectUserById(userId);
    }

    /**
     * 查询用户所属角色组
     *
     * @param userName 用户名
     * @return 结果
     */
    @Override
    public String selectUserRoleGroup(String userName)
    {
        List<Role> list = roleMapper.selectRolesByUserName(userName);
        if (CollectionUtils.isEmpty(list))
        {
            return StringUtils.EMPTY;
        }
        return list.stream().map(Role::getRoleName).collect(Collectors.joining(","));
    }

    /**
     * 查询用户所属岗位组
     *
     * @param userName 用户名
     * @return 结果
     */
    @Override
    public String selectUserPostGroup(String userName)
    {
        List<Post> list = postMapper.selectPostsByUserName(userName);
        if (CollectionUtils.isEmpty(list))
        {
            return StringUtils.EMPTY;
        }
        return list.stream().map(Post::getPostName).collect(Collectors.joining(","));
    }

    /**
     * 校验手机号码是否唯一
     *
     * @param user 用户信息
     * @return
     */
    @Override
    public boolean checkPhoneUnique(User user)
    {
        Long userId = StringUtils.isNull(user.getUserId()) ? -1L : user.getUserId();
        User info = userMapper.checkPhoneUnique(user.getPhonenumber());
        if (StringUtils.isNotNull(info) && info.getUserId().longValue() != userId.longValue())
        {
            return UserConstants.NOT_UNIQUE;
        }
        return UserConstants.UNIQUE;
    }

    /**
     * 校验email是否唯一
     *
     * @param user 用户信息
     * @return
     */
    @Override
    public boolean checkEmailUnique(User user)
    {
        Long userId = StringUtils.isNull(user.getUserId()) ? -1L : user.getUserId();
        User info = userMapper.checkEmailUnique(user.getEmail());
        if (StringUtils.isNotNull(info) && info.getUserId().longValue() != userId.longValue())
        {
            return UserConstants.NOT_UNIQUE;
        }
        return UserConstants.UNIQUE;
    }

    /**
     * 校验用户是否允许操作
     *
     * @param user 用户信息
     */
    @Override
    public void checkUserAllowed(User user)
    {
        if (StringUtils.isNotNull(user.getUserId()) && user.isAdmin())
        {
            throw new ServiceException("不允许操作超级管理员用户");
        }
    }

    /**
     * 校验用户是否有数据权限
     *
     * @param userId 用户id
     */
    @Override
    public void checkUserDataScope(Long userId)
    {
        if (!User.isAdmin(SecurityUtils.getUserId()))
        {
            UserQuery user = new UserQuery();
            user.setUserId(userId);
            List<UserVO> users = this.selectUserListScope(user);
            if (StringUtils.isEmpty(users))
            {
                throw new ServiceException("没有权限访问用户数据！");
            }
        }
    }

    /**
     * 新增保存用户信息
     *
     * @param user 用户信息
     * @return 结果
     */
    @Override
    public int insertUser(UserVO user)
    {

        // 填充数据
        if (user.getPassword() != null) {
            user.setPassword(SecurityUtils.encryptPassword(user.getPassword()));
        }
        // 设置默认信息
        this.setDefaultInfo(user);

        return executeWithLock(user, u -> {
            // 校验
            ServiceUtil.assertion(StringUtils.isNotEmpty(user.getUserName()) && !userValidator.checkUserNameUnique(user),
                    "新增用户'" + user.getUserName() + "'失败，用户账号已存在"
            );

            // 操作数据库
            Integer result = transactionTemplate.execute(status -> {
                // 新增用户信息
                int rows = userMapper.insertUser(user);

                if (rows > 0) {
                    // 后校验
                    this.afterCheck(user.getUserId());

                    // 新增用户与角色管理
                    insertUserRole(user);

                    this.clearCache();
                }

                return rows;
            });

            return result == null ? 0 : result;
        });
    }

    private void setDefaultInfo(UserVO user) {
        if (StringUtils.isBlank(user.getWithdrawServiceType()) || user.getWithdrawServiceValue() == null) {
            String type = configService.selectConfigByKey(ConfigKeys.USER_DEFAULT_WITHDRAW_SERVICE_TYPE);
            BigDecimal value = configService.getDecimal(ConfigKeys.USER_DEFAULT_WITHDRAW_SERVICE_VALUE);
            if (type != null && value != null) {
                user.setWithdrawServiceType(type);
                user.setWithdrawServiceValue(value);
            }
        }
        if (user.getBonusDelay() == null) {
            user.setBonusDelay(24);
        }
    }

    void clearCache() {
        redisCache.deleteObject(CacheConstants.USER_NAME_LIST);
    }


    /**
     * 注册用户信息
     *
     * @param user 用户信息
     * @return 结果
     */
    @Override
    public boolean registerUser(UserVO user)
    {
        int rows = this.insertUser(user);

        if (rows > 0) {
            clearCache();
        }
        return rows > 0;
    }

    /**
     * 修改保存用户信息
     *
     * @param user 用户信息
     * @return 结果
     */
    @Override
    public int updateUser(UserVO user) {
        return executeWithLock(user, u -> {
            Long userId = user.getUserId();
            Integer result = transactionTemplate.execute(status -> {
                ServiceUtil.assertion(StringUtils.hasText(user.getUserName()) && !userValidator.checkUserNameUnique(user),
                        "修改账户'" + user.getUserName() + "'失败，用户账号已存在"
                );

                int rows = userMapper.updateUser(user);

                if (rows > 0) {
                    // 后校验
                    this.afterCheck(user.getUserId());

                    if (user.getRoleIds() != null) {
                        // 删除用户与角色关联
                        userRoleMapper.deleteUserRoleByUserId(userId);
                        // 新增用户与角色关联
                        insertUserRole(user);
                    }

                    this.clearCache();
                }

                return rows;
            });

            return result == null ? 0 : result;
        });
    }


    private int executeWithLock(User user, Function<User, Integer> operation) {
        if (StringUtils.hasText(user.getUserName())) {
            boolean lockSn = redisLock.lock(RedisLockKey.USER_NAME_UNIQUE, user.getUserName());
            ServiceUtil.assertion(!lockSn, "账号为为%s的用户当前操作的人过多，请稍后再试", user.getUserName());
        }
        try {
            Integer result = operation.apply(user);
            return result == null ? 0 : result;
        } finally {
            if (StringUtils.hasText(user.getUserName())) {
                redisLock.unlock(RedisLockKey.USER_NAME_UNIQUE, user.getUserName());
            }
        }
    }

    void afterCheck(Long userId) {
        UserVO vo = selectUserById(userId);
        userValidator.validate(vo);
    }

    /**
     * 用户授权角色
     *
     * @param userId 用户ID
     * @param roleIds 角色组
     */
    @Override
    @Transactional
    public void insertUserAuth(Long userId, Long[] roleIds)
    {
        userRoleMapper.deleteUserRoleByUserId(userId);
        insertUserRole(userId, roleIds);
    }

    /**
     * 修改用户状态
     *
     * @param user 用户信息
     * @return 结果
     */
    @Override
    public int updateUserStatus(User user)
    {
        int rows = userMapper.updateUser(user);

        if (rows > 0) {
            clearCache();
        }

        return rows;
    }

    /**
     * 修改用户基本信息
     *
     * @param user 用户信息
     * @return 结果
     */
    @Override
    public int updateUserProfile(User user)
    {
        int rows = userMapper.updateUser(user);

        if (rows > 0) {
            clearCache();
        }

        return rows;
    }

    /**
     * 修改用户头像
     *
     * @param userName 用户名
     * @param avatar 头像地址
     * @return 结果
     */
    @Override
    public boolean updateUserAvatar(String userName, String avatar)
    {
        int rows = userMapper.updateUserAvatar(userName, avatar);

        if (rows > 0) {
            clearCache();
        }

        return rows > 0;
    }

    /**
     * 重置用户密码
     *
     * @param user 用户信息
     * @return 结果
     */
    @Override
    public int resetPwd(User user)
    {
        return userMapper.updateUser(user);
    }

    /**
     * 重置用户密码
     *
     * @param userName 用户名
     * @param password 密码
     * @return 结果
     */
    @Override
    public int resetUserPwd(String userName, String password)
    {
        return userMapper.resetUserPwd(userName, password);
    }

    /**
     * 新增用户角色信息
     *
     * @param user 用户对象
     */
    public void insertUserRole(UserVO user)
    {
        this.insertUserRole(user.getUserId(), user.getRoleIds());
    }

    /**
     * 新增用户岗位信息
     *
     * @param user 用户对象
     */
    public void insertUserPost(UserVO user)
    {
        Long[] posts = user.getPostIds();
        if (StringUtils.isNotEmpty(posts))
        {
            // 新增用户与岗位管理
            List<UserPost> list = new ArrayList<UserPost>(posts.length);
            for (Long postId : posts)
            {
                UserPost up = new UserPost();
                up.setUserId(user.getUserId());
                up.setPostId(postId);
                list.add(up);
            }
            userPostMapper.batchUserPost(list);
        }
    }

    /**
     * 新增用户角色信息
     *
     * @param userId 用户ID
     * @param roleIds 角色组
     */
    public void insertUserRole(Long userId, Long[] roleIds)
    {
        if (userId == null) {
            return;
        }
        if (StringUtils.isNotEmpty(roleIds))
        {
            // 新增用户与角色管理
            List<UserRole> list = new ArrayList<UserRole>(roleIds.length);
            for (Long roleId : roleIds)
            {
                if (roleId == null) {
                    continue;
                }
                UserRole ur = new UserRole();
                ur.setUserId(userId);
                ur.setRoleId(roleId);
                list.add(ur);
            }
            if (CollectionUtils.isNotEmptyElement(list)) {
                userRoleMapper.batchUserRole(list);
            }
        }
    }

    /**
     * 通过用户ID删除用户
     *
     * @param userId 用户ID
     * @return 结果
     */
    @Override
    @Transactional
    public int deleteUserById(Long userId)
    {
        // 删除用户与角色关联
        userRoleMapper.deleteUserRoleByUserId(userId);
        // 删除用户与岗位表
        userPostMapper.deleteUserPostByUserId(userId);
        int rows = userMapper.deleteUserById(userId);

        if (rows > 0) {
            clearCache();
        }

        return rows;
    }

    /**
     * 批量删除用户信息
     *
     * @param userIds 需要删除的用户ID
     * @return 结果
     */
    @Override
    @Transactional
    public int deleteUserByIds(Long[] userIds)
    {
        for (Long userId : userIds)
        {
            checkUserAllowed(new User(userId));
            checkUserDataScope(userId);
        }
        // 删除用户与角色关联
        userRoleMapper.deleteUserRole(userIds);
        // 删除用户与岗位关联
        userPostMapper.deleteUserPost(userIds);
        int rows = userMapper.deleteUserByIds(userIds);

        if (rows > 0) {
            clearCache();
        }

        return rows;
    }

    @Override
    public UserVO selectUserByPhone(String phone) {
        if (StringUtils.isBlank(phone)) {
            return null;
        }
        UserQuery query = new UserQuery();
        query.setEqUserName(phone);
        return this.selectOne(query);
    }

    @Override
    public List<UserVO> selectUserList(UserQuery query) {
        return userMapper.selectUserList(query);
    }

    @Override
    public List<UserNameVO> selectAllUserNameList() {
        List<UserNameVO> list = redisCache.getCacheObject(CacheConstants.USER_NAME_LIST);
        if (CollectionUtils.isEmptyElement(list)) {
            UserQuery query = new UserQuery();
            query.setExcludeUserId(1L);
            list = userMapper.selectNameList(query);
            if (CollectionUtils.isNotEmptyElement(list)) {
                redisCache.setCacheObject(CacheConstants.USER_NAME_LIST, list);
            }
        }
        return list;
    }

    @Override
    public List<UserVO> selectByIds(List<Long> ids) {
        if (CollectionUtils.isEmptyElement(ids)) {
            return Collections.emptyList();
        }
        UserQuery query = new UserQuery();
        query.setUserIds(ids);
        return userMapper.selectUserList(query);
    }

    private UserVO selectOne(UserQuery query) {
        PageHelper.startPage(1, 1);
        List<UserVO> list = userMapper.selectUserList(query);
        return CollectionUtils.firstElement(list);
    }

    @Override
    public List<UserVO> selectUserListByNickNames(List<String> nickNames) {
        if (CollectionUtils.isEmptyElement(nickNames)) {
            return Collections.emptyList();
        }

        UserQuery query = new UserQuery();
        query.setNickNames(nickNames);
        return userMapper.selectUserList(query);
    }

    @Override
    public List<Long> selectUserIdsByDeptIds(List<Long> deptIds) {
        if (CollectionUtils.isEmptyElement(deptIds)) {
            return Collections.emptyList();
        }
        UserQuery query = new UserQuery();
        query.setDeptIds(deptIds);
        return userMapper.selectUserIds(query);
    }

    @Override
    public UserVO selectByAppOpenId(Long appId, String openId) {
        if (appId == null || StringUtils.isBlank(openId)) {
            return null;
        }
        UserQuery query = new UserQuery();
        query.setAppId(appId);
        query.setEqOpenId(openId);
        return this.selectOne(query);
    }

    @Override
    public int addBalance(Long userId, BigDecimal amount, BalanceLogBstType bstType, Long bstId, String reason) {
        if (userId == null || amount == null ) {
            return 0;
        }
        // 如果金额为0，则不进行任何操作
        if (amount.compareTo(BigDecimal.ZERO) == 0) {
            return 1;
        }
        // 如果金额为负数，则抛出异常
        ServiceUtil.assertion(amount.compareTo(BigDecimal.ZERO) < 0, "增加的金额不能为负数");

        Integer result = transactionTemplate.execute(status -> {
            // 查询用户余额，并锁定
            BigDecimal balance = userMapper.selectBalanceForUpdate(userId);
            if (balance == null) {
                balance = BigDecimal.ZERO;
            }

            // 更新用户余额
            int rows = userMapper.addBalance(userId, amount);

            // 新增余额日志
            if (rows > 0) {
                BalanceLog log = new BalanceLog();
                log.setUserId(userId);
                log.setAmount(amount);
                log.setBeforeBalance(balance);
                log.setAfterBalance(balance.add(amount));
                log.setBstType(bstType.getCode());
                log.setBstId(bstId);
                log.setReason(reason);
                int logInsert = balanceLogService.insertBalanceLog(log);
                ServiceUtil.assertion(logInsert != 1, "新增余额日志失败");
            }

            return rows;
        });

        return result == null ? 0 : result;
    }

    @Override
    public int subtractBalance(Long userId, BigDecimal amount, BalanceLogBstType bstType, Long bstId, String reason, boolean checkBalance) {
        if (userId == null || amount == null) {
            return 0;
        }
        // 如果金额为0，则不进行任何操作
        if (amount.compareTo(BigDecimal.ZERO) == 0) {
            return 1;
        }
        // 如果金额为负数，则抛出异常
        ServiceUtil.assertion(amount.compareTo(BigDecimal.ZERO) < 0, "减少的金额不能为负数");

        Integer result = transactionTemplate.execute(status -> {
            // 查询用户余额，并锁定
            BigDecimal balance = userMapper.selectBalanceForUpdate(userId);
            if (balance == null) {
                balance = BigDecimal.ZERO;
            }

            // 更新用户余额
            int rows = userMapper.subtractBalance(userId, amount, checkBalance);

            // 新增余额日志
            if (rows > 0) {
                BalanceLog log = new BalanceLog();
                log.setUserId(userId);
                log.setAmount(amount.negate());
                log.setBeforeBalance(balance);
                log.setAfterBalance(balance.subtract(amount));
                log.setBstType(bstType.getCode());
                log.setBstId(bstId);
                log.setReason(reason);
                int logInsert = balanceLogService.insertBalanceLog(log);
                ServiceUtil.assertion(logInsert != 1, "新增余额日志失败");
            }

            return rows;
        });

        return result == null ? 0 : result;
    }

    @Override
    public int updateAreaId(Long userId, Long areaId) {
        if (userId == null || areaId == null) {
            return 0;
        }
        User user = new User();
        user.setUserId(userId);
        user.setAreaId(areaId);
        return userMapper.updateUser(user);
    }



    // 注册用户
    @Override
    public UserVO registerUser(String phone, String openId, Long appId) {
        if (StringUtils.isBlank(phone)) {
            return null;
        }

        // 生成昵称
        String nickName = null;
        if (StringUtils.hasText(openId)) {
            nickName = "微信" + openId.substring(openId.length() - MathUtils.min(openId.length(), 4));
        }
        if (StringUtils.hasText(phone)) {
            nickName = "用户" + phone.substring(phone.length() - MathUtils.min(phone.length(), 4));
        }

        UserVO user = new UserVO();
        user.setNickName(nickName);
        user.setPhonenumber(phone);
        user.setUserName(phone);
        user.setUserType(UserType.USER.getCode());
        user.setPassword(UserUtil.randomPassword());
        user.setPoint(BigDecimal.valueOf(96));

        return transactionTemplate.execute(status -> {
            int rows = this.insertUser(user);

            if (rows > 0 ) {
                // 绑定APP
                if (appId != null && StringUtils.hasText(openId)) {
                    int bind = userAppService.bind(user.getUserId(), appId, openId);
                    ServiceUtil.assertion(bind != 1, "绑定APP失败");
                }
                // 返回用户信息
                return this.selectUserById(user.getUserId());
            }

            return null;
        });
    }

    @Override
    public int updateUserRealName(RealName real) {
        if (real == null || real.getUserId() == null) {
            return 0;
        }

        // 修改实名认证信息（用户表）
        UserVO data = new UserVO();
        data.setUserId(real.getUserId());
        data.setNickName(real.getUserName());
        data.setRealName(real.getUserName());
        data.setRealIdCard(real.getIdCard());
        data.setRealPhone(real.getMobile());
        data.setIsReal(true);
        return userMapper.updateUser(data);
    }

    @Override
    public int readGuide(Long userId, String key) {
        if (userId == null || StringUtils.isBlank(key)) {
            return 0;
        }
        UserVO user = this.selectUserById(userId);
        if (user == null ) {
            return 0;
        }

        List<String> guides = user.getGuides();
        if (guides == null) {
            guides = new ArrayList<>();
        }
        guides.add(key);

        User data = new User();
        data.setUserId(userId);
        data.setGuides(guides);
        return userMapper.updateUser(data);
    }

    @Override
    public UserVO getOrCreateByPhone(String phone) {
        UserVO user = this.selectUserByUserName(phone);
        if (user == null) {
            user = this.registerUser(phone, null, null);
        }
        return user;
    }

    @Override
    public int addAuthType(Long userId, UserAuthType authType) {
        if (userId == null || authType == null) {
            return 0;
        }
        Integer result = transactionTemplate.execute(status -> {
            String authTypes = userMapper.selectAuthTypeForUpdate(userId);
            if (StringUtils.isBlank(authTypes)) {
                authTypes = authType.getCode();
            } else {
                authTypes = authTypes + "," + authType.getCode();
            }

            UserVO user = new UserVO();
            user.setUserId(userId);
            user.setAuthTypes(Arrays.asList(authTypes.split(",")));
            return userMapper.updateUser(user);
        });

        return result == null ? 0 : result;
    }
}
