package vc.inreach.aws.request.test;
import com.amazonaws.DefaultRequest;
import com.amazonaws.auth.AWS4Signer;
import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.http.HttpMethodName;
import com.amazonaws.util.StringInputStream;
import com.google.common.collect.Multimap;

import java.lang.reflect.Method;
import java.net.URI;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
 * Test utility used to generate a AWS V4 Signature using {@link com.amazonaws.auth.AWS4Signer}.  This is needed in cases where the
 * the AWS Signing Test Suite (http://docs.aws.amazon.com/general/latest/gr/signature-v4-test-suite.html) does not include
 * a suitable test.
 */
public class SkdSignerUtil {

    static public String getExpectedAuthorizationHeader(Request request) throws Exception {
        // create the signable request
        DefaultRequest signableRequest = new DefaultRequest(null, request.getServiceName());
        signableRequest.setEndpoint(new URI("http://" + request.getHost()));
        signableRequest.setResourcePath(request.getUri());
        signableRequest.setHttpMethod(HttpMethodName.valueOf(request.getHttpMethod()));
        signableRequest.setContent(new StringInputStream(request.getBody()));
        if (request.getHeaders() != null)
            signableRequest.setHeaders(request.getHeaders());
        if (request.getQueryParams() != null) {
            Map<String, List<String>> convertedQueryParams = new HashMap<>();
            for (String paramName : request.getQueryParams().keySet()) {
                convertedQueryParams.put(paramName, new ArrayList<>(request.getQueryParams().get(paramName)));
            }
            signableRequest.setParameters(convertedQueryParams);
        }

        /*
           Init the signer class

           Note: Double uri encoding is off simple before the signature does not match the expected signature of the test cases
           if it is enabled.  This was a bit unexpected because AWSElasticsearchClient (AWS SDK Class) enabled double URI encoding
           in the signer by default.  I can only assume that double encoding is needed when accessing the service but not when accessing
           elasticsearch.
         */
        AWS4Signer aws4Signer = new AWS4Signer(false);
        aws4Signer.setServiceName(request.getServiceName());
        aws4Signer.setRegionName(request.getRegion());
        Method method1 = AWS4Signer.class.getDeclaredMethod("setOverrideDate", Date.class);
        method1.setAccessible(true);
        method1.invoke(aws4Signer, request.getDate());
        aws4Signer.sign(signableRequest, request.getCredentialsProvider().getCredentials());

        return (String) signableRequest.getHeaders().get("Authorization");
    }

    /**
     * Represents a request to be signed
     */
    static class Request {
        private String serviceName;
        private String region;
        private Date date;
        private String host;
        private String uri;
        private String body = "";
        private String httpMethod;
        private Map<String, Object> headers;
        private Multimap<String, String> queryParams;
        private AWSCredentialsProvider credentialsProvider;

        public String getServiceName() {
            return serviceName;
        }
        public Request setServiceName(String serviceName) {
            this.serviceName = serviceName;
            return this;
        }
        public String getRegion() {
            return region;
        }
        public Request setRegion(String region) {
            this.region = region;
            return this;
        }
        public Date getDate() {
            return date;
        }
        public Request setDate(Date date) {
            this.date = date;
            return this;
        }
        public String getHost() {
            return host;
        }
        public Request setHost(String host) {
            this.host = host;
            return this;
        }
        public String getUri() {
            return uri;
        }
        public Request setUri(String uri) {
            this.uri = uri;
            return this;
        }
        public String getBody() {
            return body;
        }
        public Request setBody(String body) {
            this.body = body;
            return this;
        }
        public String getHttpMethod() {
            return httpMethod;
        }
        public Request setHttpMethod(String httpMethod) {
            this.httpMethod = httpMethod;
            return this;
        }
        public Map<String, Object> getHeaders() {
            return headers;
        }
        public Request setHeaders(Map<String, Object> headers) {
            this.headers = headers;
            return this;
        }
        public Multimap<String, String> getQueryParams() {
            return queryParams;
        }
        public Request setQueryParams(Multimap<String, String> queryParams) {
            this.queryParams = queryParams;
            return this;
        }
        public AWSCredentialsProvider getCredentialsProvider() {
            return credentialsProvider;
        }
        public Request setCredentialsProvider(AWSCredentialsProvider credentialsProvider) {
            this.credentialsProvider = credentialsProvider;
            return this;
        }
    }

}