package com.ytpm.auth.impl;

import cn.hutool.core.util.StrUtil;
import com.google.common.collect.Maps;
import com.ytpm.auth.AuthService;
import com.ytpm.general.RepMessage;
import com.ytpm.general.Result;
import com.ytpm.general.StatusCode;
import com.ytpm.handle.CustomerException;
import com.ytpm.oauth.model.CaptchaValid;
import com.ytpm.util.CacheUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.endpoint.TokenEndpoint;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Map;
import java.util.Objects;

@Slf4j(topic = "auth-service")
@Service
public class AuthServiceImpl implements AuthService {
    @Resource
    private RedisTokenStore redisTokenStore;

    @Resource
    private TokenEndpoint tokenEndpoint;

    private static final String USER_NAME = "username";
    private static final String PASS_WORD = "password";

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result<Map<String, Object>> createAccessToken(Principal principal, Map<String, String> parameters) {
        CaptchaValid captchaValid = new CaptchaValid(true,
                3,
                parameters.get("uuid"),
                0,
                parameters.get("captcha"));
        validCode(captchaValid);
        // 判断用户名密码是否正确，是否被锁定等逻辑
        String loginName = parameters.get(USER_NAME);
        String password = parameters.get(PASS_WORD);

        if (StrUtil.isEmpty(loginName)) {
            return new Result<>(StatusCode.USER_NAME_EMPTY, RepMessage.USER_NAME_EMPTY);
        }

        parameters.put(USER_NAME, loginName);
        parameters.put(PASS_WORD, password);
        return logoutAndReLogin(principal, parameters);
    }

    private void validCode(CaptchaValid bo){
        log.info("验证码校验");

        //判断是否需要输入验证码,密码错误次数大于配置错误次数
        if (bo.getShowCaptcha() || bo.getErrorCount() > bo.getSettingErrorCountCaptcha()) {
            if (StrUtil.isBlank(bo.getCaptcha())) {
                throw new CustomerException("验证码不能为空");
            }
            if (StrUtil.isBlank(bo.getUuid())) {
                throw new CustomerException("验证码已过期，请刷新后重试");
            }
            String vailCode = (String) CacheUtil.get("sms_captcha_" + bo.getUuid());
            if (vailCode == null) {
                throw new CustomerException("验证码已过期，请刷新后重试");
            }
            if (!bo.getCaptcha().equals(vailCode)) {
                throw new CustomerException("验证码错误");
            }
        }
    }
    /**
     * 登录或者重新登录
     */
    private Result<Map<String, Object>> logoutAndReLogin(Principal principal, Map<String, String> parameters) {
        Map<String, Object> resultMap = Maps.newLinkedHashMap();
        // 刷新并废弃掉当前token再重新生成token
        OAuth2AccessToken accessToken = null;
        try {
            //首次拿到缓存中的旧token
            accessToken = tokenEndpoint.postAccessToken(principal, parameters).getBody();
            if (Objects.isNull(accessToken)) {
                return new Result<>(StatusCode.PWD_ERROR, RepMessage.USERNAME_OR_PASSWORD_ERROR);
            }
            //如果token过期则注销旧token重新生产
            if(accessToken.isExpired()){
                // 注销token
                logout(accessToken.getValue());
                // 重新生成token
                accessToken = tokenEndpoint.postAccessToken(principal, parameters).getBody();
            }

            if (Objects.isNull(accessToken)) {
                return new Result<>(StatusCode.MANAGE_NOT_LOGIN, RepMessage.TOKEN_INVALID);
            }
        } catch (Exception e) {
            e.printStackTrace();
            return new Result<>(403, RepMessage.USERNAME_OR_PASSWORD_ERROR);
        }
        resultMap.put("access_token", accessToken.getValue());
        resultMap.put("refresh_token", accessToken.getRefreshToken().getValue());
        resultMap.put("token_type", accessToken.getTokenType());
        resultMap.put("expires_in", accessToken.getExpiresIn());
        resultMap.put("scope", StringUtils.join(accessToken.getScope(), ","));
        resultMap.putAll(accessToken.getAdditionalInformation());
        resultMap.put("authorities", new ArrayList<>());
        return new Result<>(StatusCode.OK, "成功", resultMap);
    }

    /**
     * 登出接口
     *
     * @param accessToken
     * @return
     */
    @Override
    public Result logout(String accessToken) {
        try {
            if (StrUtil.isNotBlank(accessToken)) {
                OAuth2AccessToken oAuth2AccessToken = redisTokenStore.readAccessToken(accessToken);
                if (null != oAuth2AccessToken) {
                    redisTokenStore.removeAccessToken(oAuth2AccessToken);
                }
            }
            return new Result<>(200, RepMessage.LOGOUT_SUCCESS);
        } catch (Exception e) {
            return new Result<>(StatusCode.ERROR, RepMessage.LOGOUT_FAIL);
        }
    }
}
