package com.qmth.boot.test.core.security;

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.impl.BaseAuthorizationSupport;
import com.qmth.boot.test.core.security.model.TestAuthorizationService;
import com.qmth.boot.tools.signature.SignatureEntity;
import com.qmth.boot.tools.signature.SignatureType;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;

public class AuthTest {

    public static SecurityProperties properties;

    public static BaseAuthorizationSupport support;

    public static TestAuthorizationService authorization = new TestAuthorizationService();

    public static String method = "post";

    public static String tokenPath = "/api/token";

    public static String secretPath = "/api/secret";

    @BeforeClass
    public static void prepare() {
        properties = new SecurityProperties();
        support = new BaseAuthorizationSupport(properties, registration -> {
            registration.setPrefix("/api/token", SignatureType.TOKEN, authorization);
            registration.setPrefix("/api/secret", SignatureType.SECRET, authorization);
        });
    }

    @Test
    public void testAuthSecret() {
        properties.setDebugMode(false);
        long time = System.currentTimeMillis();
        String accessKey = "123456";
        String accessSecret = "secret";
        authorization.secret = accessSecret;
        String signature = SignatureEntity
                .build(SignatureType.SECRET, method, secretPath, time, accessKey, accessSecret);

        try {
            support.validateSignature(signature, method, "/api/token", String.valueOf(time));
        } catch (AuthorizationException ee) {
            Assert.assertEquals(ee, AuthorizationException.SERVICE_NOT_FOUND);
        }
        authorization.disturb = true;
        try {
            support.validateSignature(signature, method, secretPath, String.valueOf(time));
        } catch (AuthorizationException ee) {
            Assert.assertEquals(ee, AuthorizationException.IDENTITY_NOT_FOUND);
        }
        authorization.disturb = false;
        authorization.secret = "error";
        try {
            support.validateSignature(signature, method, secretPath, String.valueOf(time));
        } catch (AuthorizationException ee) {
            Assert.assertEquals(ee, AuthorizationException.SECRET_ERROR);
        }
        authorization.secret = accessSecret;
        try {
            support.validateSignature(signature, method, secretPath, String.valueOf(time));
        } catch (AuthorizationException ee) {
            Assert.assertEquals(ee, AuthorizationException.NO_PERMISSION);
        }
        AccessEntity entity = support.validateSignature(signature, method, secretPath, String.valueOf(time));
        Assert.assertNotNull(entity);
        Assert.assertEquals(entity.getIdentity(), accessKey);
        Assert.assertEquals(entity.getSecret(), accessSecret);
    }

    @Test
    public void testAuthToken() {
        properties.setDebugMode(false);
        long time = System.currentTimeMillis();
        String sessionId = "123456";
        String token = "token";
        authorization.secret = token;
        String signature = SignatureEntity.build(SignatureType.TOKEN, method, tokenPath, time, sessionId, token);

        try {
            support.validateSignature(signature, method, tokenPath, "");
        } catch (AuthorizationException ee) {
            Assert.assertEquals(ee, AuthorizationException.TIME_INVALID);
        }
        try {
            support.validateSignature(signature, method, tokenPath, "0");
        } catch (AuthorizationException ee) {
            Assert.assertEquals(ee, AuthorizationException.TIME_INVALID);
        }
        try {
            support.validateSignature(signature, method, tokenPath,
                    String.valueOf(System.currentTimeMillis() + properties.getTimeMaxAhead().toMillis() + 1000));
        } catch (AuthorizationException ee) {
            Assert.assertEquals(ee, AuthorizationException.TIME_EXPIRED);
        }
        try {
            support.validateSignature(signature, method, tokenPath,
                    String.valueOf(System.currentTimeMillis() - properties.getTimeMaxDelay().toMillis() - 1000));
        } catch (AuthorizationException ee) {
            Assert.assertEquals(ee, AuthorizationException.TIME_EXPIRED);
        }
        try {
            support.validateSignature("", method, tokenPath, String.valueOf(time));
        } catch (AuthorizationException ee) {
            Assert.assertEquals(ee, AuthorizationException.SIGNATURE_INVALID);
        }
        try {
            support.validateSignature(signature, method, "/api/wasd", String.valueOf(time));
        } catch (AuthorizationException ee) {
            Assert.assertEquals(ee, AuthorizationException.SERVICE_NOT_FOUND);
        }
        authorization.disturb = true;
        try {
            support.validateSignature(signature, method, tokenPath, String.valueOf(time));
        } catch (AuthorizationException ee) {
            Assert.assertEquals(ee, AuthorizationException.IDENTITY_NOT_FOUND);
        }
        authorization.disturb = false;
        authorization.secret = "error";
        try {
            support.validateSignature(signature, method, tokenPath, String.valueOf(time));
        } catch (AuthorizationException ee) {
            Assert.assertEquals(ee, AuthorizationException.TOKEN_ERROR);
        }
        authorization.secret = token;
        try {
            support.validateSignature(signature, method, tokenPath, String.valueOf(time));
        } catch (AuthorizationException ee) {
            Assert.assertEquals(ee, AuthorizationException.NO_PERMISSION);
        }
        AccessEntity entity = support.validateSignature(signature, method, tokenPath, String.valueOf(time));
        Assert.assertNotNull(entity);
        Assert.assertEquals(entity.getIdentity(), sessionId);
        Assert.assertEquals(entity.getSecret(), token);
    }

    @Test
    public void testDebug() {
        properties.setDebugMode(true);
        long time = System.currentTimeMillis();
        String sessionId = "123456";
        String token = "token";
        authorization.secret = token + token;
        String signature = SignatureEntity.build(SignatureType.TOKEN, method, tokenPath, time, sessionId, token);

        AccessEntity entity = support.validateSignature(signature, method, tokenPath, String.valueOf(time));
        Assert.assertNotNull(entity);
        Assert.assertEquals(entity.getIdentity(), sessionId);
    }

}
