package cn.quantgroup.xyqb.aspect.limit;

import cn.quantgroup.xyqb.Constants;
import cn.quantgroup.xyqb.entity.User;
import cn.quantgroup.xyqb.model.JsonResult;
import cn.quantgroup.xyqb.model.LoginProperties;
import cn.quantgroup.xyqb.model.session.SessionStruct;
import cn.quantgroup.xyqb.session.XyqbSessionContextHolder;
import cn.quantgroup.xyqb.util.IpUtil;
import cn.quantgroup.xyqb.util.TenantUtil;
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.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Objects;
import java.util.Set;

/**
 * 免密访问校验切面
 *
 * @author 任文超
 * @version 1.0.0
 * @since 2017-11-21
 */
@Slf4j
@Aspect
@Component
public class PasswordFreeAccessValidateAdvisor {

    private static final String USER_ID = "userId";

    /**
     * 免密访问校验切面
     */
    @Pointcut("@annotation(cn.quantgroup.xyqb.aspect.limit.PasswordFreeAccessValidator)")
    private void passwordFreeAccess() {
    }

    /**
     * 执行免密访问校验
     *
     * @throws Throwable
     */
    @Around("passwordFreeAccess()")
    private Object checkToken(ProceedingJoinPoint pjp) throws Throwable {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        boolean valid = tokenValid(request);
        if (valid) {
            return pjp.proceed();
        }
        return JsonResult.buildErrorStateResult("拒绝访问", "");
    }

    /**
     * 校验免密访问
     * 规则：token 与 身份标记（phoneNo、userId匹配）
     *
     * @return True or False
     */
    private boolean tokenValid(HttpServletRequest request) {
        Objects.requireNonNull(request, "无效请求");
        String clientIp = IpUtil.getRemoteIP(request);
        Set<String> paramKeys = request.getParameterMap().keySet();
        if (!paramKeys.contains(Constants.PHONE_NO) && !paramKeys.contains(USER_ID)) {
            log.info("非法请求 - 缺少参数, paramKeys={}, clientIp={}", paramKeys, clientIp);
            return false;
        }
        // 当前请求的phoneNo/userId
        String phoneNo = request.getParameter(Constants.PHONE_NO);
        String userId = request.getParameter(USER_ID);
        if (StringUtils.isBlank(phoneNo) && StringUtils.isBlank(userId)) {
            log.info("非法请求 - 缺少参数, phoneNo={}, userId={}, clientIp={}", phoneNo, userId, clientIp);
            return false;
        }
        // 当前请求的Token
        String token = request.getHeader(Constants.X_AUTH_TOKEN);
        if (StringUtils.length(token) != Constants.TOKEN_LENGTH) {
            log.info("非法请求 - 无效token, token={}, phoneNo={}, userId={}, clientIp={}", token, phoneNo, userId, clientIp);
            return false;
        }

        // 当前session
        SessionStruct session = XyqbSessionContextHolder.getXSessionFromRedis(token);
        if (Objects.isNull(session) || Objects.isNull(session.getValues()) || Objects.isNull(session.getValues().getUser())) {
            log.info("非法请求 - 未登录, token={}, phoneNo={}, userId={}, clientIp={}", token, phoneNo, userId, clientIp);
            return false;
        }

        // 获取头部qg-tenant-id
        String tenantId = request.getHeader(Constants.X_AUTH_TENANT);
        LoginProperties loginProperties = session.getValues().getLoginProperties();
        // 如果token Session tenantId 不为空
        if (!Objects.isNull(loginProperties.getTenantId())) {
            // 如果头部没有tenantId参数
            if (StringUtils.isBlank(tenantId)) {
                // 如果 token Session tenantId 不是默认羊小咩, 那么拒绝登陆
                if (!loginProperties.getTenantId().toString().equals(TenantUtil.TENANT_DEFAULT)) {
                    log.info("非法请求 - 错误租户, token={}, phoneNo={}, userId={}, clientIp={}, tenantId={}, loginTenantId={}", token, phoneNo, userId, clientIp, tenantId, loginProperties.getTenantId().toString());
                    return false;
                }
            } else {
                //如果头部有tenantId参数,判断是否相等，并且是否是默认羊小咩
                if (!loginProperties.getTenantId().toString().equals(tenantId) && !TenantUtil.TENANT_DEFAULT.toString().equals(tenantId)) {
                    log.info("非法请求 - 错误租户, token={}, phoneNo={}, userId={}, clientIp={}, tenantId={}, loginTenantId={}", token, phoneNo, userId, clientIp, tenantId, loginProperties.getTenantId().toString());
                    return false;
                }
            }
        } else {
            // 如果token seesion tenantId 为空, tenantId不为空，并且不是默认羊小咩
           if(!StringUtils.isBlank(tenantId) && !TenantUtil.TENANT_DEFAULT.toString().equals(tenantId))  {
               log.info("非法请求 - 错误租户, token={}, phoneNo={}, userId={}, clientIp={}, tenantId={}", token, phoneNo, userId, clientIp, tenantId);
               return false;
           }
        }

        // 当前用户
        User user = session.getValues().getUser();
        if (Objects.isNull(user.getId()) && StringUtils.isBlank(user.getPhoneNo())) {
            log.info("非法请求 - 未登录, token={}, phoneNo={}, userId={}, clientIp={}", token, phoneNo, userId, clientIp);
            return false;
        }
        // 校对用户信息是否匹配
        boolean valid = (Objects.nonNull(user.getId()) && Objects.equals(userId, user.getId().toString()));
        valid = valid || (StringUtils.isNotBlank(phoneNo) && Objects.equals(phoneNo, user.getPhoneNo()));
        if (!valid) {
            log.info("非法请求 - 身份不匹配, token={}, phoneNo=({},{}), userId=({},{}), clientIp={}", token, phoneNo, user.getPhoneNo(), userId, user.getId(), clientIp);
        }
        return valid;
    }

}
