using OF_DL.Helpers;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text;

namespace WidevineClient
{
    class HttpUtil
    {
        public static HttpClient Client { get; set; } = new HttpClient(new HttpClientHandler
        {
            AllowAutoRedirect = true,
            //Proxy = null
        });

        public static async Task<byte[]> PostData(string URL, Dictionary<string, string> headers, string postData)
        {
            var mediaType = postData.StartsWith("{") ? "application/json" : "application/x-www-form-urlencoded";
            var response = await PerformOperation(async () =>
            {
                StringContent content = new StringContent(postData, Encoding.UTF8, mediaType);
                //ByteArrayContent content = new ByteArrayContent(postData);

                return await Post(URL, headers, content);
            });

            byte[] bytes = await response.Content.ReadAsByteArrayAsync();
            return bytes;
        }

        public static async Task<byte[]> PostData(string URL, Dictionary<string, string> headers, byte[] postData)
        {
            var response = await PerformOperation(async () =>
            {
                ByteArrayContent content = new ByteArrayContent(postData);

                return await Post(URL, headers, content);
            });

            byte[] bytes = await response.Content.ReadAsByteArrayAsync();
            return bytes;
        }

        public static async Task<byte[]> PostData(string URL, Dictionary<string, string> headers, Dictionary<string, string> postData)
        {
            var response = await PerformOperation(async () =>
            {
                FormUrlEncodedContent content = new FormUrlEncodedContent(postData);

                return await Post(URL, headers, content);
            });

            byte[] bytes = await response.Content.ReadAsByteArrayAsync();
            return bytes;
        }

        public static async Task<string> GetWebSource(string URL, Dictionary<string, string> headers = null)
        {
            var response = await PerformOperation(async () =>
            {
                return await Get(URL, headers);
            });

            byte[] bytes = await response.Content.ReadAsByteArrayAsync();
            return Encoding.UTF8.GetString(bytes);
        }

        public static async Task<byte[]> GetBinary(string URL, Dictionary<string, string> headers = null)
        {
            var response = await PerformOperation(async () =>
            {
                return await Get(URL, headers);
            });

            byte[] bytes = await response.Content.ReadAsByteArrayAsync();
            return bytes;
        }
        public static string GetString(byte[] bytes)
        {
            return Encoding.UTF8.GetString(bytes);
        }

        private static async Task<HttpResponseMessage> Get(string URL, Dictionary<string, string> headers = null)
        {
            HttpRequestMessage request = new HttpRequestMessage()
            {
                RequestUri = new Uri(URL),
                Method = HttpMethod.Get
            };

            if (headers != null)
                foreach (KeyValuePair<string, string> header in headers)
                    request.Headers.TryAddWithoutValidation(header.Key, header.Value);

            return await Send(request);
        }

        private static async Task<HttpResponseMessage> Post(string URL, Dictionary<string, string> headers, HttpContent content)
        {
            HttpRequestMessage request = new HttpRequestMessage()
            {
                RequestUri = new Uri(URL),
                Method = HttpMethod.Post,
                Content = content
            };

            if (headers != null)
                foreach (KeyValuePair<string, string> header in headers)
                    request.Headers.TryAddWithoutValidation(header.Key, header.Value);

            return await Send(request);
        }

        private static async Task<HttpResponseMessage> Send(HttpRequestMessage request)
        {
            return await Client.SendAsync(request);
        }

        private static async Task<HttpResponseMessage> PerformOperation(Func<Task<HttpResponseMessage>> operation)
        {
            var response = await operation();

            var retryCount = 0;

            while (retryCount < Constants.WIDEVINE_MAX_RETRIES && response.StatusCode == System.Net.HttpStatusCode.TooManyRequests)
            {
                //
                // We've hit a rate limit, so we should wait before retrying.
                //
                var retryAfterSeconds = Constants.WIDEVINE_RETRY_DELAY * (retryCount + 1); // Default retry time. Increases with each retry.
                if (response.Headers.RetryAfter != null && response.Headers.RetryAfter.Delta.HasValue)
                {
                    if (response.Headers.RetryAfter.Delta.Value.TotalSeconds > 0)
                        retryAfterSeconds = (int)response.Headers.RetryAfter.Delta.Value.TotalSeconds + 1; // Add 1 second to ensure we wait a bit longer than the suggested time
                }

                await Task.Delay(retryAfterSeconds * 1000); // Peform the delay

                response = await operation();
                retryCount++;
            }

            response.EnsureSuccessStatusCode(); // Throw an exception if the response is not successful

            return response;
        }
    }
}
