/*
 *  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 admin.rest;

import admin.annotation.Log;
import admin.annotation.rest.AnonymousDeleteMapping;
import admin.annotation.rest.AnonymousGetMapping;
import admin.annotation.rest.AnonymousPostMapping;

import admin.config.RedisCacheConfigPath;
import admin.config.RsaProperties;

import admin.exception.BadRequestException;
import admin.model.syncToken.GetTokenRep;
import admin.model.syncToken.VerifyRep;
import admin.modules.security.config.bean.LoginProperties;
import admin.modules.security.config.bean.SecurityProperties;
import admin.modules.security.security.TokenProvider;
import admin.modules.security.service.OnlineUserService;
import admin.modules.security.service.dto.AuthUserDto;
import admin.modules.security.service.dto.JwtUserDto;
import admin.modules.system.service.UserService;
import admin.modules.system.service.dto.UserDto;
import admin.service.syncToken.SyncTokenService;
import admin.utils.RedisUtils;
import admin.utils.RsaUtils;
import admin.utils.SecurityUtils;
import admin.utils.StringUtils;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONObject;
import com.wf.captcha.base.Captcha;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @date 2018-11-23
 * 授权、根据token获取用户详细信息
 */
@Slf4j
@RestController
@RequestMapping("/auth")
@RequiredArgsConstructor
@Api(tags = "系统：系统授权接口")
public class AuthorizationController {
    private final SecurityProperties properties;
    private final RedisUtils redisUtils;
    private final OnlineUserService onlineUserService;
    private final TokenProvider tokenProvider;
    private final AuthenticationManagerBuilder authenticationManagerBuilder;
    @Autowired
    private final SyncTokenService syncTokenService;
    private final UserService userService;
    @Resource
    private LoginProperties loginProperties;

    @Log("用户登录")
    @ApiOperation("登录授权")
    @AnonymousPostMapping(value = "/login")
    public ResponseEntity<Object> login(@Validated @RequestBody AuthUserDto authUser, HttpServletRequest request) throws Exception {
        // 密码解密
        String password = RsaUtils.decryptByPrivateKey(RsaProperties.privateKey, authUser.getPassword());
        // 查询验证码
        String code = (String) redisUtils.get(authUser.getUuid());
        // 清除验证码
        redisUtils.del(authUser.getUuid());
        if (StringUtils.isBlank(code)) {
            throw new BadRequestException("验证码不存在或已过期");
        }
        if (StringUtils.isBlank(authUser.getCode()) || !authUser.getCode().equalsIgnoreCase(code)) {
            throw new BadRequestException("验证码错误");
        }
        UsernamePasswordAuthenticationToken authenticationToken =
                new UsernamePasswordAuthenticationToken(authUser.getUsername(), password);
        Authentication authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken);
        SecurityContextHolder.getContext().setAuthentication(authentication);
        // 生成令牌
        String token = tokenProvider.createToken(authentication);
        final JwtUserDto jwtUserDto = (JwtUserDto) authentication.getPrincipal();
        // 保存在线信息
        onlineUserService.save(jwtUserDto, token, request);
        // 返回 token 与 用户信息
        Map<String, Object> authInfo = new HashMap<String, Object>(2) {{
            put("token", properties.getTokenStartWith() + token);
            put("user", jwtUserDto);
        }};
        if (loginProperties.isSingleLogin()) {
            //踢掉之前已经登录的token
            onlineUserService.checkLoginOnUser(authUser.getUsername(), token);
        }
        //记录登录用户信息
        redisUtils.del(String.format(RedisCacheConfigPath.loginUserCacheKey,jwtUserDto.getUsername()));
        redisUtils.set(String.format(RedisCacheConfigPath.loginUserCacheKey,jwtUserDto.getUsername()),JSONObject.toJSONString(jwtUserDto),2,TimeUnit.HOURS);
        return ResponseEntity.ok(authInfo);
    }

    @Log("门户跳转")
    @ApiOperation("门户跳转")
    @AnonymousGetMapping(value = "/jump")
    public ResponseEntity<Object> jump(@Validated @RequestParam String token, HttpServletRequest request, HttpServletResponse httpServletResponse) throws Exception{
        //获取token,先走缓存，缓存没值再查询
        log.info("获取token,先走缓存，缓存没值再查询");
        String syncToken = (String) redisUtils.get(RedisCacheConfigPath.syncTokenGetKey);
        if(syncToken==null) {
            GetTokenRep rep = syncTokenService.getToken();
            if (rep.getCode() != 0) {
                log.error("门户跳转获取token失败");
                return ResponseEntity.badRequest().body(rep);
            } else {
                redisUtils.del(RedisCacheConfigPath.syncTokenGetKey);
                redisUtils.set(RedisCacheConfigPath.syncTokenGetKey, rep.getAccess_token(), 1, TimeUnit.HOURS);
            }
            syncToken=rep.getAccess_token();
        }
        String userName = (String) redisUtils.get(String.format(RedisCacheConfigPath.syncTokenVerifyKey, token));
        log.info("校验token合法性");
        if(userName==null) {
            //校验token合法性
            VerifyRep verify = syncTokenService.verify(syncToken, token);
            if(!"成功".equals(verify.getMsg())){
                log.error("token不合法");
                return ResponseEntity.badRequest().body("token不合法");
            }
            userName = verify.getData().getUserName();
            redisUtils.del(String.format(RedisCacheConfigPath.syncTokenVerifyKey, token));
            redisUtils.set(String.format(RedisCacheConfigPath.syncTokenVerifyKey, token), userName, 1, TimeUnit.HOURS);
        }
        //模拟用户登录，跳转前端首页
        log.info("模拟用户登录，跳转前端首页");
        UserDto userDto = userService.findByName(userName);
        log.info("userDto:{}", JSONUtil.toJsonStr(userDto));
        if (userDto == null || userDto.getSyncPassword() == null) {
            log.error("未有同步该用户");
            return ResponseEntity.badRequest().body(userDto);
        }
        UsernamePasswordAuthenticationToken authenticationToken =
                new UsernamePasswordAuthenticationToken(userDto.getUsername(), userDto.getSyncPassword());
        Authentication authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken);
        SecurityContextHolder.getContext().setAuthentication(authentication);
        // 生成令牌
        String token1 = tokenProvider.createToken(authentication);
        final JwtUserDto jwtUserDto = (JwtUserDto) authentication.getPrincipal();
        // 保存在线信息
        onlineUserService.save(jwtUserDto, token1, request);
        if (loginProperties.isSingleLogin()) {
            //踢掉之前已经登录的token
            onlineUserService.checkLoginOnUser(userDto.getUsername(), token1);
        }
        jwtUserDto.setToken("Bearer "+token1);
        //记录登录用户信息
        redisUtils.del(String.format(RedisCacheConfigPath.loginUserCacheKey, jwtUserDto.getUsername()));
        redisUtils.set(String.format(RedisCacheConfigPath.loginUserCacheKey, jwtUserDto.getUsername()), JSONObject.toJSONString(jwtUserDto), 1, TimeUnit.HOURS);
        log.info("跳转前端首页--token:{}",jwtUserDto.getToken());
        httpServletResponse.setHeader("authorization",jwtUserDto.getToken());
        httpServletResponse.sendRedirect("https://qyzhjt.justh5.com/dashboard?token="+jwtUserDto.getToken());
        return null;
    }

    @ApiOperation("获取用户信息")
    @GetMapping(value = "/info")
    public ResponseEntity<Object> getUserInfo() {
        return ResponseEntity.ok(SecurityUtils.getCurrentUser());
    }

    @ApiOperation("获取验证码")
    @AnonymousGetMapping(value = "/code")
    public ResponseEntity<Object> getCode() {
        // 获取运算的结果
        Captcha captcha = loginProperties.getCaptcha();
        String uuid = properties.getCodeKey() + IdUtil.simpleUUID();
        // 保存
        redisUtils.set(uuid, captcha.text(), loginProperties.getLoginCode().getExpiration(), TimeUnit.MINUTES);
        // 验证码信息
        Map<String, Object> imgResult = new HashMap<String, Object>(2) {{
            put("img", captcha.toBase64());
            put("uuid", uuid);
        }};
        return ResponseEntity.ok(imgResult);
    }

    @ApiOperation("退出登录")
    @AnonymousDeleteMapping(value = "/logout")
    public ResponseEntity<Object> logout(HttpServletRequest request) {
        onlineUserService.logout(tokenProvider.getToken(request));
        return new ResponseEntity<>(HttpStatus.OK);
    }
}
