namespace Kit.Helpers
{
    using System;
    using System.Collections.Generic;
    using System.Net.Http;
    using System.Text;
    using System.Threading.Tasks;

    public class FluentHttpClient
    {
        private readonly Uri _requestUrl;
        private Func<HttpResponseMessage, string, Task<string>> _successHandler = null;
        private Func<HttpResponseMessage, string, Task<string>> _errorHandler = null;
        private string _securityKey = null;

        private readonly List<KeyValuePair<string, string>> _parameters = new List<KeyValuePair<string, string>>();
        private readonly HttpMethod _httpMethod;
        private string _postStringContent = string.Empty;
        private string _postJsonContent = string.Empty;

        public FluentHttpClient(Uri requestUrl, HttpMethod httpMethod)
        {
            _requestUrl = requestUrl;

            if (httpMethod != HttpMethod.Get && httpMethod != HttpMethod.Post) throw new ArgumentOutOfRangeException("httpMethod", "only POST and GET methods supported");
            _httpMethod = httpMethod;
        }

        #region prepare methods

        public FluentHttpClient WithParameterNullable<TObject>(string name, TObject value)
        {
            if (value != null)
            {
                _parameters.Add(new KeyValuePair<string, string>(name, value?.ToString()));
            }
            return this;
        }

        public FluentHttpClient WithParameter<TObject>(string name, TObject value)
        {
            _parameters.Add(new KeyValuePair<string, string>(name, value?.ToString()));
            return this;
        }

        public FluentHttpClient WithStringContent(string content)
        {
            if (_httpMethod != HttpMethod.Post) throw new ArgumentOutOfRangeException("httpMethod", "only for POST method string content supported");
            _postStringContent = content;
            return this;
        }

        public FluentHttpClient WithJsonContent<TObject>(TObject obj)
        {
            if (_httpMethod != HttpMethod.Post) throw new ArgumentOutOfRangeException("httpMethod", "only for POST method string content supported");
            _postJsonContent = obj.JsonSerialize();
            return this;
        }

        public FluentHttpClient WithSecurityKey(string securityKey)
        {
            _securityKey = securityKey;
            return this;
        }

        public FluentHttpClient WithSuccessHandler(Func<HttpResponseMessage, string, Task<string>> responseHandler)
        {
            _successHandler = responseHandler;
            return this;
        }

        public FluentHttpClient WithErrorHandler(Func<HttpResponseMessage, string, Task<string>> errorHandler)
        {
            _errorHandler = errorHandler;
            return this;
        }

        #endregion

        #region finish methods

        public async Task<string> ExecuteRequest()
        {
            Func<HttpClient, Task<HttpResponseMessage>> createRequestMethod;
            if (_httpMethod == HttpMethod.Get)
            {
                // классический GET запрос
                createRequestMethod = async x => await x.GetAsync(_requestUrl.SetQueryParameters(_parameters)).ConfigureAwait(false);
            }
            else if (!string.IsNullOrWhiteSpace(_postStringContent))
            {
                // запрос с собственным содержанием тела
                createRequestMethod = async x => await x.PostAsync(_requestUrl.SetQueryParameters(_parameters), new StringContent(_postStringContent)).ConfigureAwait(false);
            }
            else if (!string.IsNullOrWhiteSpace(_postJsonContent))
            {
                // JSON запрос
                createRequestMethod = async x => await x.PostAsync(_requestUrl.SetQueryParameters(_parameters), new StringContent(_postJsonContent, Encoding.UTF8, "application/json")).ConfigureAwait(false);
            }
            else

            {
                // классический POST запрос
                createRequestMethod = async y => await y.PostAsync(_requestUrl, new FormUrlEncodedContent(_parameters)).ConfigureAwait(false);
            }

            return await HttpHelper.ExecuteRequest(createRequestMethod, _successHandler, _errorHandler, _securityKey).ConfigureAwait(false);
        }

        public async Task<Stream> ExecuteStreamRequest()
        {
            if (_httpMethod == HttpMethod.Get)
            {
                return await HttpHelper.ExecuteGetStreamUrl(_requestUrl, _parameters, null!, null!, _securityKey).ConfigureAwait(false);
            }
            return await HttpHelper.ExecutePostStreamUrl(_requestUrl, _parameters, null!, null!, _securityKey).ConfigureAwait(false);
        }

        #endregion
    }

    public static class FluentHttpClient_Extensions
    {
        public static FluentHttpClient PrepareHttpGet(this Uri requestUri)
        {
            return new FluentHttpClient(requestUri, HttpMethod.Get);
        }

        public static FluentHttpClient PrepareHttpPost(this Uri requestUri)
        {
            return new FluentHttpClient(requestUri, HttpMethod.Post);
        }
    }
}

//_createTokenUrl.HttpRequest()
//               .AddHeader("header1", "header1_value")
//               .AddCookie("cookie1", "cookie1_value")
//               .AddQueryStringParameter("qsp", "qsp_value")
//               .AddParameter("lifeTimeInSeconds", lifeTimeInSeconds)
//               .AddParameter("data", data)
//               .AddParameters(new List<KeyValuePair<string, object>> 
//               {
//                   new KeyValuePair<string, object>("k1", "v1"),
//                   new KeyValuePair<string, object>("k2", "v2")
//               })
//               .AddParameters(new { a = 5, b = "abc" })
//               .SendPost()
//               .OnSuccess(response =>
//               {
//                   // HttpResponseMessage response
//               })
//               .OnError(ex =>
//               {
//                   // exception handling
//               })
//               .ConvertToObject<string>()
//               .ConvertToCollection<string>();