using System.IO;
using System.Net;
using System.Security.Cryptography.X509Certificates;

namespace EdecBeispielClient
{
    public class SoapRequest
    {
        static readonly string POST = "POST";
        static readonly string XML_CONTENT_TYPE = "text/xml;charset=UTF-8";
        static readonly string SSL_CLIENT_CERT_S_DN_CN = "SSL_CLIENT_CERT_S_DN_CN";

        public string Endpoint { get; set; }
        public string EnvelopePath { get; set; }
        public X509Certificate2 Certificate { get; set; }
        public string Spediteur { get; set; } // Only used for requests without a certificate

        public SoapRequest() { }

        public SoapRequest(string endpoint, string envelopePath, X509Certificate2 certificate = null)
        {
            Endpoint = endpoint;
            EnvelopePath = envelopePath;
            Certificate = certificate;
        }

        public string Request()
        {
            return Request(Endpoint, EnvelopePath, Certificate);
        }

        private string Request(string uri, string envelopePath, X509Certificate2 certificate = null)
        {
            var requestBody = File.ReadAllBytes(envelopePath);

            var request = certificate != null
                ? CreateEdecWebRequest(uri, certificate)
                : CreateEdecWebRequest(uri, sslClientCertHeader: Spediteur);

            using (var response = StreamSoapRequest(request, requestBody))
                return GetBodyFromWebResponse(response);
        }

        private HttpWebRequest CreateEdecWebRequest(string requestUri, X509Certificate2 certificate = null, string sslClientCertHeader = null)
        {
            var request = CreateHttpWebRequest(requestUri);
            
            if (certificate != null)
            {
                request.ClientCertificates = new X509Certificate2Collection(certificate);
            }
            else if (sslClientCertHeader != null) // this headers is only needed if there's no certificate
            {
                request.Headers = new WebHeaderCollection
                {
                    { SSL_CLIENT_CERT_S_DN_CN, sslClientCertHeader }
                };
            }

            return request;
        }

        private HttpWebRequest CreateHttpWebRequest(string requestUri)
        {
            var request = WebRequest.CreateHttp(requestUri);

            request.Method = POST;
            request.ContentType = XML_CONTENT_TYPE;
            request.KeepAlive = true;

            return request;
        }

        private WebResponse StreamSoapRequest(HttpWebRequest request, byte[] requestBody)
        {
            request.ContentLength = requestBody.LongLength;

            using (var stream = request.GetRequestStream())
                stream.Write(requestBody, 0, requestBody.Length);

            return GetWebResponseFromRequest(request);
        }

        private WebResponse GetWebResponseFromRequest(WebRequest request)
        {
            try
            {
                return request.GetResponse();
            }
            catch (WebException webException)
            {
                return webException.Response;
            }
        }

        private string GetBodyFromWebResponse(WebResponse response)
        {
            using (var responseStream = response.GetResponseStream())
                return ReadStreamToEnd(responseStream);
        }

        private string ReadStreamToEnd(Stream stream)
        {
            using (var reader = new StreamReader(stream))
                return reader.ReadToEnd();
        }
    }
}
