using IVM.Models;
using Microsoft.Extensions.Primitives;
using Newtonsoft.Json;
using System.Text;
using System.Xml;

namespace IVM.Services
{
    public class JsonHelper
    {
        internal IFormCollection requestForm { get; }
        internal IQueryCollection Query { get; private set; }

        public Dictionary<string, StringValues> requestDict { get; private set; }
        public Dictionary<string, string[]> ArrayNeeded { get; private set; }

        public string[] ArrayNames { get; private set; }
        public string? ArrayName { get; private set; }
        public string JsonString { get; set; }
        public bool AllRecords { get; set; }

        public webClient WebClient { get; private set; }

        public XmlElement? Pagination { get; private set; }

        public JsonHelper(string startString, int arrayCount, string arrayName)
        {
            JsonString = startString;
            ArrayName = arrayName;

            if (ArrayName is not null)
            {
                if (ArrayNeeded is null)
                    ArrayNeeded = new Dictionary<string, string[]>();
                ArrayNeeded.Add(ArrayName, new string[arrayCount]);
            }
        }

        #region Constructors
        public JsonHelper(IFormCollection form, string arrayName) : this("{", arrayName is null ? 0 : form.Keys.Count((key) => key.Contains(arrayName)), arrayName)
        {
          requestForm = form;
          requestDict = requestForm.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
        }

        public JsonHelper(IQueryCollection query, string arrayName) : this(arrayName, arrayName is null ? 0 : query.Keys.Count((key) => key.Contains(arrayName)), arrayName)
        { 
          Query = query;
          requestDict = Query.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
        }

        public JsonHelper(webClient webClient)
        { WebClient = webClient; }

        public JsonHelper()
        { }

        #endregion


        #region JSon Serializers
        internal T DeserializeObject<T>(string restResponse)
        {
          object response = null;
          response = JsonConvert.DeserializeObject<T>(restResponse);

          return (T)response;
        }

        #endregion

        #region String Builders
        
        /// <summary>
        /// Builds JsonString for Search Criteria
        /// </summary>
        internal void BuildAddressJSon()
        {
            foreach (string aKey in requestDict.Keys)
              if (!string.IsNullOrEmpty(requestDict[aKey]) && !aKey.Contains("_input"))
                switch (aKey)
                {
                  case "AddressTypes":
                    JsonString += $"\"addressType\":{requestForm[aKey]},";
                    break;
                  case "Country":
                    string cCode = CountryModel.Countries.Where(x => x.id == int.Parse(s: requestDict[aKey].ToString())).ToList()[0].code;
                    JsonString += $"\"countryCode\":\"{cCode}\",";
                    break;
                  case "StateProvince":
                    int State_Id = StatesProvinces.StateProvince.Where(x => x.code == requestDict[aKey].ToString()).ToList()[0].id;
                    JsonString += $"\"stateProvinceId\":{State_Id},";
                    break;
                  case "virtual":
                  case "pageSize":
                  case "page":
                    break;
                  default:
                    JsonString += $"\"{aKey}\":\"{requestDict[aKey]}\",";
                    break;
                }

            //JsonString = $"?{JsonString.Substring(1)}";
            JsonString = JsonString.EndsWith(",") ? JsonString[0..^1] : JsonString;
            JsonString += "}";
        }

        internal void BuildAddressUrl()
        {
            foreach (string aKey in requestDict.Keys)
              if (!string.IsNullOrEmpty(requestDict[aKey]) && !aKey.Contains("_input"))
                switch (aKey)
                {
                  case "AddressTypes":
                    JsonString += $"&addressType={requestDict[aKey]}";
                    break;
                  case "Country":
                    string cCode = CountryModel.Countries.Where(x => x.id == int.Parse(s: requestDict[aKey].ToString())).ToList()[0].code;
                    JsonString += $"&countryCode={cCode}";
                    break;
                  case "StateProvince":
                    JsonString += $"&stateProvinceId={requestDict[aKey]}";
                    break;
                  case "virtual":
                    break;
                  default:
                    JsonString += $"&{aKey}={requestDict[aKey]}";
                    break;
                }
        }
        #endregion

        #region DataExtract
        /// <summary>
        /// Receives the response content from the API call and drills down on the data array in the JSon return object. <para/>Additionally, sets the Pagination XML Element if Pagination is present.
        /// </summary>
        /// <param name="responseText"></param>
        /// <returns>JSon formatted array of data [{"field":"value"}]</returns>
        internal string? GetDataArray(string? responseText)
        {
            string retval = null;
            int rposition = responseText.IndexOf("data") + 6; // + 6 accounts for Data": this sets the position of the data array retruned by request.
            int pposition = responseText.IndexOf("data") - 2; // - 2 reveals the pagination object on the request.
            if (Pagination == null && pposition > 0)
              Pagination = JsonConvert.DeserializeXmlNode($"{responseText.Substring(0, pposition)}}}").DocumentElement;

            if (!string.IsNullOrEmpty(responseText))
            {
                retval = responseText.Substring(rposition);
                retval = retval.EndsWith("}") ? retval.Substring(0, retval.Length - 1) : retval; // this omits the JSon object trailing }
            }

            return retval;
        }
        #endregion

        #region WebClent Calls
        
        internal string? GetRequest(string endpoint)
        {
            Task<HttpResponseMessage> response = WebClient.GetData(endpoint);
            do { }
            while (response.Result is null);
            return JHReader(response.Result.Content);
        }

        internal string? PutDataArray(string _endPoint)
        { Task<HttpResponseMessage> response = WebClient.PutData(_endPoint);
          do { }
          while (response.Result is null);

          return JHReader(response.Result.Content);
        }

        private string? JHReader(HttpContent content)
        { using var reader = new StreamReader(content.ReadAsStream());
          return reader.ReadToEnd();
        }

        internal string? DeleteData(string endpoint)
        {
            Task<HttpResponseMessage> response = WebClient.DeleteRequest(endpoint);
            do { }
            while (response.Result is null);
            return JHReader(response.Result.Content);
        }
        #endregion
    }
}
