using EVCB_OCPP.WEBAPI.Models.WebAPI;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Security.Cryptography;
using System.Security.Policy;
using System.ServiceModel.Channels;
using System.Text;
using System.Threading.Tasks;

namespace EVCB_OCPP.WEBAPI.Services
{
    public class InternalHttpClient
    {
        //private const string DockerSelfUrl = "{0}://host.docker.internal:{1}";
        private const string DockerSelfUrl = "http://localhost";
        private readonly HttpClientService httpClient;
        private readonly bool isInDocker;

        public InternalHttpClient(IConfiguration configuration)
        {
            httpClient = new HttpClientService();
            isInDocker = !string.IsNullOrEmpty(configuration["DOTNET_RUNNING_IN_CONTAINER"]);
        }

        async public Task<InternalHttpResult> Post(string baseUrl, string resource, Dictionary<string, string> headers, object requestBody, string saltkey)
        {
            InternalHttpResult result = new InternalHttpResult() { Success = false };
            string url = GetRedirctedUrl(baseUrl, resource);
            string responseMsg = "";

            try
            {
                string body = PreAction(url, ref headers, requestBody, saltkey);
                HttpResponse _response = await httpClient.PostJsonAsync(url, body, headers);
                responseMsg = _response.Response;
                JObject jo = null;

                try
                {
                    jo = JObject.Parse(responseMsg);
                }
                catch
                {
                    result.Status = _response.StatusCode;
                    result.Success = _response.IsSuccessStatusCode;
                    result.Message = _response.Response;
                    result.Exception = _response.Exception;

                    return result;
                }

                if (_response.IsSuccessStatusCode)
                {
                    if (jo.ContainsKey("SerialNo"))
                    {
                        string _SerialNo = jo["SerialNo"].Value<string>();
                        result.SerialNo = _SerialNo;
                    }

                    if (jo.ContainsKey("Message"))
                    {
                        string _Message = jo["Message"].Value<string>();
                        result.Message = _Message;
                    }

                }
                else
                {
                    if (jo.ContainsKey("Message"))
                    {
                        string _Message = jo["Message"].Value<string>();
                        result.Message = _Message;
                    }

                    if (jo.ContainsKey("Code"))
                    {
                        int _ErrorCode = jo["Code"].Value<int>();
                        result.ErrorCode = _ErrorCode;
                    }
                }

                result.Status = _response.StatusCode;
                result.Success = _response.IsSuccessStatusCode;
                result.Exception = _response.Exception;


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

            return result;
        }

        async public Task<InternalHttpResult> Get(string baseUrl, string resource, Dictionary<string, string> headers, string saltkey)
        {
            InternalHttpResult result = new InternalHttpResult() { Success = false };
            string url = GetRedirctedUrl(baseUrl, resource);

            try
            {
                string body = PreAction(url, ref headers, null, saltkey);
                var _response = await httpClient.GetJsonAsync(url, headers);
                if (_response.IsSuccessStatusCode)
                {
                    JObject jo = JObject.Parse(_response.Response);

                    if (jo.ContainsKey("SerialNo"))
                    {
                        string _SerialNo = jo["SerialNo"].Value<string>();
                        result.SerialNo = _SerialNo;
                    }

                    if (jo.ContainsKey("Message"))
                    {
                        string _Message = jo["Message"].Value<string>();
                        result.Message = _Message;
                    }

                }
                else
                {
                    JObject jo = JObject.Parse(_response.Response);

                    if (jo.ContainsKey("Message"))
                    {
                        string _Message = jo["Message"].Value<string>();
                        result.Message = _Message;
                    }

                    if (jo.ContainsKey("Code"))
                    {
                        int _ErrorCode = jo["Code"].Value<int>();
                        result.ErrorCode = _ErrorCode;
                    }
                }

                result.Status = _response.StatusCode;
                result.Success = _response.IsSuccessStatusCode;
                result.Exception = _response.Exception;


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

            return result;
        }

        async public Task<InternalHttpResult> Delete(string baseUrl, string resource, Dictionary<string, string> headers, string saltkey)
        {
            InternalHttpResult result = new InternalHttpResult() { Success = false };
            string url = GetRedirctedUrl(baseUrl, resource);

            try
            {
                string body = PreAction(url, ref headers, null, saltkey);
                var _response = await httpClient.DeleteJsonAsync(url, headers);
                if (_response.IsSuccessStatusCode)
                {
                    JObject jo = JObject.Parse(_response.Response);

                    if (jo.ContainsKey("SerialNo"))
                    {
                        string _SerialNo = jo["SerialNo"].Value<string>();
                        result.SerialNo = _SerialNo;
                    }

                    if (jo.ContainsKey("Message"))
                    {
                        string _Message = jo["Message"].Value<string>();
                        result.Message = _Message;
                    }

                }
                else
                {
                    JObject jo = JObject.Parse(_response.Response);

                    if (jo.ContainsKey("Message"))
                    {
                        string _Message = jo["Message"].Value<string>();
                        result.Message = _Message;
                    }

                    if (jo.ContainsKey("Code"))
                    {
                        int _ErrorCode = jo["Code"].Value<int>();
                        result.ErrorCode = _ErrorCode;
                    }
                }

                result.Status = _response.StatusCode;
                result.Success = _response.IsSuccessStatusCode;
                result.Exception = _response.Exception;


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

            return result;
        }

        async public Task<InternalHttpResult> Put(string baseUrl, string resource, Dictionary<string, string> headers, object requestBody, string saltkey)
        {
            InternalHttpResult result = new InternalHttpResult() { Success = false };
            string url = GetRedirctedUrl(baseUrl, resource);

            try
            {
                string body = PreAction(url, ref headers, requestBody, saltkey);
                var _response = await httpClient.PutJsonAsync(url, body, headers);
                if (_response.IsSuccessStatusCode)
                {
                    JObject jo = JObject.Parse(_response.Response);

                    if (jo.ContainsKey("SerialNo"))
                    {
                        string _SerialNo = jo["SerialNo"].Value<string>();
                        result.SerialNo = _SerialNo;
                    }

                    if (jo.ContainsKey("Message"))
                    {
                        string _Message = jo["Message"].Value<string>();
                        result.Message = _Message;
                    }

                }
                else
                {
                    JObject jo = JObject.Parse(_response.Response);

                    if (jo.ContainsKey("Message"))
                    {
                        string _Message = jo["Message"].Value<string>();
                        result.Message = _Message;
                    }

                    if (jo.ContainsKey("Code"))
                    {
                        int _ErrorCode = jo["Code"].Value<int>();
                        result.ErrorCode = _ErrorCode;
                    }
                }

                result.Status = _response.StatusCode;
                result.Success = _response.IsSuccessStatusCode;
                result.Exception = _response.Exception;


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

            return result;
        }

        private string PreAction(string url, ref Dictionary<string, string> headers, object requestBody, string saltkey)
        {
            var _body = requestBody == null ? "" : JsonConvert.SerializeObject(requestBody, EVCBConfiguration.JSONSERIALIZER_FORMAT);
            headers.Add("Timestamp", DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString());
            string signature = GetSignature(GetUnencodeText( url, _body, headers["Timestamp"], headers["PartnerId"], saltkey));
            headers.Add("Signature", signature);

            return _body;
        }


        private string GetUnencodeText( string url, string body, string timestamp, string partnerId, string saltkey)
        {

            string tempText = url.Substring(url.IndexOf('?') + 1).ToLower();
            tempText = tempText.StartsWith("http") ? string.Empty : tempText;
            body = tempText+ body;            
            string unencodeText = string.Format("{0}{1}{2}{3}", body, timestamp, partnerId, saltkey).ToLower();

            return unencodeText;
        }

        private string GetSignature(string unencodeText)
        {
            if ((unencodeText == null) || (unencodeText.Length == 0))
            {
                return String.Empty;
            }
            unencodeText = unencodeText.ToLower();

            MD5 md5 = new MD5CryptoServiceProvider();
            byte[] textToHash = Encoding.UTF8.GetBytes(unencodeText);
            byte[] result = md5.ComputeHash(textToHash);
            return BitConverter.ToString(result).Replace("-", "").ToLower();
        }

        private string GetRedirctedUrl(string baseUrl, string resource)
        {
            string url = baseUrl;
            if (isInDocker)
            {
                var baseUri = new Uri(url);
                url = string.Format(DockerSelfUrl, baseUri.Scheme, baseUri.Port);
            }
            url = url + resource;
            return url;
        }
    }

    public class InternalHttpResult : InternalGenericResponse
    {
        public int ErrorCode { set; get; }

        public bool Success { set; get; }

        public HttpStatusCode Status { set; get; }

        public Exception Exception { set; get; }



    }

}