package com.lutao.cloud.admin.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
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.lutao.cloud.admin.api.dto.ForgetPwdDTO;
import com.lutao.cloud.admin.api.dto.RegisterDTO;
import com.lutao.cloud.admin.api.dto.UserDTO;
import com.lutao.cloud.admin.api.dto.UserInfo;
import com.lutao.cloud.admin.api.entity.*;
import com.lutao.cloud.admin.api.feign.RemoteTokenService;
import com.lutao.cloud.admin.api.vo.UserByIdVO;
import com.lutao.cloud.admin.api.vo.UserExcelVO;
import com.lutao.cloud.admin.api.vo.UserPageVo;
import com.lutao.cloud.admin.api.vo.UserVO;
import com.lutao.cloud.admin.mapper.SysUserMapper;
import com.lutao.cloud.admin.mq.UserMqMessage;
import com.lutao.cloud.admin.service.*;
import com.lutao.cloud.common.core.constant.BusinessConstants;
import com.lutao.cloud.common.core.constant.CacheConstants;
import com.lutao.cloud.common.core.constant.CommonConstants;
import com.lutao.cloud.common.core.constant.SecurityConstants;
import com.lutao.cloud.common.core.util.KeyStrResolver;
import com.lutao.cloud.common.core.util.MessageInfo;
import com.lutao.cloud.common.data.datascope.DataScope;
import com.lutao.cloud.common.data.tenant.TenantContextHolder;
import com.lutao.cloud.common.excel.vo.ErrorMessage;
import com.lutao.cloud.common.log.annotation.SysLog;
//import com.lutao.cloud.common.security.password.PasswordEncoderSha;
import com.lutao.cloud.common.security.password.PasswordEncoderSha;
import com.lutao.cloud.common.security.service.CloudCustomTokenServices;
import com.lutao.cloud.common.security.service.CloudUser;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.data.redis.core.BoundValueOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2RefreshToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.BindingResult;
import com.lutao.cloud.common.security.util.SecurityUtils;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**

 * @date 2017/10/31
 */
@ApiModel(description = "")
@Slf4j
@Service
@AllArgsConstructor
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements SysUserService{

//	private static final PasswordEncoder ENCODER = new PasswordEncoderSha();
	@ApiModelProperty("private static final PasswordEncoder ENCODER = new PasswordEncoderSha();")
	private static final PasswordEncoder ENCODER = new PasswordEncoderSha();

	private final SysMenuService sysMenuService;

	private final SysRoleService sysRoleService;

	private final SysDeptService sysDeptService;

	private final SysUserRoleService sysUserRoleService;

	private final SysDeptRelationService sysDeptRelationService;

	private final RemoteTokenService remoteTokenService;

	private final SysDictItemService sysDictItemService;

	private final UserMqMessage userMqMessage;

	private final SysCompanyService sysCompanyService;

	private final MobileService mobileService;

	private final RedisTemplate redisTemplate;

	private final KeyStrResolver keyStrResolver;

	private final TokenStore tokenStore;

	private final CacheManager cacheManager;


	/**
	 * 保存用户信息
	 * @param userDto DTO 对象
	 * @return success/fail
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public MessageInfo<Boolean> saveUser(UserDTO userDto){

		// TODO: 2021/11/23 对租户有效期和租户用户数限制
		int tenantId = TenantContextHolder.getTenantId();
////		 查询租户信息
//		SysTenant sysTenant = sysTenantMapper.selectById(tenantId);
//		if (sysTenant.getEndTime().isBefore(LocalDateTime.now())){
//			return MessageInfo.failed("租户已到期");
//		}

		SysUser sysUser = new SysUser();
		BeanUtils.copyProperties(userDto, sysUser);
		sysUser.setDelFlag(CommonConstants.STATUS_NORMAL);
		if (StrUtil.isNotBlank(userDto.getPassword())) {
			sysUser.setPassword(ENCODER.encode(userDto.getPassword()));
		} else {
			sysUser.setPassword(ENCODER.encode("aky!@#q"));
		}
		sysUser.setNickname(StrUtil.isBlank(userDto.getNickname()) ? userDto.getUsername() : userDto.getNickname());

		baseMapper.insert(sysUser);
		List<SysUserRole> userRoleList = userDto.getRole().stream().map(roleId ->{
			SysUserRole userRole = new SysUserRole();
			userRole.setUserId(sysUser.getUserId());
			userRole.setRoleId(roleId);
			return userRole;
		}).collect(Collectors.toList());
		userMqMessage.sendMessage(sysUser,UserMqMessage.USER_ADD);
		return MessageInfo.ok(sysUserRoleService.saveBatch(userRoleList));
	}

	@Override
	public UserVO findUserPermission(String username) {
		UserVO sysUser = baseMapper.getUserPermission(username);
		if (sysUser==null){
			return null;
		}
		return sysUser;
	}

	/**
	 * 通过查用户的全部信息
	 * @param sysUser 用户
	 * @return
	 */
	@Override
	public UserInfo findUserInfo(SysUser sysUser){

		UserInfo userInfo = new UserInfo();
		userInfo.setSysUser(sysUser);
		// 设置角色列表 （ID）
		List<Integer> roleIds = sysRoleService.findRolesByUserId(sysUser.getUserId()).stream().map(SysRole::getRoleId)
				.collect(Collectors.toList());
		userInfo.setRoles(ArrayUtil.toArray(roleIds, Integer.class));

		// 设置权限列表（menu.permission）
		Set<String> permissions = new HashSet<>();
		roleIds.forEach(roleId ->{
			List<String> permissionList = sysMenuService.findMenuByRoleId(roleId).stream()
					.filter(menu -> StrUtil.isNotEmpty(menu.getPermission())).map(SysMenu::getPermission)
					.collect(Collectors.toList());
			permissions.addAll(permissionList);
		});

		// 设置职位信息

		userInfo.setPermissions(ArrayUtil.toArray(permissions, String.class));

		return userInfo;
	}

	/**
	 * 分页查询用户信息（含有角色信息）
	 * @param page 分页对象
	 * @param userDTO 参数列表
	 * @return
	 */
	@Override
	public IPage getUsersWithRolePage(Page page, UserDTO userDTO){
		return baseMapper.getUserVosPage(page, userDTO, new DataScope());
	}

	/**
	 * 通过ID查询用户信息
	 * @param id 用户ID
	 * @return 用户信息
	 */
	@Override
	public UserVO selectUserVoById(Integer id){
		UserVO vo = baseMapper.getUserVoById(id);
		return vo;
	}

	/**
	 * 通过用户id查询用户信息和部门信息
	 * @return
	 */
	@Override
	public UserByIdVO selectPerson() {
		CloudUser cloudUser = SecurityUtils.getUser();
		Integer id = cloudUser.getId();
		log.info("userId:{}",id);
		return baseMapper.getPersonInfo(id);
	}

	@Override
	public MessageInfo<IPage<UserPageVo>> getSelectAll(Page page) {
		return MessageInfo.ok(baseMapper.selectUserAll(page));
	}

	/**
	 * 删除用户
	 * @param sysUser 用户
	 * @return Boolean
	 */
	@Override
	@CacheEvict(value = CacheConstants.USER_DETAILS, key = "#sysUser.username")
	public Boolean deleteUserById(SysUser sysUser){
		sysUser = baseMapper.selectById(sysUser.getUserId());
		sysUserRoleService.deleteByUserId(sysUser.getUserId());
		this.removeById(sysUser.getUserId());
		MessageInfo<Boolean> delRet = remoteTokenService.delTokenByUsername(sysUser.getUsername(),SecurityConstants.FROM_IN);

		userMqMessage.sendMessage(sysUser,UserMqMessage.USER_DELETE);


		return Boolean.TRUE;
	}




	@Override
	@CacheEvict(value = CacheConstants.USER_DETAILS, key = "#userDto.username")
	@SysLog("更新用户信息")
	public MessageInfo<Boolean> updateUserInfo(UserDTO userDto){
		CloudUser cloudUser = SecurityUtils.getUser();
		UserVO userVO = baseMapper.getUserVoByUsername(cloudUser.getUsername());
		if (!ENCODER.matches(userDto.getPassword(), userVO.getPassword())){
			log.info("原密码错误，修改个人信息失败:{}", userDto.getUsername());
			return MessageInfo.failed("原密码错误，修改个人信息失败");
		}

		SysUser sysUser = new SysUser();
		if (StrUtil.isNotBlank(userDto.getNewpassword1())){
			String newPassword = ENCODER.encode(userDto.getNewpassword1());
			if (userVO.getPassword().equals(newPassword)){
				return MessageInfo.failed("新密码和旧密码相同，修改个人信息失败");
			}
			sysUser.setPassword(newPassword);
		}
		if (StrUtil.isNotBlank(userDto.getPhone())){
			sysUser.setPhone(userDto.getPhone());
		}
		if (StrUtil.isNotBlank(userDto.getAvatar())){
			sysUser.setAvatar(userDto.getAvatar());
		}
		sysUser.setUserId(userVO.getUserId());

		if (StrUtil.isNotBlank(userDto.getEmail())){
			sysUser.setEmail(userDto.getEmail());
		}

		this.updateById(sysUser);

		userMqMessage.sendMessage(sysUser,UserMqMessage.USER_UPDATE);

		return MessageInfo.ok(Boolean.TRUE);
	}

	/**
	 * 更新用户
	 * 同时更新缓存里的信息
	 * @param userDto 用户信息
	 * @return
	 */
	@Override
	@CacheEvict(value = CacheConstants.USER_DETAILS, key = "#userDto.username")
	@SysLog("更新用户")
	public Boolean updateUser(UserDTO userDto){
		SysUser sysUser = new SysUser();
		BeanUtils.copyProperties(userDto, sysUser);
		sysUser.setUpdateTime(LocalDateTime.now());
		SysUser sysUserVo = baseMapper.selectById(userDto.getUserId());
		if (StrUtil.isNotBlank(userDto.getPassword())){
			sysUser.setPassword(ENCODER.encode(userDto.getPassword()));
		} else{
			sysUser.setPassword(sysUserVo.getPassword());
		}

		this.updateById(sysUser);

		if (userDto.getRole() != null) {
			sysUserRoleService
					.remove(Wrappers.<SysUserRole>update().lambda().eq(SysUserRole::getUserId, userDto.getUserId()));
			userDto.getRole().forEach(roleId ->{
				SysUserRole userRole = new SysUserRole();
				userRole.setUserId(sysUser.getUserId());
				userRole.setRoleId(roleId);
				userRole.insert();
			});
		}
		userMqMessage.sendMessage(sysUser,UserMqMessage.USER_DELETE);
		return Boolean.TRUE;
	}

	/**
	 * 查询上级部门的用户信息
	 * @param username 用户名
	 * @return R
	 */
	@Override
	public List<SysUser> listAncestorUsers(String username){
		SysUser sysUser = this.getOne(Wrappers.<SysUser>query().lambda().eq(SysUser::getUsername, username));

		SysDept sysDept = sysDeptService.getById(sysUser.getDeptId());
		if (sysDept == null){
			return null;
		}

		Integer parentId = sysDept.getParentId();
		return this.list(Wrappers.<SysUser>query().lambda().eq(SysUser::getDeptId, parentId));
	}

	/**
	 * 获取当前用户的子部门信息
	 * @return 子部门列表
	 */
	private List<Integer> getChildDepts(){
		Integer deptId = SecurityUtils.getUser().getDeptId();
		// 获取当前部门的子部门
		return sysDeptRelationService
				.list(Wrappers.<SysDeptRelation>query().lambda().eq(SysDeptRelation::getAncestor, deptId)).stream()
				.map(SysDeptRelation::getDescendant).collect(Collectors.toList());
	}


	/**
	 * 查询全部的用户
	 *
	 * @param userDTO 查询条件
	 * @return list
	 */
	@Override
	public List<UserExcelVO> listUser(UserDTO userDTO){
		// 根据数据权限查询全部的用户信息
		List<UserVO> voList = baseMapper.selectVoListByScope(userDTO, new DataScope());
		// 转换成execl 对象输出
		List<UserExcelVO> userExcelVOList = voList.stream().map(userVO ->{
			UserExcelVO excelVO = new UserExcelVO();
			BeanUtils.copyProperties(userVO, excelVO);
			String roleNameList = userVO.getRoleList().stream().map(SysRole::getRoleName)
					.collect(Collectors.joining(StrUtil.COMMA));
			excelVO.setRoleNameList(roleNameList);
			return excelVO;
		}).collect(Collectors.toList());
		return userExcelVOList;
	}

	/**
	 * excel 导入用户
	 *
	 * @param excelVOList   excel 列表数据
	 * @param bindingResult 错误数据
	 * @return ok fail
	 */
	@Override
	public MessageInfo importUser(List<UserExcelVO> excelVOList, BindingResult bindingResult){
		// 通用校验获取失败的数据
		List<ErrorMessage> errorMessageList = (List<ErrorMessage>) bindingResult.getTarget();

		// 个性化校验逻辑
		List<SysUser> userList = this.list();
		List<SysDept> deptList = sysDeptService.list();
		List<SysRole> roleList = sysRoleService.list();

		// 执行数据插入操作 组装 UserDto
		for (int i = 0; i < excelVOList.size(); i++){
			UserExcelVO excel = excelVOList.get(i);
			Set<String> errorMsg = new HashSet<>();
			// 校验用户名是否存在
			boolean exsitUserName = userList.stream()
					.anyMatch(sysUser -> excel.getUsername().equals(sysUser.getUsername()));

			if (exsitUserName){
				errorMsg.add(String.format("%s 用户名已存在", excel.getUsername()));
			}

			// 判断输入的部门名称列表是否合法
			Optional<SysDept> deptOptional = deptList.stream()
					.filter(dept -> excel.getDeptName().equals(dept.getName())).findFirst();
			if (!deptOptional.isPresent()){
				errorMsg.add(String.format("%s 部门名称不存在", excel.getDeptName()));
			}

			// 判断输入的角色名称列表是否合法
			List<String> roleNameList = StrUtil.split(excel.getRoleNameList(), StrUtil.COMMA);
			List<SysRole> roleCollList = roleList.stream()
					.filter(role -> roleNameList.stream().anyMatch(name -> role.getRoleName().equals(name)))
					.collect(Collectors.toList());

			if (roleCollList.size() != roleNameList.size()){
				errorMsg.add(String.format("%s 角色名称不存在", excel.getRoleNameList()));
			}

			// 数据合法情况
			if (CollUtil.isEmpty(errorMsg)){
				insertExcelUser(excel, deptOptional, roleCollList);
			}
			else{
				// 数据不合法情况
				errorMessageList.add(new ErrorMessage((long) (i + 2), errorMsg));
			}

		}

		if (CollUtil.isNotEmpty(errorMessageList)){
			return MessageInfo.failed(errorMessageList);
		}
		return MessageInfo.ok();
	}


	/**
	 * 插入excel User
	 * @param excel
	 * @param deptOptional
	 * @param roleCollList
	 */
	private void insertExcelUser(UserExcelVO excel, Optional<SysDept> deptOptional, List<SysRole> roleCollList){
		UserDTO userDTO = new UserDTO();
		userDTO.setUsername(excel.getUsername());
		userDTO.setPhone(excel.getPhone());
		// 批量导入初始密码为手机号
		userDTO.setPassword(userDTO.getPhone());
		// 根据部门名称查询部门ID
		userDTO.setDeptId(deptOptional.get().getDeptId());
		// 根据角色名称查询角色ID
		List<Integer> roleIdList = roleCollList.stream().map(SysRole::getRoleId).collect(Collectors.toList());
		userDTO.setRole(roleIdList);
		// 插入用户
		this.saveUser(userDTO);
	}

	/**
	 * 用户注册
	 * @param registerDTO DTO 对象
	 * @return
	 */
	@Override
	public MessageInfo<Integer> register(RegisterDTO registerDTO){

		int count = count(Wrappers.<SysUser>lambdaQuery().eq(SysUser::getUsername,registerDTO.getUsername()));
		if (count > 0){
			return MessageInfo.failed("用户名已存在");
		}

		SysDictItem deptDict = sysDictItemService.getOne(Wrappers.<SysDictItem>lambdaQuery().eq(SysDictItem::getType,CommonConstants.REGISTER_DEPT_ID));
		if (deptDict == null){
			return MessageInfo.failed("注册用户部门没有初始化");
		}
		SysUser sysUser = new SysUser();
		BeanUtils.copyProperties(registerDTO, sysUser);
		sysUser.setDelFlag(CommonConstants.STATUS_NORMAL);
		sysUser.setPassword(ENCODER.encode(registerDTO.getPassword()));
		sysUser.setUserFrom(BusinessConstants.USER_FROM_GENERAL);
		sysUser.setNickname(StrUtil.isBlank(registerDTO.getNickname()) ? registerDTO.getUsername() : registerDTO.getNickname());
		sysUser.setDeptId(Integer.parseInt(deptDict.getValue()));
		SysDictItem sysDictItem = sysDictItemService.getOne(Wrappers.<SysDictItem>lambdaQuery().eq(SysDictItem::getType,CommonConstants.REGISTER_ROLE_TYPE));
		if (sysDictItem == null){
			return MessageInfo.failed("角色初始化错误");
		}

		if (StrUtil.isNotBlank(registerDTO.getCompany())) {
			if (StrUtil.isAllNotBlank(registerDTO.getCompanyCode(),registerDTO.getCompanyType())) {
				// 添加企业信息
				SysCompany company = new SysCompany();
				company.setCompanyCode(registerDTO.getCompanyCode());
				company.setCompanyName(registerDTO.getCompany());
				company.setCompanyType(registerDTO.getCompanyType());
				sysCompanyService.save(company);
				sysUser.setCompanyId(company.getId());
				sysUser.setUserFrom(BusinessConstants.USER_FROM_COMPANY);
			} else {
				return MessageInfo.failed("请完善企业信息");
			}
		}
		baseMapper.insert(sysUser);
		SysUserRole userRole = new SysUserRole();
		userRole.setUserId(sysUser.getUserId());
		userRole.setRoleId(Integer.parseInt(sysDictItem.getValue()));
		sysUserRoleService.save(userRole);
		return MessageInfo.ok(sysUser.getUserId());
	}


	/**
	 * 忘记密码
	 * @param forgetPwdDTO
	 * @return
	 */
	@Override
	public MessageInfo forgetPwd(ForgetPwdDTO forgetPwdDTO) {
		if (!forgetPwdDTO.getNewpassword().equals(forgetPwdDTO.getNewpassword1())) {
			return MessageInfo.failed("两次密码输入不一致！");
		}
		SysUser sysUser = getOne(Wrappers.<SysUser>lambdaQuery()
				.eq(SysUser::getUsername,forgetPwdDTO.getUsername())
				.eq(SysUser::getDelFlag,0));
		if (sysUser ==  null) {
			return MessageInfo.failed("用户不存在");
		}
		if (!forgetPwdDTO.getPhone().equals(sysUser.getPhone())) {
			return MessageInfo.failed("手机号和用户绑定的手机不一致");
		}
		String password = ENCODER.encode(forgetPwdDTO.getNewpassword());
		// 校验验证码
		String key = mobileService.getVerificodeKey(forgetPwdDTO.getPhone(),null);
		BoundValueOperations boundValueOperations = redisTemplate.boundValueOps(key);
		// 先删除token
		String cacheCode = boundValueOperations.get() + "";
		if (!cacheCode.equals(forgetPwdDTO.getCheckCode())) {
			return MessageInfo.failed("验证码错误");
		}
		// 删除缓存
		String cacheKey = keyStrResolver.extract(
				SecurityConstants.PIGX_PREFIX + SecurityConstants.OAUTH_PREFIX + "uname_to_access_z:", StrUtil.COLON);

		String detailsKey = String.format("%s:user_details::%s",1,forgetPwdDTO.getUsername());
		redisTemplate.delete(detailsKey);

		// 2.获取tokenList
		List collect = (List)redisTemplate.keys("*:" + forgetPwdDTO.getUsername()).stream().filter(k -> ((String) k).contains(cacheKey))
				.flatMap(k ->{
					redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
					return redisTemplate.opsForZSet().range(k, 0, System.currentTimeMillis()).stream();
				}).collect(Collectors.toList());

		if (collect != null){
			collect.forEach( token ->{
				JSONObject jsonObject = JSONUtil.parseObj(token);
				OAuth2AccessToken accessToken = tokenStore.readAccessToken(jsonObject.getStr("value"));
				if (accessToken == null || StrUtil.isBlank(accessToken.getValue())){
					return;
				}

				OAuth2Authentication auth2Authentication = tokenStore.readAuthentication(accessToken);
				// 清空用户信息
				cacheManager.getCache(CacheConstants.USER_DETAILS).evict(auth2Authentication.getName());

				// 清空access token
				tokenStore.removeAccessToken(accessToken);

				// 清空 refresh token
				OAuth2RefreshToken refreshToken = accessToken.getRefreshToken();
				if (refreshToken != null){
					tokenStore.removeRefreshToken(refreshToken);
				}
			});
		}
		// 修改密码
		sysUser.setPassword(password);
		updateById(sysUser);
		return MessageInfo.ok();
	}

}
