using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;

namespace EVCB_OCPP.WSServer.Service
{
    public class HttpClientService
    {
        /// <summary>
        /// 要求逾時前等候的時間長度
        /// #網域名稱系統（DNS）查詢最多可能需要15秒的時間才會傳回或超時
        /// 預設60秒
        /// </summary>
        public int Timeout { get => _timeout; set => _timeout = value; }

        /// <summary>
        /// 取得或設定使用 HttpClient 物件提出要求時，所允許的同時連線 數目上限 (每個伺服器端點)。
        /// 請注意，此限制是按照每個伺服器端點計算，例如值 256 允許 http://www.adatum.com/ 使用 256 個同時連線，
        /// 而 http://www.adventure-works.com/ 另有 256 個同時連線。
        /// 預設100個
        /// </summary>
        public int MaxConnectionsPerServer { get => _maxConnectionsPerServer; set => _maxConnectionsPerServer = value; }


        /// <summary>
        /// 設定HttpMessageHandler的生命週期，如果其存留期間尚未過期，HttpMessageHandler 執行個體可從集區重複使用(建立新的 HttpClient 執行個體時)
        /// 因為處理常式通常會管理自己專屬的底層 HTTP 連線。 建立比所需數目更多的處理常式，可能會導致連線延遲。 有些處理常式也會保持連線無限期地開啟，這可能導致處理常式無法回應 DNS （網域名稱系統）變更。
        /// 預設處理常式存留時間為120秒。
        /// </summary>
        public int HandlerLifetime { get => _handlerLifetime; set => _handlerLifetime = value; }


        private static IHttpClientFactory _clientFactory = null;
        //private IServiceCollection _services = new ServiceCollection();
        private int _handlerLifetime = 2;
        private int _maxConnectionsPerServer = 300;
        private int _timeout = 60;


        public HttpClientService(string baseAddress = "")
        {
            Init(baseAddress);
        }


        private void Init(string baseAddress)
        {
            if (_clientFactory is not null)
            {
                return;
            }

            IServiceCollection _services = new ServiceCollection();
            _services.AddHttpClient("Default", c =>
            {
                if (!string.IsNullOrEmpty(baseAddress))
                {
                    c.BaseAddress = new Uri(baseAddress);
                }
                c.Timeout = TimeSpan.FromSeconds(_timeout);
                c.DefaultRequestHeaders.Add("Cache-Control", "no-cache");
            })
            .AddTypedClient<HttpClient>()
            .SetHandlerLifetime(TimeSpan.FromDays(1))
            .ConfigurePrimaryHttpMessageHandler((h =>
            {
                return new HttpClientHandler
                {
                    MaxConnectionsPerServer = _maxConnectionsPerServer,
                };
            }));

            _clientFactory = _services.BuildServiceProvider()
                     .GetRequiredService<IHttpClientFactory>();
        }


        public virtual async Task<HttpResponse> PostJsonAsync(string Url, string bodyData, Dictionary<string, string> headers, string clientName = "Default", bool bearerToken = false, string authorizationToken = null)
        {
            HttpResponse result = new HttpResponse() { IsError = false };

            try
            {
                var client = _clientFactory.CreateClient(clientName);

                if (!string.IsNullOrEmpty(authorizationToken))
                {
                    client.DefaultRequestHeaders.Authorization = bearerToken ? new AuthenticationHeaderValue("Bearer", authorizationToken) : new AuthenticationHeaderValue(authorizationToken);
                }
                if (headers != null)
                {
                    for (int idx = 0; idx < headers.Count; idx++)
                    {
                        client.DefaultRequestHeaders.Add(headers.ElementAt(idx).Key, headers.ElementAt(idx).Value);
                    }
                }

                HttpContent content = new StringContent(bodyData);
                content.Headers.ContentType = new MediaTypeWithQualityHeaderValue("application/json");
                content.Headers.ContentType.CharSet = "UTF-8";

                var response = await client.PostAsync(Url, content).ConfigureAwait(false);

                result.IsSuccessStatusCode = response.IsSuccessStatusCode;
                result.Headers = response.Headers;
                result.RequestMessage = response.RequestMessage;
                result.StatusCode = response.StatusCode;
                result.Response = await response.Content.ReadAsStringAsync();



            }
            catch (Exception ex)
            {
                result.IsError = true;
                result.Exception = ex;

            }


            return result;
        }

        public virtual async Task<HttpResponse> GetJsonAsync(string Url, Dictionary<string, string> headers, string clientName = "Default", bool bearerToken = false, string authorizationToken = null)
        {
            HttpResponse result = new HttpResponse() { IsError = false };

            try
            {
                var client = _clientFactory.CreateClient(clientName);

                if (!string.IsNullOrEmpty(authorizationToken))
                {
                    client.DefaultRequestHeaders.Authorization = bearerToken ? new AuthenticationHeaderValue("Bearer", authorizationToken) : new AuthenticationHeaderValue(authorizationToken);
                }

                if (headers != null)
                {
                    for (int idx = 0; idx < headers.Count; idx++)
                    {
                        client.DefaultRequestHeaders.Add(headers.ElementAt(idx).Key, headers.ElementAt(idx).Value);
                    }
                }

                // client.DefaultRequestHeaders.Add("Content-Type", "application/json");


                var response = await client.GetAsync(Url).ConfigureAwait(false);

                result.IsSuccessStatusCode = response.IsSuccessStatusCode;
                result.Headers = response.Headers;
                result.RequestMessage = response.RequestMessage;
                result.StatusCode = response.StatusCode;
                result.Response = await response.Content.ReadAsStringAsync();



            }
            catch (Exception ex)
            {
                result.IsError = true;
                result.Exception = ex;

            }


            return result;
        }

        public virtual async Task<HttpResponse> PutJsonAsync(string Url, string bodyData, Dictionary<string, string> headers, string clientName = "Default", bool bearerToken = false, string authorizationToken = null)
        {
            HttpResponse result = new HttpResponse() { IsError = false };

            try
            {
                var client = _clientFactory.CreateClient(clientName);

                if (!string.IsNullOrEmpty(authorizationToken))
                {
                    client.DefaultRequestHeaders.Authorization = bearerToken ? new AuthenticationHeaderValue("Bearer", authorizationToken) : new AuthenticationHeaderValue(authorizationToken);
                }
                if (headers != null)
                {
                    for (int idx = 0; idx < headers.Count; idx++)
                    {
                        client.DefaultRequestHeaders.Add(headers.ElementAt(idx).Key, headers.ElementAt(idx).Value);
                    }
                }

                HttpContent content = new StringContent(bodyData);
                content.Headers.ContentType = new MediaTypeWithQualityHeaderValue("application/json");
                content.Headers.ContentType.CharSet = "UTF-8";

                var response = await client.PutAsync(Url, content).ConfigureAwait(false);

                result.IsSuccessStatusCode = response.IsSuccessStatusCode;
                result.Headers = response.Headers;
                result.RequestMessage = response.RequestMessage;
                result.StatusCode = response.StatusCode;
                result.Response = await response.Content.ReadAsStringAsync();



            }
            catch (Exception ex)
            {
                result.IsError = true;
                result.Exception = ex;

            }


            return result;
        }

        public virtual async Task<HttpResponse> DeleteJsonAsync(string Url, Dictionary<string, string> headers, string clientName = "Default", bool bearerToken = false, string authorizationToken = null)
        {
            HttpResponse result = new HttpResponse() { IsError = false };

            try
            {
                var client = _clientFactory.CreateClient(clientName);

                if (!string.IsNullOrEmpty(authorizationToken))
                {
                    client.DefaultRequestHeaders.Authorization = bearerToken ? new AuthenticationHeaderValue("Bearer", authorizationToken) : new AuthenticationHeaderValue(authorizationToken);
                }

                if (headers != null)
                {
                    for (int idx = 0; idx < headers.Count; idx++)
                    {
                        client.DefaultRequestHeaders.Add(headers.ElementAt(idx).Key, headers.ElementAt(idx).Value);
                    }
                }

                //   client.DefaultRequestHeaders.Add("Content-Type", "application/json");


                var response = await client.DeleteAsync(Url).ConfigureAwait(false);

                result.IsSuccessStatusCode = response.IsSuccessStatusCode;
                result.Headers = response.Headers;
                result.RequestMessage = response.RequestMessage;
                result.StatusCode = response.StatusCode;
                result.Response = await response.Content.ReadAsStringAsync();



            }
            catch (Exception ex)
            {
                result.IsError = true;
                result.Exception = ex;

            }


            return result;
        }

        public virtual async Task<HttpResponse> PostFormDataAsync(string Url, Dictionary<string, string> bodyData, Dictionary<string, string> headers, string clientName = "Default", bool bearerToken = false, string authorizationToken = null)
        {
            HttpResponse result = new HttpResponse() { IsError = false };

            try
            {
                var client = _clientFactory.CreateClient(clientName);

                ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;

                if (!string.IsNullOrEmpty(authorizationToken))
                {
                    client.DefaultRequestHeaders.Authorization = bearerToken ? new AuthenticationHeaderValue("Bearer", authorizationToken) : new AuthenticationHeaderValue(authorizationToken);
                }
                if (headers != null)
                {
                    for (int idx = 0; idx < headers.Count; idx++)
                    {
                        client.DefaultRequestHeaders.Add(headers.ElementAt(idx).Key, headers.ElementAt(idx).Value);
                    }
                }

                var content = new MultipartFormDataContent();

                foreach (var keyValuePair in bodyData)
                {
                    content.Add(new StringContent(keyValuePair.Value), "\"" + keyValuePair.Key + "\"");
                }

                var response = await client.PostAsync(Url, content).ConfigureAwait(false);



                result.IsSuccessStatusCode = response.IsSuccessStatusCode;
                result.Headers = response.Headers;
                result.RequestMessage = response.RequestMessage;
                result.StatusCode = response.StatusCode;
                result.Response = await response.Content.ReadAsStringAsync();



            }
            catch (Exception ex)
            {
                result.IsError = true;
                result.Exception = ex;

            }


            return result;
        }



    }


    public class HttpResponse
    {
        public bool IsError { internal set; get; }

        public Exception Exception { internal set; get; }

        public string Response { internal set; get; }

        /// <summary>
        /// 摘要:
        ///     取得或設定 HTTP 回應的狀態碼。
        /// 傳回:
        ///    HTTP 回應的狀態碼。
        /// </summary>
        public HttpStatusCode StatusCode { get; internal set; }


        /// <summary>
        /// 摘要:
        ///      取得 HTTP 回應標頭的集合。
        /// 傳回:
        ///     HTTP 回應標頭的集合。
        /// </summary>
        public HttpResponseHeaders Headers { get; internal set; }


        /// <summary>
        /// 摘要:
        ///     取得或設定導致此回應訊息的要求訊息。
        /// 傳回:
        ///       導致此回應訊息的要求訊息。
        /// </summary>
        public HttpRequestMessage RequestMessage { get; internal set; }

        /// <summary>
        /// 摘要:
        ///   取得指示 HTTP 回應是否成功的值。
        /// 傳回:
        ///       指示 HTTP 回應是否成功的值。 如果 System.Net.Http.HttpResponseMessage.StatusCode 在 200-299
        ///     的範圍內，則為 true；否則為 false。
        /// </summary>
        public bool IsSuccessStatusCode { get; internal set; }
    }
}
