package cn.quantgroup.xyqb.aspect.captcha;

import cn.quantgroup.xyqb.Constants;
import cn.quantgroup.xyqb.model.JsonResult;
import cn.quantgroup.xyqb.thirdparty.jcaptcha.AbstractManageableImageCaptchaService;
import cn.quantgroup.xyqb.util.IpUtil;
import com.octo.captcha.service.CaptchaServiceException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.nio.charset.Charset;
import java.util.Optional;
import java.util.UUID;

/**
 * 类名称：CaptchaValidateAdvisor
 * 类描述：
 *
 * @author 李宁
 * @version 1.0.0
 * 创建时间：15/11/17 14:49
 * 修改人：
 * 修改时间：15/11/17 14:49
 * 修改备注：
 */
@Slf4j
@Aspect
@Component
public class CaptchaValidateAdvisor {

    private static final String SUPER_CAPTCHA_ID = UUID.nameUUIDFromBytes("__QG_APPCLIENT_AGENT__".getBytes(Charset.forName("UTF-8"))).toString();
    private static final String SUPER_CAPTCHA = "__SUPERQG__";

    @Autowired
    @Qualifier("stringRedisTemplate")
    private RedisTemplate<String, String> redisTemplate;

    @Autowired
    @Qualifier("customCaptchaService")
    private AbstractManageableImageCaptchaService imageCaptchaService;

    /**
     * 自动化测试忽略验证码
     */
    @Value("${xyqb.auth.captcha.autotest.enable:false}")
    private boolean autoTestCaptchaEnabled;

    /**
     * 图形验证码切面
     */
    @Pointcut("@annotation(cn.quantgroup.xyqb.aspect.captcha.CaptchaValidator)")
    private void needCaptchaValidate() {
    }

    /**
     * 在受图形验证码保护的接口方法执行前, 执行图形验证码校验
     *
     * @param pjp
     * @return
     * @throws Throwable
     */
    @Around("needCaptchaValidate()")
    private Object doCapchaValidate(ProceedingJoinPoint pjp) throws Throwable {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String registerFrom = Optional.ofNullable(request.getParameter("registerFrom")).orElse("");
        String captchaId = Optional.ofNullable(request.getParameter(Constants.QG_CAPTCHA_ID)).orElse("");
        String captchaValue = request.getParameter(Constants.QG_CAPTCHA_VALUE);
        if (shouldSkipCaptchaValidate(registerFrom, captchaId, captchaValue)) {
            log.info("使用超级图形验证码校验, registerFrom={}, clientIp={}", registerFrom, IpUtil.getRemoteIP(request));
            return pjp.proceed();
        }
        JsonResult result = JsonResult.buildSuccessResult("验证码不正确", "");
        result.setBusinessCode("0002");
        if (StringUtils.isNotBlank(captchaValue)) {
            // 忽略用户输入的大小写
            String captcha = StringUtils.lowerCase(captchaValue);
            // 验证码校验
            Boolean validCaptcha = false;
            try {
                validCaptcha = imageCaptchaService.validateResponseForID(Constants.IMAGE_CAPTCHA_KEY + captchaId, captcha);
            } catch (CaptchaServiceException ex) {
                log.error("验证码校验异常, {}, {}", ex.getMessage(), ex);
            }
            if (validCaptcha) {
                return pjp.proceed();
            }
        }
        return result;
    }

    private boolean shouldSkipCaptchaValidate(String registerFrom, String captchaId, Object captchaValue) {
        // 如果启用了超级验证码功能, 检查超级验证码, 超级验证码区分大小写
        if (autoTestCaptchaEnabled) {
            return true;
        }
        return StringUtils.equals(SUPER_CAPTCHA_ID, String.valueOf(captchaId)) && StringUtils.equals(SUPER_CAPTCHA, String.valueOf(captchaValue));
    }

}
