using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web;
namespace EasyDevCore.Remote.HttpAccess
{
    /// <summary>
    /// 
    /// </summary>
    public static class HttpClientRequestExtensions
    {
        /// <summary>
        /// set the base URL.
        /// </summary>
        /// <param name="httpClient">The HTTP client.</param>
        /// <param name="baseUrl">The base URL.</param>
        /// <returns></returns>
        public static HttpClient BaseUrl(this HttpClient httpClient, string baseUrl)
        {
            httpClient.BaseAddress = new Uri(baseUrl);
            return httpClient;
        }

        /// <summary>
        /// Posts as parameter asynchronous.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="httpClient">The HTTP client.</param>
        /// <param name="url">The URL.</param>
        /// <param name="data">The data.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns></returns>
        public static async Task<HttpResponseMessage> PostAsParamAsync<T>(this HttpClient httpClient, string url, T data, CancellationToken cancellationToken = default)
        {
            if (typeof(T).IsArray)
            {
                StringBuilder sb = new StringBuilder(url);
                foreach (var item in data as object[])
                {
                    sb.Append("/" + HttpUtility.UrlEncode(item.ToInvariantString()));
                }
                return await httpClient.PostAsync(sb.ToString(), (HttpContent)null, cancellationToken).ConfigureAwait(false);
            }
            else
            {
                var argValues = data.GetType().GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance)
                    .ToDictionary(p => p.Name, p => p.GetValue(data).ToInvariantString());
                var formContent = new FormUrlEncodedContent(argValues); //HttpAccessSetup.UrlEncodedSerializerRequest.Serialize(argValues);
                return await httpClient.PostAsync(url, formContent, cancellationToken).ConfigureAwait(false);
            }
        }

        /// <summary>
        /// Gets the asynchronous.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="httpClient">The HTTP client.</param>
        /// <param name="url">The URL.</param>
        /// <param name="data">The data.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns></returns>
        public static async Task<HttpResponseMessage> GetAsync<T>(this HttpClient httpClient, string url, T data, CancellationToken cancellationToken = default)
        {
            if (typeof(T).IsArray)
            {
                var argValues = (data as object[]).Select(v => HttpUtility.UrlEncode(v.ToInvariantString()));
                var formContent = string.Join("/", argValues);
                return await httpClient.GetAsync(url + "/" + formContent, cancellationToken).ConfigureAwait(false);
            }
            else
            {
                var argValues = data.GetType().GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance)
                                    .Select(p => $"{p.Name}={HttpUtility.UrlEncode(p.GetValue(data).ToInvariantString())}");
                var formContent = string.Join("&", argValues);
                return await httpClient.GetAsync(url + "/" + formContent, cancellationToken).ConfigureAwait(false);
            }
        }

        /// <summary>
        /// Returns a string that represents the current object, using CultureInfo.InvariantCulture where possible.
        /// Dates are represented in IS0 8601.
        /// </summary>
        public static string ToInvariantString(this object obj)
        {
            // inspired by: http://stackoverflow.com/a/19570016/62600
            return
                obj == null ? null :
                obj is DateTime dt ? dt.ToString("o", CultureInfo.InvariantCulture) :
                obj is DateTimeOffset dto ? dto.ToString("o", CultureInfo.InvariantCulture) :
                obj is IConvertible c ? c.ToString(CultureInfo.InvariantCulture) :
                obj is IFormattable f ? f.ToString(null, CultureInfo.InvariantCulture) :
                obj.ToString();
        }

    }

}
