package com.sw.mes.auth.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sw.mes.auth.domain.AuthPermission;
import com.sw.mes.auth.domain.AuthRole;
import com.sw.mes.auth.domain.AuthUser;
import com.sw.mes.auth.domain.AuthUserRole;
import com.sw.mes.auth.mapper.AuthUserMapper;
import com.sw.mes.auth.service.AuthPermissionService;
import com.sw.mes.auth.service.AuthRoleService;
import com.sw.mes.auth.service.AuthUserRoleService;
import com.sw.mes.auth.service.AuthUserService;
import com.sw.mes.auth.utils.PasswordUtil;
import com.sw.mes.core.constant.Constants;
import com.sw.mes.core.dto.auth.UserImportDto;
import com.sw.mes.core.response.Result;
import com.sw.mes.core.response.ResultCode;
import com.sw.mes.core.response.ResultGenerator;
import com.sw.mes.core.utils.NumberUtil;
import com.sw.mes.core.utils.StringUtils;
import com.sw.mes.redis.server.RedissonUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;

import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.sw.mes.core.constant.Constants.*;

/**
 * @author JS111816
 * @description 针对表【auth_user(【系统域—系统管理】用户表)】的数据库操作Service实现
 * @createDate 2022-05-19 14:21:52
 */
@Service
public class AuthUserServiceImpl extends ServiceImpl<AuthUserMapper, AuthUser>
        implements AuthUserService {
    //登录有效时间（一个星期）
    public static final Long timeout = 7 * 24 * 60 * 60 * 1L;

    @Autowired
    private AuthUserMapper authUserMapper;

    @Autowired
    private AuthUserRoleService authUserRoleService;

    @Autowired
    private AuthPermissionService authPermissionService;

    @Autowired
    private AuthUserService authUserService;

    @Autowired
    private AuthRoleService authRoleService;

    @Autowired
    RedisTemplate<String, Object> redisTemplate;

    @Autowired
    @Qualifier(value = "authMesThreadPoolTaskExecutor")
    private ThreadPoolTaskExecutor authMesThreadPoolTaskExecutor;


    @Override
    public AuthUser findByLoginName(String loginName) {
        LambdaQueryWrapper<AuthUser> queryWrapper = new LambdaQueryWrapper();
        queryWrapper.eq(AuthUser::getLoginName, loginName);
        List<AuthUser> users = authUserMapper.selectList(queryWrapper);
        if (users != null && users.size() > 0) {
            return users.get(0);
        }
        return null;
    }

    @Override
    public AuthUser getByCardNumber(String cardno) {
        LambdaQueryWrapper<AuthUser> queryWrapper = new LambdaQueryWrapper();
        queryWrapper.eq(AuthUser::getCardNumber, cardno);
        List<AuthUser> users = authUserMapper.selectList(queryWrapper);
        if (users != null && users.size() > 0) {
            return users.get(0);
        }
        return null;
    }

    @Override
    public Map<String, Object> loginByUsername(String username, String password) throws Exception {
        Map<String, Object> map = new HashMap<>();
        AuthUser user = this.findByLoginName(username);
        if (user != null) {
            try {
                if (PasswordUtil.validatePassword(password, user.getPassword(), user.getSalt())) {
                    map.put("userId", user.getId());
                    map.put("realname", user.getRealName());
                    map.put("roleids", authUserRoleService.getRoleNamesByUserId(user.getId()));
                } else {
                    throw new Exception("用户名或密码错误！");
                }
            } catch (Exception e) {
                e.printStackTrace();
                throw new Exception("登录异常！请联系管理员");
            }
        } else {
            throw new Exception("用户不存在！");
        }
        return map;
    }

    @Override
    public Map<String, Object> loginByCardNo(String cardNo) throws Exception {
        Map<String, Object> map = new HashMap<>();
        AuthUser user = getByCardNumber(cardNo);
        if (user != null) {
            map.put("userId", user.getId());
            map.put("realname", user.getRealName());
            map.put("roleids", authUserRoleService.getRoleNamesByUserId(user.getId()));
        } else {
            throw new Exception("卡号不存在！");
        }
        return map;
    }

    @Override
    public List<AuthUser> findByUserName(String userName) {
        return authUserMapper.findByUserName(userName);
    }

    @Override
    public Map<String, Object> getPermissionByLoginName(String username, String password) throws Exception {
        Map<String, Object> map = new HashMap<>();
        AuthUser user = findByLoginName(username);
        if (user != null) {
            try {
                if (PasswordUtil.validatePassword(password, user.getPassword(), user.getSalt())) {
                    map.put("userId", user.getId());
                    map.put("realName", user.getRealName());
                    List<AuthPermission> permissions = authPermissionService.getByUserId(user.getId());
                    if (permissions != null) {
                        map.put("user_permission_codes", String.join(",", permissions.stream().map(permission -> permission.getCode()).collect(Collectors.toList())));
                    } else {
                        map.put("user_permission_codes", "");
                    }
                } else {
                    throw new Exception("用户名或密码错误！");
                }
            } catch (Exception e) {
                e.printStackTrace();
                throw new Exception("登录异常！请联系管理员");
            }
        } else {
            throw new Exception("用户不存在！");
        }
        return map;
    }

    @Override
    public List<AuthUser> getByUserName(String name) {
        return authUserMapper.getByUserName(name);
    }

    @Override
    public AuthUser findByWorkNo(String receiverCode) {
        LambdaQueryWrapper<AuthUser> queryWrapper = new LambdaQueryWrapper();
        queryWrapper.eq(AuthUser::getWorkNo, receiverCode);
        List<AuthUser> list = authUserMapper.selectList(queryWrapper);
        if (list != null && list.size() > 0) {
            return list.get(0);
        }
        return null;
    }

    @Override
    public Result login(String loginName, String password, String clientType) {
        AuthUser user = authUserService.findByLoginName(loginName);
        if (user == null) {
            return ResultGenerator.fail("用户名不存在");
        }

        if (USER_DISABLE.equals(user.getStatus())) {
            return ResultGenerator.fail("该账号已被禁用");
        }

        // 验证密码
        try {
            boolean checkPassword = PasswordUtil.validatePassword(password, user.getPassword(), user.getSalt());
            if (checkPassword) {
                String token = cacheUser(user, clientType);
                if (USER_ORIGINAL.equals(user.getStatus())) {
                    return ResultGenerator.success(ResultCode.SUCCESS_201.getCode(), "初次登录，请先重置密码", token);
                }
                return ResultGenerator.success(ResultCode.SUCCESS.getCode(), "登录成功", token);
            }
            return ResultGenerator.fail("密码错误");
        } catch (Exception e) {
            e.printStackTrace();
            return ResultGenerator.fail("登录异常：" + e.getMessage());
        }
    }

    @Override
    public String cacheUser(AuthUser user, String clientType) {
        String token = UUID.randomUUID().toString();
        Map<String, Object> userinfo = new HashMap<>();
        userinfo.put(TOKEN, token);
        userinfo.put(USER_ID, user.getId() + "");
        userinfo.put(USER_NAME, user.getRealName());
        //角色id集合
        List<AuthRole> roles = authRoleService.getByUserId(user.getId());
        userinfo.put(USER_ROLES, String.join(",", roles.stream().map(role -> role.getId().toString()).collect(Collectors.toList())));

        //权限id集合
        List<AuthPermission> permissions = authPermissionService.getByUserId(user.getId());
        userinfo.put(USER_PERMISSIONS, String.join(",", permissions.stream().map(permission -> permission.getId().toString()).collect(Collectors.toList())));
        String userinfostr = JSON.toJSONString(userinfo);
//        userinfo.put(USER_PERMISSION_CODES, String.join(",", permissions.stream().map(permission -> permission.getCode()).collect(Collectors.toList())));
//        //推送权限集合
//        List<Permission> pushPermissions = permissions.stream().filter(permission -> permission.getType().equals(PUSH_PERMISSION)).collect(Collectors.toList());
//        userinfo.put(PUSH_TOPICS, String.join(",", pushPermissions.stream().map(permission -> permission.getCode()).collect(Collectors.toSet())));
        if (StringUtils.isBlank(clientType)) {
            clientType = "web";
        }

        String md5Password = DigestUtils.md5DigestAsHex(("user_id" + user.getId()).getBytes());
        String tokenPrefix = TOKEN_PREFIX + clientType + "_" + md5Password + "_";
        Set<String> keys = redisTemplate.keys(tokenPrefix + "*");
        //找到历史token
        if (keys != null && keys.size() > 0) {
            for (String historyTokenKeys : keys) {
                String historyToken = NumberUtil.object2String(historyTokenKeys, null);
                redisTemplate.delete(historyToken);
            }
        }
        token = tokenPrefix + token;
        redisTemplate.opsForValue().set(TOKEN_PREFIX + token, userinfostr, timeout, TimeUnit.SECONDS);
        return token;
    }


    @Override
    public Integer getOnLineUserCount() {
        String tokenPrefix = TOKEN_PREFIX + "web_*";
        Set<String> keys = redisTemplate.keys(TOKEN_PREFIX + tokenPrefix);
        if (keys == null) {
            return null;
        }
        return keys.size();
    }


    @Override
    public List<AuthUser> findByLoginNameIn(List<String> userName) {
        return authUserMapper.findByLoginNameIn(userName);
    }

    @Override
    public Result<String> login(String loginName, String password, String clientType, String teamGroup, String classNo) {
        AuthUser user = authUserService.findByLoginName(loginName);
        if (user == null) {
            return ResultGenerator.fail("用户名不存在");
        }

        if (USER_DISABLE.equals(user.getStatus())) {
            return ResultGenerator.fail("该账号已被禁用");
        }

        // 验证密码
        try {
            boolean checkPassword = PasswordUtil.validatePassword(password, user.getPassword(), user.getSalt());
            if (checkPassword) {
                String token = cacheUser(user, clientType, teamGroup, classNo);
                if (USER_ORIGINAL.equals(user.getStatus())) {
                    return ResultGenerator.success(ResultCode.SUCCESS_201.getCode(), "初次登录，请先重置密码", token);
                }
                return ResultGenerator.success(ResultCode.SUCCESS.getCode(), "登录成功", token);
            }
            return ResultGenerator.fail("密码错误");
        } catch (Exception e) {
            e.printStackTrace();
            return ResultGenerator.fail("登录异常：" + e.getMessage());
        }
    }

    @Override
    @Transactional(rollbackFor = Throwable.class)
    public void importUser(List<UserImportDto> users) {
        checkImportUser(users);  //检查导入用户对象

        //保存导入用户及分配角色
        saveImportUser(users);
    }

    private void saveImportUser(List<UserImportDto> users) {

        for (UserImportDto userDto : users) {
            //保存用户
            AuthUser user = buildUser(userDto);
            try {
                PasswordUtil.HashPassword hashPassword = PasswordUtil.encryptPassword(user.getPassword());
                user.setSalt(hashPassword.salt);
                user.setPassword(hashPassword.password);
            } catch (Exception e) {
                e.printStackTrace();
                log.error("密码加密错误", e);
                throw new RuntimeException("密码加密错误" + e.getMessage());
            }
            user.setCreateTime(new Date());
            user.setStatus(Constants.USER_ORIGINAL);
            authUserService.saveOrUpdate(user);

            //保存角色
            if (StringUtils.isNotBlank(userDto.getRoleName())) {
                LambdaQueryWrapper<AuthRole> queryWrapper = new LambdaQueryWrapper<>();
                String[] roleNames = userDto.getRoleName().split(",");
                List<String> roleList = new ArrayList<>();
                for (String roleName : roleNames) {
                    roleList.add(roleName.trim());
                }
                queryWrapper.in(AuthRole::getName, roleList);
                List<AuthRole> authRoles = authRoleService.getBaseMapper().selectList(queryWrapper);
                for (AuthRole authRole : authRoles) {
                    AuthUserRole userRole = new AuthUserRole();
                    userRole.setRoleId(authRole.getId());
                    userRole.setUserId(user.getId());
                    authUserRoleService.saveOrUpdate(userRole);
                }
            }
        }
    }

    private AuthUser buildUser(UserImportDto userDto) {
        AuthUser authUser = new AuthUser();
        authUser.setLoginName(userDto.getLoginName());
        authUser.setRealName(userDto.getName());
        authUser.setPhoneNo(userDto.getJob());
        authUser.setPassword(userDto.getPassword());
        authUser.setWorkNo(userDto.getWorkNo());
        authUser.setEmail(userDto.getEmail());
        authUser.setClassTeam("白班");
        authUser.setClassName("A班");
        return authUser;
    }

    private void checkImportUser(List<UserImportDto> users) {
        StringBuffer globalError = new StringBuffer();

        CountDownLatch countDownLatch = new CountDownLatch(3);

        authMesThreadPoolTaskExecutor.execute(() -> {
            try {
                checkImportRequiredFields(users); //检查必填项
            } catch (Exception e) {
                e.printStackTrace();
                log.error("用户导入检查必填项", e);
                globalError.append(e.getMessage());
            }finally {
                countDownLatch.countDown();
            }
        });

        authMesThreadPoolTaskExecutor.execute(() -> {
            try {
                checkImportRepeatFields(users); //检查重复项
            } catch (Exception e) {
                e.printStackTrace();
                log.error("用户导入检查重复项", e);
                globalError.append(e.getMessage());
            }finally {
                countDownLatch.countDown();
            }
        });

        authMesThreadPoolTaskExecutor.execute(() -> {
            try {
                checkImportRole(users); //检查用户角色名合法性
            } catch (Exception e) {
                e.printStackTrace();
                log.error("用户导入检查用户角色名合法性", e);
                globalError.append(e.getMessage());
            }finally {
                countDownLatch.countDown();
            }
        });

        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
            log.error("导入错误", e);
            globalError.append(e.getMessage());
        }

        if (globalError.length() > 0){
            throw new RuntimeException(globalError.toString());
        }
    }

    private void checkImportRole(List<UserImportDto> users) {
        StringBuilder roleNameError = new StringBuilder();

        List<AuthRole> authRoles = authRoleService.getBaseMapper().selectList(new LambdaQueryWrapper<>());
        Map<String, AuthRole> authRoleMap = authRoles.stream().collect(Collectors.toMap(AuthRole::getName, item -> item, (s1,s2) -> s1));

        users.forEach(item -> {
            String roleNames = item.getRoleName();
            String[] split = roleNames.split(",");
            for (String role : split) {
                AuthRole authRole = authRoleMap.get(role.trim());
                if (authRole == null){
                    roleNameError.append("Excel中登录名/工号:");
                    roleNameError.append(item.getLoginName()).append("/").append(item.getWorkNo()).append("对应角色栏中");
                    roleNameError.append("[").append(role).append("]在系统中不存在</br>");
                }
            }
        });

        if (roleNameError.length() > 0){
            throw new RuntimeException(roleNameError.toString());
        }
    }

    private void checkImportRepeatFields(List<UserImportDto> users) {
        StringBuilder repeatError = new StringBuilder();

        List<AuthUser> authUsers = authUserService.getBaseMapper().selectList(new LambdaQueryWrapper<>());

        //检查excel中是否存在重复字段
        Map<String, Integer> loginNameDtoMap = new HashMap<>();
        Map<String, Integer> workNoDtoMap = new HashMap<>();
        users.forEach(item -> {
            loginNameDtoMap.put(item.getLoginName(), loginNameDtoMap.getOrDefault(item.getLoginName(), 0) + 1);
        });
        users.forEach(item -> {
            workNoDtoMap.put(item.getWorkNo(), workNoDtoMap.getOrDefault(item.getWorkNo(), 0) + 1);
        });
        List<String> loginNameDtos = loginNameDtoMap.entrySet().stream().filter(item -> item.getValue() > 1).map(Map.Entry::getKey).collect(Collectors.toList());
        List<String> workNoDtos = workNoDtoMap.entrySet().stream().filter(item -> item.getValue() > 1).map(Map.Entry::getKey).collect(Collectors.toList());
        if (loginNameDtos.size() > 0) {
            repeatError.append("登录名:");
            repeatError.append(loginNameDtos.toString());
            repeatError.append("在Excel中存在多行</br>");
        }
        if (workNoDtos.size() > 0) {
            repeatError.append("工号:");
            repeatError.append(workNoDtos.toString());
            repeatError.append("在Excel中存在多行</br>");
        }


        //检查数据库中是否存在重复字段
        Map<String, AuthUser> loginNameMap = authUsers.stream().collect(Collectors.toMap(AuthUser::getLoginName, item -> item, (s1,s2) -> s1));
        Map<String, AuthUser> workNoMap = authUsers.stream().collect(Collectors.toMap(AuthUser::getWorkNo, item -> item, (s1,s2) -> s1));
        for (UserImportDto user : users) {
            AuthUser authUser1 = loginNameMap.get(user.getLoginName());
            if (authUser1 != null) {
                repeatError.append("登录名:");
                repeatError.append(user.getLoginName());
                repeatError.append("在系统中已存在</br>");
            }
            AuthUser authUser2 = workNoMap.get(user.getWorkNo());
            if (authUser2 != null) {
                repeatError.append("工号:");
                repeatError.append(user.getWorkNo());
                repeatError.append("在系统中已存在</br>");
            }
        }

        if (repeatError.length() > 0){
            throw new RuntimeException(repeatError.toString());
        }

    }

    private void checkImportRequiredFields(List<UserImportDto> users) {
        Map<Integer, String> map = new HashMap<>();
        map.put(1, "存在有[登录名]未填的一行</br>");
        map.put(2, "存在有[密码]未填的一行</br>");
        map.put(3, "存在有[真实姓名]未填的一行</br>");
        map.put(4, "存在有[工号]未填的一行</br>");

        Set<String> set = new HashSet<>();
        for (UserImportDto user : users) {
            if (StringUtils.isBlank(user.getLoginName())){
                set.add(map.get(1));
            }

            if (StringUtils.isBlank(user.getPassword())){
                set.add(map.get(2));
            }

            if (StringUtils.isBlank(user.getName())){
                set.add(map.get(3));
            }

            if (StringUtils.isBlank(user.getWorkNo())){
                set.add(map.get(4));
            }
        }

        if (set.size() > 0){
            throw new RuntimeException(String.join(",", set));
        }
    }

    private String cacheUser(AuthUser user, String clientType, String teamGroup, String classNo) {
        String token = UUID.randomUUID().toString();
        Map<String, Object> userinfo = new HashMap<>();
        userinfo.put(TOKEN, token);
        userinfo.put(USER_ID, user.getId() + "");
        userinfo.put(USER_NAME, user.getRealName());
        userinfo.put(USER_TEAM_GROUP, teamGroup);
        userinfo.put(USER_CLASS_NO, classNo);
        //角色id集合
        List<AuthRole> roles = authRoleService.getByUserId(user.getId());
        userinfo.put(USER_ROLES, String.join(",", roles.stream().map(role -> role.getId().toString()).collect(Collectors.toList())));

        //权限id集合
        List<AuthPermission> permissions = authPermissionService.getByUserId(user.getId());
        userinfo.put(USER_PERMISSIONS, String.join(",", permissions.stream().map(permission -> permission.getId().toString()).collect(Collectors.toList())));
        String userinfostr = JSON.toJSONString(userinfo);
//        userinfo.put(USER_PERMISSION_CODES, String.join(",", permissions.stream().map(permission -> permission.getCode()).collect(Collectors.toList())));
//        //推送权限集合
//        List<Permission> pushPermissions = permissions.stream().filter(permission -> permission.getType().equals(PUSH_PERMISSION)).collect(Collectors.toList());
//        userinfo.put(PUSH_TOPICS, String.join(",", pushPermissions.stream().map(permission -> permission.getCode()).collect(Collectors.toSet())));
        if (StringUtils.isBlank(clientType)) {
            clientType = "web";
        }
        String md5Password = DigestUtils.md5DigestAsHex(("user_id" + user.getId()).getBytes());
        String tokenPrefix = TOKEN_PREFIX + clientType + "_" + md5Password + "_";
        if(clientType.equals("web")){
            String delTokenPrefix= TOKEN_PREFIX + "web" + "_" + md5Password + "_";
            Set<String> keys = redisTemplate.keys(TOKEN_PREFIX + delTokenPrefix + "*");
            //找到历史token
            if (keys != null && keys.size() > 0) {
                for (String historyTokenKeys : keys) {
                    String historyToken = NumberUtil.object2String(historyTokenKeys, null);
                    redisTemplate.delete(historyToken);
                }
            }
        }
        token = tokenPrefix + token;
        redisTemplate.opsForValue().set(TOKEN_PREFIX + token, userinfostr, timeout, TimeUnit.SECONDS);
        return token;
    }
}




