// Depends on UniTask to support cancellation token and GetAwaiter: https://github.com/Cysharp/UniTask
// Otherwise, the code can be adapted using https://gist.github.com/krzys-h/9062552e33dd7bd7fe4a6c12db109a1a

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
//using Cysharp.Threading.Tasks;

using UnityEngine;
using UnityEngine.Networking;

namespace ETSI.ARF.OpenAPI
{
    public interface IHttpClient
    {
        public Uri BaseAddress { get; set; }
        public HttpRequestHeaders DefaultRequestHeaders { get; }

        public Task<HttpResponseMessage> SendAsync(HttpRequestMessage message, HttpCompletionOption option,
            CancellationToken token);

        public void Dispose();
    }

    public class UnityWebRequestHttpClient : IHttpClient
    {
        public UnityWebRequestHttpClient() { }

        public UnityWebRequestHttpClient(string baseUri)
        {
            BaseAddress = new Uri(baseUri);
        }

        public UnityWebRequestHttpClient(Uri baseUri)
        {
            BaseAddress = baseUri;
        }

        public Uri BaseAddress { get; set; }
        public HttpRequestHeaders DefaultRequestHeaders => _httpClient.DefaultRequestHeaders;

        private readonly HttpClient _httpClient = new HttpClient();

        public async Task<HttpResponseMessage> SendAsync(HttpRequestMessage message, HttpCompletionOption option, CancellationToken token)
        {
            var content = await (message.Content?.ReadAsStringAsync() ?? Task.FromResult(""));
            var webRequest = GetUnityWebRequest(message.Method.Method, message.RequestUri, content);

            AppendHeaders(webRequest);

            Debug.Log("[HTTP] Request " + webRequest.uri.ToString());
            try
            {
                //SylR
                webRequest.SendWebRequest();
                while (!webRequest.isDone)
                {
                    if (token.IsCancellationRequested)
                    {
                        Debug.Log($"Task '{ message.RequestUri }' cancelled");
                        token.ThrowIfCancellationRequested();
                    }
                    await Task.Yield();
                }

                //await webRequest
                //    .SendWebRequest()
                //        .WithCancellation(cancellationToken: token);
            }
            catch (Exception)
            {
                webRequest.Dispose();
                throw;
            }

            Debug.Log("[HTTP] Result: " + webRequest.result.ToString());

            var responseMessage = CreateHttpResponseMessage(webRequest);
            webRequest.Dispose();

            Debug.Log("[HTTP] Response len: " + responseMessage.Content.Headers.ContentLength);

            return responseMessage;
        }

        public void Dispose()
        {
            _httpClient.Dispose();
            DefaultRequestHeaders.Clear();
            BaseAddress = null;
        }

        private UnityWebRequest GetUnityWebRequest(string method, Uri endpoint, string content = "")
        {
            var requestUri = BaseAddress.AbsoluteUri + endpoint;
            var webRequest = UnityWebRequest.Get(requestUri);
            webRequest.method = method;

            webRequest.disposeUploadHandlerOnDispose = true;
            webRequest.disposeDownloadHandlerOnDispose = true;

            if (!string.IsNullOrEmpty(content))
            {
                var data = new System.Text.UTF8Encoding().GetBytes(content);
                webRequest.uploadHandler = new UploadHandlerRaw(data);
                webRequest.SetRequestHeader("Content-Type", "application/json");
                //webRequest.SetRequestHeader("Content-Type", "image/jpeg");
            }
            return webRequest;
        }

        private void AppendHeaders(UnityWebRequest webRequest)
        {
            using var enumerator = DefaultRequestHeaders.GetEnumerator();

            while (enumerator.MoveNext())
            {
                var (key, value) = enumerator.Current;
                webRequest.SetRequestHeader(key, value.First());
            }
        }

        private HttpResponseMessage CreateHttpResponseMessage(UnityWebRequest webRequest)
        {
            var responseContent = webRequest.downloadHandler?.text;

            var response = new HttpResponseMessage();
            response.Content = new StringContent(responseContent);
            response.StatusCode = (HttpStatusCode)webRequest.responseCode;

            Dictionary<string, string> headers = webRequest.GetResponseHeaders();

            if (headers != null)
            {
                Debug.Log("[HTTP] Header: " + headers.Count.ToString());
                foreach (var h in headers)
                {
                    switch (h.Key.ToLower().Trim())
                    {
                        case "content-type":
                            {
                                var trimmed = h.Value.ToLower().Split(";").FirstOrDefault();
                                response.Content.Headers.ContentType = new MediaTypeHeaderValue(trimmed);
                                break;
                            }
                        case "content-length":
                            response.Content.Headers.ContentLength = long.Parse(h.Value);
                            break;

                        default:
                            if (h.Value == "gzip")
                            {
                                // bug???
                            }
                            else
                                response.Headers.Add(h.Key, h.Value);
                            break;
                    }
                }
            }
            return response;
        }
    }

}