/*
 * Copyright 2018 Scytl Secure Electronic Voting SA
 *
 * All rights reserved
 *
 * See our extended copyright notice in *file 'Copyright.txt' which is part of this source code package
 */
package com.scytl.products.ov.commons.infrastructure.remote.client;

import java.io.IOException;
import java.security.PrivateKey;
import java.util.Base64;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.scytl.cryptolib.api.exceptions.GeneralCryptoLibException;
import com.scytl.products.ov.commons.exception.OvCommonsSignException;
import com.scytl.products.ov.commons.sign.RequestSigner;
import com.scytl.products.ov.commons.sign.beans.SignedRequestContent;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.RequestBody;

import okio.Buffer;

/**
 * Retrofit Interceptor class to include a Signature inside every request 'Signature' header.
 */
public class RestClientInterceptor implements Interceptor {

    private static final Logger LOG = LoggerFactory.getLogger(RestClientInterceptor.class);

    private PrivateKey privateKey;

    private NodeIdentifier nodeIdentifier;

    public static final String HEADER_SIGNATURE = "Signature";

    public static final String HEADER_ORIGINATOR = "Originator";

    public RestClientInterceptor (final PrivateKey privateKey, final NodeIdentifier nodeIdentifier) {
        this.privateKey = privateKey;
        this.nodeIdentifier = nodeIdentifier;
    }

    @Override
    public okhttp3.Response intercept(Chain chain) throws IOException {

        Request originalRequest = chain.request();

        // Ask for a new signature
        String signature = null;
        try {
            signature = getEncodedSignatureString(originalRequest, nodeIdentifier);

        } catch (GeneralCryptoLibException e) {
            LOG.error("GeneralCryptoLibException");
            throw new IOException(e);
        } catch (OvCommonsSignException e) {
            LOG.error("OvCommonsSignException");
            throw new IOException(e);
        }

        okhttp3.Response returnedResponse;
        
        Request.Builder requestBuilder = originalRequest.newBuilder().header(HEADER_SIGNATURE, signature)
            .header(HEADER_ORIGINATOR, nodeIdentifier.name());

        Request newRequest = requestBuilder.build();
        
        if(LOG.isDebugEnabled()) {
        	LOG.debug("REQUEST HEADERS: " + newRequest.headers());
        }

        returnedResponse = chain.proceed(newRequest);

        if(LOG.isDebugEnabled()) {
        	LOG.debug("RESPONSE HEADERS: " + returnedResponse.headers());
        }
        return returnedResponse;
    }

    private String getEncodedSignatureString(final Request request, final NodeIdentifier nodeIdentifier)
            throws IOException, GeneralCryptoLibException, OvCommonsSignException {
        RequestSigner requestSigner = new RequestSigner();

        String body = "";
        if (request.body() != null) {
            body = getRequestBodyToString(request.body());
        }

        SignedRequestContent signedRequestContent =
            new SignedRequestContent(request.url().toString(), request.method(), body, nodeIdentifier.name());

        byte[] signature = requestSigner.sign(signedRequestContent, privateKey);

        return new String(Base64.getEncoder().encode(signature));
    }

    private String getRequestBodyToString(final RequestBody request) {
        final RequestBody copy = request;
        try (final Buffer buffer = new Buffer()){
            if (copy != null)
                copy.writeTo(buffer);
            else
                return "";
            return buffer.readUtf8();
        } catch (final IOException e) {
        	final String errorMsg = "Exception occurred during process body to String";
        	LOG.error(errorMsg, e);
            return "";
        }
    }

}
