package com.qmth.boot.core.security.service.impl;

import com.qmth.boot.core.security.config.SecurityProperties;
import com.qmth.boot.core.security.exception.AuthorizationException;
import com.qmth.boot.core.security.model.AccessEntity;
import com.qmth.boot.core.security.service.AuthorizationService;
import com.qmth.boot.core.security.service.AuthorizationServiceRegistration;
import com.qmth.boot.core.security.service.AuthorizationSupport;
import com.qmth.boot.core.security.service.CustomizeAuthorizationService;
import com.qmth.boot.tools.signature.SignatureEntity;
import com.qmth.boot.tools.signature.SignatureType;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;

import javax.validation.constraints.NotNull;

/**
 * 内置默认鉴权服务
 */
@Component
public class BaseAuthorizationSupport implements AuthorizationSupport {

    private SecurityProperties securityProperties;

    private AuthorizationServiceContainer serviceContainer;

    public BaseAuthorizationSupport(@NotNull SecurityProperties securityProperties,
            @Nullable CustomizeAuthorizationService customizeAuthorizationService) {
        this.securityProperties = securityProperties;
        this.serviceContainer = new AuthorizationServiceContainer();
        if (customizeAuthorizationService != null) {
            customizeAuthorizationService.register(serviceContainer);
        }
    }

    public AuthorizationServiceRegistration getServiceContainer() {
        return serviceContainer;
    }

    @Override
    public AccessEntity validateSignature(String signature, String method, String uri, String timestamp,
            SignatureType... signTypes) {
        // 验证时间戳字段是否过期
        long time = validateTime(timestamp);
        // 解析签名鉴权信息
        SignatureEntity entity = SignatureEntity.parse(signature, method, uri, time);
        if (entity == null) {
            throw AuthorizationException.SIGNATURE_INVALID;
        }
        // 过滤签名类型
        validateSignatureType(entity, signTypes);
        // 获取合适的鉴权服务
        AuthorizationService service = serviceContainer.find(uri, entity.getType());
        if (service == null) {
            throw AuthorizationException.SERVICE_NOT_FOUND;
        }
        // 获取身份信息并验证摘要内容
        AccessEntity ae = service.findByIdentity(entity.getInvoker(), entity.getType(), uri);
        if (ae == null) {
            throw AuthorizationException.IDENTITY_NOT_FOUND;
        }
        // 非调试模式下验证签名的密文部分
        if (!securityProperties.isDebugMode() && !entity.validate(ae.getSecret())) {
            throw entity.getType() == SignatureType.SECRET ?
                    AuthorizationException.SECRET_ERROR :
                    AuthorizationException.TOKEN_ERROR;
        }
        // 提交请求对象和接口地址进行权限验证
        if (!service.hasPermission(ae, uri)) {
            throw AuthorizationException.NO_PERMISSION;
        }
        return ae;
    }

    private void validateSignatureType(SignatureEntity entity, SignatureType[] types) {
        if (ArrayUtils.isNotEmpty(types)) {
            boolean match = false;
            for (SignatureType type : types) {
                if (entity.getType() == type) {
                    match = true;
                    break;
                }
            }
            if (!match) {
                throw AuthorizationException.SIGNATURE_TYPE_INVALID;
            }
        }
    }

    private long validateTime(String timestamp) {
        long time = NumberUtils.toLong(timestamp, 0);
        if (time <= 0) {
            throw AuthorizationException.TIME_INVALID;
        }
        long diff = System.currentTimeMillis() - time;
        if (diff < (-1 * securityProperties.getTimeMaxAhead().toMillis()) || diff > securityProperties.getTimeMaxDelay()
                .toMillis()) {
            throw AuthorizationException.TIME_EXPIRED;
        }
        return time;
    }

}
