package com.sunyard.config.security.jwt;

import cn.hutool.core.util.StrUtil;
import com.google.gson.Gson;
import com.sunyard.chsm.constant.SecurityConstant;
import com.sunyard.ssp.modules.user.entity.ScUser;
import com.sunyard.ssp.modules.user.service.IScUserService;
import com.sunyard.ssp.utils.ResponseUtil;
import com.sunyard.ssp.utils.SecurityUtil;
import com.sunyard.ssp.vo.TokenUser;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * 登录成功处理类
 * @author Exrickx
 */
@Slf4j
@Component
public class AuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {

    @Value("${ssp.token.redis}")
    private Boolean tokenRedis;

    @Value("${ssp.tokenExpireTime}")
    private Integer tokenExpireTime;

    @Value("${ssp.saveLoginTime}")
    private Integer saveLoginTime;

    @Value("${ssp.passMaxDays}")
    private Integer passMaxDays;

    @Value("${ssp.passWarnAge}")
    private Integer passWarnAge;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private SecurityUtil securityUtil;

    @Autowired
    private IScUserService scUserService;

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {

        long beginTime = System.currentTimeMillis();
        //用户选择保存登录状态几天
        String saveLogin = request.getParameter(SecurityConstant.SAVE_LOGIN);
        Boolean saved = false;
        String msg = "登录成功";
        if(StrUtil.isNotBlank(saveLogin) && Boolean.valueOf(saveLogin)){
            saved = true;
            if(!tokenRedis){
                tokenExpireTime = saveLoginTime * 60 * 24;
            }
        }
        String username = ((UserDetails)authentication.getPrincipal()).getUsername();
        log.info("loginSuccess username:" + username + "begin:" + (beginTime - System.currentTimeMillis()));
        List<GrantedAuthority> authorities = (List<GrantedAuthority>) ((UserDetails)authentication.getPrincipal()).getAuthorities();
        List<String> list = new ArrayList<>();
        for(GrantedAuthority g : authorities){
            list.add(g.getAuthority());
        }
        log.info("loginSuccess username:" + username + "getCurrUser begin:" + (beginTime - System.currentTimeMillis()));
        ScUser scuser = securityUtil.getCurrUser();
        //ipInfoUtil.getUrl(request);
        log.info("loginSuccess username:" + username + "getCurrUser end:" + (beginTime - System.currentTimeMillis()));
        // 登陆成功生成token
        String token;
        if(tokenRedis){
            // redis
            token = UUID.randomUUID().toString().replace("-", "");
            TokenUser user = new TokenUser(username, list, saved);
            // 单点登录 之前的token失效
            String oldToken = redisTemplate.opsForValue().get(SecurityConstant.USER_TOKEN + username);
            if(StrUtil.isNotBlank(oldToken)){
                redisTemplate.delete(SecurityConstant.TOKEN_PRE + oldToken);
            }

            //清空失败次数
            String key = "loginTimeLimit:" + username;
            redisTemplate.delete(key);

            if(saved){
                redisTemplate.opsForValue().set(SecurityConstant.USER_TOKEN + username, token, saveLoginTime, TimeUnit.DAYS);
                redisTemplate.opsForValue().set(SecurityConstant.TOKEN_PRE + token, new Gson().toJson(user), saveLoginTime, TimeUnit.DAYS);
            }else{
                redisTemplate.opsForValue().set(SecurityConstant.USER_TOKEN + username, token, tokenExpireTime, TimeUnit.MINUTES);
                redisTemplate.opsForValue().set(SecurityConstant.TOKEN_PRE + token, new Gson().toJson(user), tokenExpireTime, TimeUnit.MINUTES);
            }
        }else{
            // jwt
            token = SecurityConstant.TOKEN_SPLIT + Jwts.builder()
                    //主题 放入用户名
                    .setSubject(username)
                    //自定义属性 放入用户拥有请求权限
                    .claim(SecurityConstant.AUTHORITIES, new Gson().toJson(list))
                    //失效时间
                    .setExpiration(new Date(System.currentTimeMillis() + tokenExpireTime * 60 * 1000))
                    //签名算法和密钥
                    .signWith(SignatureAlgorithm.HS512, SecurityConstant.JWT_SIGN_KEY)
                    .compact();
        }

        LocalDateTime limitDate = scuser.getPassUpdateTime().plusDays(passMaxDays);

        DateTimeFormatter sdf = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        if(limitDate.isBefore(LocalDateTime.now().plusDays(passWarnAge))){
            msg = "密码将于" + sdf.format(limitDate) + "过期,请及时修改";
        }

        /*sunyard*/
        response.setHeader("Access-Control-Allow-Methods",  "GET, POST, OPTIONS, PUT, HEAD, DELETE");
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("access-control-allow-headers", "Content-Type,Accept,Authorization,accesstoken");
        /*sunyard end*/
        log.info("loginSuccess username:" + username + "end:" + (beginTime - System.currentTimeMillis()));
        ResponseUtil.out(response, ResponseUtil.resultMap(true,200,msg, token));
    }
}
