/*
 *     Copyright (C) 2015 ASIP Santé
 */
package fr.asipsante.mss.connectkit.connection;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.CookieManager;
import java.net.CookiePolicy;
import java.net.HttpCookie;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

import javax.net.ssl.HttpsURLConnection;

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

/**
 * Fontionnalités génériques pour les connexions HTTP.
 * 
 * @author ASIP Santé
 *
 */
public class ConnectionUtil {

    private static final Logger LOGGER = LoggerFactory
            .getLogger(ConnectionUtil.class);
    private static final Logger PAYLOLAD_LOGGER = LoggerFactory
            .getLogger("PAYLOAD");

    public int sendPost(HttpsURLConnection connection, InputStream request)
            throws IOException {

        byte[] buffer = new byte[2048];
        int nb = request.read(buffer);
        ByteArrayOutputStream payloadBuffer;
        String charsetName = "UTF-8";
        if (PAYLOLAD_LOGGER.isDebugEnabled()) {
            payloadBuffer = new ByteArrayOutputStream();
            String requestSpecs = "POSTREQUEST TO " + connection.getURL()
                    + " \n";
            payloadBuffer.write(requestSpecs.getBytes(charsetName));
            dumpConnectionProperties(payloadBuffer,
                    connection.getRequestProperties(), charsetName);
        } else {
            payloadBuffer = null;
        }
        OutputStream dest = connection.getOutputStream();
        while (nb >= 0) {
            dest.write(buffer, 0, nb);
            if (PAYLOLAD_LOGGER.isDebugEnabled()) {
                payloadBuffer.write(buffer, 0, nb);
            }
            nb = request.read(buffer);
        }
        if (PAYLOLAD_LOGGER.isDebugEnabled()) {
            PAYLOLAD_LOGGER.debug(payloadBuffer.toString(charsetName));
        }
        int responseCode = connection.getResponseCode();
        return responseCode;
    }

    private void dumpConnectionProperties(ByteArrayOutputStream payloadBuffer,
            Map<String, List<String>> headerset, String charset)
            throws UnsupportedEncodingException, IOException {
        for (Entry<String, List<String>> header : headerset.entrySet()) {
            String key = header.getKey();
            if (key != null) {
                payloadBuffer.write(key.getBytes(charset));
                payloadBuffer.write(":".getBytes("UTF-8"));
            }

            for (String value : header.getValue()) {
                if (value != null) {
                    payloadBuffer.write(value.getBytes("UTF-8"));
                }
                payloadBuffer.write("\n".getBytes("UTF-8"));
            }
        }
    }

    public byte[] getResponse(HttpsURLConnection connection) throws IOException {
        InputStream responseStream;
        int responseClass = getResponseClass(connection);
        if (isOKClass(responseClass)) {
            responseStream = connection.getInputStream();
        } else {
            responseStream = connection.getErrorStream();
        }
        ByteArrayOutputStream responseBuffer = new ByteArrayOutputStream(
                Math.max(0, connection.getContentLength()));
        byte[] buffer2 = new byte[2048];
        int nb = responseStream.read(buffer2);
        while (nb >= 0) {
            responseBuffer.write(buffer2, 0, nb);
            nb = responseStream.read(buffer2);
        }

        byte[] reponseData = responseBuffer.toByteArray();
        if (isErrorClass(responseClass)) {
            StringBuilder messageBuilder = new StringBuilder(
                    "Connexion response HTTP headers:\n");
            for (Entry<String, List<String>> header : connection
                    .getHeaderFields().entrySet()) {
                messageBuilder.append(header.getKey()).append(":");
                if (header.getValue().size() > 1) {
                    messageBuilder.append("\n");
                }
                for (String value : header.getValue()) {
                    messageBuilder.append(" ").append(value).append("\n");
                }
            }
            LOGGER.debug(messageBuilder.toString());
            ;
            dumpReponse(connection, reponseData);
            throw new Fault(reponseData);
        }
        dumpReponse(connection, reponseData);
        return reponseData;
    }

    private void dumpReponse(HttpsURLConnection connection, byte[] reponseData)
            throws IOException, UnsupportedEncodingException {
        if (PAYLOLAD_LOGGER.isDebugEnabled()) {
            ByteArrayOutputStream payloadBuffer = new ByteArrayOutputStream();
            String charsetName = "UTF-8";
            String responseSpec = "RESPONSE FROM " + connection.getURL()
                    + " \n";
            payloadBuffer.write(responseSpec.getBytes(charsetName));
            dumpConnectionProperties(payloadBuffer,
                    connection.getHeaderFields(), charsetName);
            payloadBuffer.write(reponseData);
            PAYLOLAD_LOGGER.debug(payloadBuffer.toString(charsetName));
        }
    }

    private boolean isErrorClass(int responseClass) {
        return responseClass >= 4;
    }

    private boolean isOKClass(int responseClass) {
        return responseClass < 4;
    }

    private int getResponseClass(HttpsURLConnection connection)
            throws IOException {
        return connection.getResponseCode() / 100;
    }

    /**
     * Méthode d'injection d'un cookie sur une connexion.
     * 
     * @param connection
     * @param cookie
     * @throws URISyntaxException
     */
    public void addCookie(HttpsURLConnection connection, HttpCookie cookie)
            throws URISyntaxException {
        HashSet<HttpCookie> cookieSet = new HashSet<>();
        cookieSet.add(cookie);
        addCookies(connection, cookieSet);
    }

    /**
     * Méthode d'injection d'un ensemble de cookies sur une connexion.
     * 
     * @param connection
     * @param cookies
     * @throws URISyntaxException
     */
    public void addCookies(HttpsURLConnection connection,
            Set<HttpCookie> cookies) throws URISyntaxException {
        CookieManager manager = new CookieManager();
        manager.setCookiePolicy(new CookiePolicy() {

            @Override
            public boolean shouldAccept(URI uri, HttpCookie cookie) {
                return true;
            }
        });
        for (HttpCookie cookie : cookies) {
            String header = cookie.toString();
            connection.addRequestProperty("Cookie", header);
        }

    }

    public String debugByteResponse(byte[] authNrequest, String encoding) {
        String logMessage;
        try {
            logMessage = new String(authNrequest, encoding);
        } catch (UnsupportedEncodingException e) {
            LOGGER.error("Encodage non-supporté: " + encoding, e);
            logMessage = new String(authNrequest);
        }
        return logMessage;
    }
}