/*
 *  Copyright 2019-2020 Zheng Jie
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package org.nl.system.service.secutiry.impl;

import cn.dev33.satoken.secure.SaSecureUtil;
import cn.dev33.satoken.stp.SaLoginModel;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.nl.common.utils.*;
import org.nl.config.RsaProperties;
import org.nl.common.exception.BadRequestException;
import org.nl.common.utils.dto.CurrentUser;
import org.nl.system.service.secutiry.dto.UserDto;
import org.nl.system.service.role.ISysRoleService;
import org.nl.system.service.secutiry.dto.AuthUserDto;
import org.nl.system.service.user.ISysUserService;
import org.nl.system.service.user.dao.SysUser;
import org.nl.system.service.user.dto.OnlineUserDto;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Pageable;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;

/**
 * @author Zheng Jie
 * @date 2019年10月26日21:56:27
 */
@Service
@Slf4j
public class OnlineUserService {

    @Autowired
    private ISysUserService sysUserService;
    @Autowired
    private ISysRoleService roleService;
    @Autowired
    private RedisUtils redisUtils;

    public OnlineUserService(RedisUtils redisUtils) {
        this.redisUtils = redisUtils;
    }

    /**
     * 保存在线用户信息
     * @param userDto /
     * @param token /
     * @param request /
     */
    public void save(UserDto userDto, String token, HttpServletRequest request){
//        String dept = userDto.getDept().getName();
        String dept = "";
        String ip = StringUtils.getIp(request);
        String browser = StringUtils.getBrowser(request);
        // String address = StringUtils.getCityInfo(ip);
        String address = "局域网";
        OnlineUserDto onlineUserDto = null;
        try {
//            onlineUserDto = new OnlineUserDto(userDto.getUsername(), userDto.getNickName(), dept, browser , ip, address, EncryptUtils.desEncrypt(token), new Date());
        } catch (Exception e) {
            log.error(e.getMessage(),e);
        }
        redisUtils.set(token, onlineUserDto, StpUtil.getTokenTimeout());
    }

    /**
     * 查询全部数据
     * @param filter /
     * @param pageable /
     * @return /
     */
    public Map<String,Object> getAll(String filter, Pageable pageable){
        List<OnlineUserDto> onlineUserDtos = getAll(filter);
        return PageUtil.toPage(
                PageUtil.toPage(pageable.getPageNumber(),pageable.getPageSize(), onlineUserDtos),
                onlineUserDtos.size()
        );
    }

    /**
     * 查询全部数据，不分页
     * @param filter /
     * @return /
     */
    public List<OnlineUserDto> getAll(String filter){
        List<String> keys = redisUtils.scan("*");
        Collections.reverse(keys);
        List<OnlineUserDto> onlineUserDtos = new ArrayList<>();
        for (String key : keys) {
            if (key.length() == 1511) {
                OnlineUserDto onlineUserDto = (OnlineUserDto) redisUtils.get(key);
                if(StrUtil.isNotEmpty(filter)){
                    if(onlineUserDto.toString().contains(filter)){
                        onlineUserDtos.add(onlineUserDto);
                    }
                } else {
                    onlineUserDtos.add(onlineUserDto);
                }
            }

        }
        onlineUserDtos.sort((o1, o2) -> o2.getLoginTime().compareTo(o1.getLoginTime()));
        return onlineUserDtos;
    }

    /**
     * 踢出用户
     * @param key /
     */
    public void kickOut(String key){
        redisUtils.del(key);
    }

    /**
     * 退出登录
     * @param token /
     */
    public void logout(String token) {
        redisUtils.del(token);
    }

    /**
     * 导出
     * @param all /
     * @param response /
     * @throws IOException /
     */
    public void download(List<OnlineUserDto> all, HttpServletResponse response) throws IOException {
        List<Map<String, Object>> list = new ArrayList<>();
        for (OnlineUserDto user : all) {
            Map<String,Object> map = new LinkedHashMap<>();
            map.put("用户名", user.getUserName());
            map.put("部门", user.getDept());
            map.put("登录IP", user.getIp());
            map.put("登录地点", user.getAddress());
            map.put("浏览器", user.getBrowser());
            map.put("登录日期", user.getLoginTime());
            list.add(map);
        }
        FileUtil.downloadExcel(list, response);
    }

    /**
     * 查询用户
     * @param key /
     * @return /
     */
    public OnlineUserDto getOne(String key) {
        return (OnlineUserDto)redisUtils.get(key);
    }

    /**
     * 检测用户是否在之前已经登录，已经登录踢下线
     * @param userName 用户名
     */
    public void checkLoginOnUser(String userName, String igoreToken){
        List<OnlineUserDto> onlineUserDtos = getAll(userName);
        if(onlineUserDtos ==null || onlineUserDtos.isEmpty()){
            return;
        }
        for(OnlineUserDto onlineUserDto : onlineUserDtos){
            if(onlineUserDto.getUserName().equals(userName)){
                try {
                    String token =EncryptUtils.desDecrypt(onlineUserDto.getKey());
                    if(StrUtil.isNotEmpty(igoreToken)&&!igoreToken.equals(token)){
                        this.kickOut(token);
                    }else if(StrUtil.isEmpty(igoreToken)){
                        this.kickOut(token);
                    }
                } catch (Exception e) {
                    log.error("checkUser is error",e);
                }
            }
        }
    }

    /**
     * 根据用户名强退用户
     * @param username /
     */
    @Async
    public void kickOutForUsername(String username) throws Exception {
        List<OnlineUserDto> onlineUsers = getAll(username);
        for (OnlineUserDto onlineUser : onlineUsers) {
            if (onlineUser.getUserName().equals(username)) {
                String token = EncryptUtils.desDecrypt(onlineUser.getKey());
                kickOut(token);
            }
        }
    }
    @SneakyThrows
    public Map<String, Object>  login(Map paramMap){
        // 密码解密 - 前端的加密规则: encrypt
        AuthUserDto authUser = JSON.toJavaObject((JSON) JSON.toJSON(paramMap), AuthUserDto.class);
        String password = RsaUtils.decryptByPrivateKey(RsaProperties.privateKey, authUser.getPassword());
        // 查询验证码
        String code = (String) redisUtils.get(authUser.getUuid());
        // 清除验证码
        redisUtils.del(authUser.getUuid());
        if (StrUtil.isEmpty(code)) {
            throw new BadRequestException("验证码不存在或已过期");
        }
        if (StrUtil.isEmpty(authUser.getCode()) || !authUser.getCode().equalsIgnoreCase(code)) {
            throw new BadRequestException("验证码错误");
        }
        // 校验数据库
        // 根据用户名查询，在比对密码
        SysUser userInfo = sysUserService.getOne(new QueryWrapper<SysUser>().eq("username",authUser.getUsername()));
        if (userInfo == null||!userInfo.getPassword().equals(SaSecureUtil.md5BySalt(password, "salt"))) { // 这里需要密码加密
            throw new BadRequestException("账号或密码错误");
        }

        // 获取权限列表 - 登录查找权限
        List<String> permissionList = roleService.getPermissionList((JSONObject) JSON.toJSON(userInfo));


        if (!userInfo.getIs_used()) {
            throw new BadRequestException("账号未激活");
        }

        // 登录输入，登出删除
        CurrentUser user = new CurrentUser();
        user.setId(userInfo.getUser_id());
        user.setUsername(userInfo.getUsername());
        user.setPresonName((userInfo.getPerson_name()));
        user.setUser(userInfo);
        user.setPermissions(permissionList);

        // SaLoginModel 配置登录相关参数
        StpUtil.login(userInfo.getUser_id(), new SaLoginModel()
                .setDevice("PC")                // 此次登录的客户端设备类型, 用于[同端互斥登录]时指定此次登录的设备类型
                .setExtra("loginInfo", user)    // Token挂载的扩展参数 （此方法只有在集成jwt插件时才会生效）
        );

        // 返回 token 与 用户信息
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("roles", permissionList);
        jsonObject.put("user", userInfo);
        Map<String, Object> authInfo = new HashMap<String, Object>(2) {{
            put("token", "Bearer " + StpUtil.getTokenValue());
            put("user", user);
        }};
        // 保存在线信息
//        onlineUserService.save(userDto, StpUtil.getTokenValue(), request);
        return authInfo;
    }
}
