using GLS.MyGLS.ServiceData.APIDTOs.LabelOperations;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace csharp_clients
{
    public class RestClient
    {
        public static string ServiceName = string.Empty;

        public static string _url = "https://api.test.mygls.hu/";

        #region "Parcel service"
        /// <summary>
        /// Label(s) generation by the service.
        /// </summary>
        public static async Task PrintLabelsAsync(bool isXmlFormat)
        {
            //Test request:
            var request = new GLS.MyGLS.ServiceData.APIDTOs.LabelOperations.PrintLabelsRequest()
            {
                Username = Program._username,
                Password = Program.Sha512(Program._password),
                ParcelList = PrepareParcelData(),
                PrintPosition = 1,
                ShowPrintDialog = false
            };

            string data = SerializeFromObject(request, isXmlFormat);

            //Service calling:
            string response = await PostDataAsync("PrintLabels", data, isXmlFormat);

            GLS.MyGLS.ServiceData.APIDTOs.LabelOperations.PrintLabelsResponse printLabelsResponse = DeserializeToObject<GLS.MyGLS.ServiceData.APIDTOs.LabelOperations.PrintLabelsResponse>(response, isXmlFormat);

            if (printLabelsResponse != null && printLabelsResponse.PrintLabelsErrorList.Count == 0 && printLabelsResponse.Labels.Length > 0)
            {
                //Label(s) saving:
                File.WriteAllBytes("csharp_clients_rest_PrintLabels.pdf", printLabelsResponse.Labels);
            }
        }

        /// <summary>
        /// Preparing label(s) by the service.
        /// </summary>
        public static async Task<string> PrepareLabelsAsync(bool isXmlFormat)
        {
            //Test request:
            var request = new GLS.MyGLS.ServiceData.APIDTOs.LabelOperations.PrepareLabelsRequest()
            {
                Username = Program._username,
                Password = Program.Sha512(Program._password),
                ParcelList = PrepareParcelData()                
            };

            string data = SerializeFromObject(request, isXmlFormat);

            //Service calling:
            string response = await PostDataAsync("PrepareLabels", data, isXmlFormat);

            GLS.MyGLS.ServiceData.APIDTOs.LabelOperations.PrepareLabelsResponse prepareLabelsResponse = DeserializeToObject<GLS.MyGLS.ServiceData.APIDTOs.LabelOperations.PrepareLabelsResponse>(response, isXmlFormat);

            string parcelIds = string.Empty;
            if (prepareLabelsResponse != null && prepareLabelsResponse.PrepareLabelsError.Count == 0 && prepareLabelsResponse.ParcelInfoList.Count > 0)
            {
                foreach (var p in prepareLabelsResponse.ParcelInfoList)
                {
                    if (parcelIds == string.Empty)
                    {
                        parcelIds = p.ParcelId.ToString();
                    }
                    else
                    {
                        parcelIds += "," + p.ParcelId.ToString();
                    }
                }
            }

            return parcelIds;
        }

        /// <summary>
        /// Get label(s) by the service.
        /// </summary>
        public static async Task GetPrintedLabelsAsync(string parcelIds, bool isXmlFormat)
        {
            List<int> parcelIdList = parcelIds.Split(',').Select(i => int.Parse(i)).ToList();

            //Test request:
            var request = new GLS.MyGLS.ServiceData.APIDTOs.LabelOperations.GetPrintedLabelsRequest()
            {
                Username = Program._username,
                Password = Program.Sha512(Program._password),
                ParcelIdList = parcelIdList,
                PrintPosition = 1,
                ShowPrintDialog = false
            };

            string data = SerializeFromObject(request, isXmlFormat);

            //Service calling:
            string response = await PostDataAsync("GetPrintedLabels", data, isXmlFormat);

            GLS.MyGLS.ServiceData.APIDTOs.LabelOperations.GetPrintedLabelsResponse getPrintedLabelsResponse = DeserializeToObject<GLS.MyGLS.ServiceData.APIDTOs.LabelOperations.GetPrintedLabelsResponse>(response, isXmlFormat);

            if (getPrintedLabelsResponse != null && getPrintedLabelsResponse.GetPrintedLabelsErrorList.Count == 0 && getPrintedLabelsResponse.Labels.Length > 0)
            {
                //Label(s) saving:
                File.WriteAllBytes("csharp_clients_rest_GetPrintedLabels.pdf", getPrintedLabelsResponse.Labels);
            }
        }

        /// <summary>
        /// Get parcel(s) information by date ranges.
        /// </summary>
        public static async Task GetParcelListAsync(bool isXmlFormat)
        {
            //Test request:
            var request = new GLS.MyGLS.ServiceData.APIDTOs.ParcelOperations.GetParcelListRequest()
            {
                Username = Program._username,
                Password = Program.Sha512(Program._password),
                PickupDateFrom = new DateTime(2020, 04, 16, 0, 0, 0),
                PickupDateTo = new DateTime(2020, 04, 16, 0, 0, 0),
                PrintDateFrom = null,
                PrintDateTo = null
            };

            string data = SerializeFromObject(request, isXmlFormat);

            //Service calling:
            string response = await PostDataAsync("GetParcelList", data, isXmlFormat);

            GLS.MyGLS.ServiceData.APIDTOs.ParcelOperations.GetParcelListResponse getParcelListResponse = DeserializeToObject<GLS.MyGLS.ServiceData.APIDTOs.ParcelOperations.GetParcelListResponse>(response, isXmlFormat);

            Console.WriteLine(getParcelListResponse.GetParcelListErrors.Count());
            Console.WriteLine(getParcelListResponse.PrintDataInfoList.Count());
        }

        /// <summary>
        /// Get parcel statuses.
        /// </summary>
        public static async Task GetParcelStatusesAsync(bool isXmlFormat)
        {
            //Test request:
            var request = new GLS.MyGLS.ServiceData.APIDTOs.ParcelOperations.GetParcelStatusesRequest()
            {
                Username = Program._username,
                Password = Program.Sha512(Program._password),
                ReturnPOD = true,
                ParcelNumber = 0,
                LanguageIsoCode = "HU"
            };

            string data = SerializeFromObject(request, isXmlFormat);

            //Service calling:
            string response = await PostDataAsync("GetParcelStatuses", data, isXmlFormat);

            GLS.MyGLS.ServiceData.APIDTOs.ParcelOperations.GetParcelStatusResponse getParcelStatusResponse = DeserializeToObject<GLS.MyGLS.ServiceData.APIDTOs.ParcelOperations.GetParcelStatusResponse>(response, isXmlFormat);

            if (getParcelStatusResponse != null)
            {
                Console.WriteLine(getParcelStatusResponse.GetParcelStatusErrors.Count());
                if (getParcelStatusResponse.GetParcelStatusErrors.Count == 0 && getParcelStatusResponse.POD?.Length > 0)
                {
                    //POD saving:
                    File.WriteAllBytes("csharp_clients_rest_GetParcelStatuses.pdf", getParcelStatusResponse.POD);
                }
            }
        }
        #endregion

        #region "utility functions"
        /// <summary>
        /// Object serialization.
        /// </summary>
        public static string SerializeFromObject(object obj, bool isXmlFormat)
        {
            string data = string.Empty;

            if (obj == null)
            {
                return data;
            }

            try
            {
                XmlObjectSerializer serializer = isXmlFormat ? (XmlObjectSerializer)new DataContractSerializer(obj.GetType()) : new DataContractJsonSerializer(obj.GetType());

                using (MemoryStream msObj = new MemoryStream())
                {
                    serializer.WriteObject(msObj, obj);
                    return Encoding.UTF8.GetString(msObj.ToArray());
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }

            return data;
        }

        /// <summary>
        /// Object deserialization.
        /// </summary>
        public static T DeserializeToObject<T>(string data, bool isXmlFormat)
        {
            T result = default(T);

            if (data == null)
            {
                return result;
            }

            try
            {
                XmlObjectSerializer serializer = isXmlFormat ? (XmlObjectSerializer)new DataContractSerializer(typeof(T)) : new DataContractJsonSerializer(typeof(T));

                using (var stream = data.ToStream())
                {
                    result = (T)serializer.ReadObject(stream);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }

            return result;
        }

        /// <summary>
        /// Data sending.
        /// </summary>
        public static async Task<string> PostDataAsync(string methodName, string data, bool isXmlFormat)
        {
            string format = isXmlFormat ? "xml" : "json";

            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

            var uri = new Uri(_url + ServiceName + ".svc" + $"/{format}/{methodName}");
            using (var httpClient = new HttpClient())
            {
                httpClient.Timeout = TimeSpan.FromMilliseconds(600000);

                using (var stringContent = new StringContent(data, Encoding.UTF8, $"application/{format}"))
                using (var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromMinutes(10)))
                using (var response = await httpClient.PostAsync(uri, stringContent, cancellationTokenSource.Token))
                {
                    var content = await response.Content.ReadAsStringAsync();
                    if (false == response.IsSuccessStatusCode)
                    {
                        throw new ApiException
                        {
                            StatusCode = (int)response.StatusCode,
                            Content = content
                        };
                    }
                    return content;
                }
            }
        }

        /// <summary>
        /// Preparing parcel data.
        /// </summary>
        public static List<GLS.MyGLS.ServiceData.APIDTOs.LabelOperations.Parcel> PrepareParcelData()
        {
            List<GLS.MyGLS.ServiceData.APIDTOs.LabelOperations.Parcel> parcelList = new List<GLS.MyGLS.ServiceData.APIDTOs.LabelOperations.Parcel>();

            var parcel = new GLS.MyGLS.ServiceData.APIDTOs.LabelOperations.Parcel()
            {
                ClientNumber = Program._clientNumber,
                ClientReference = "TEST PARCEL",
                CODAmount = 0,
                CODReference = "COD REFERENCE",
                Content = "CONTENT",
                Count = 1,
                PickupAddress = new GLS.MyGLS.ServiceData.APIDTOs.LabelOperations.Address()
                {
                    ContactName = "Contact Name",
                    ContactPhone = "+36701234567",
                    ContactEmail = "something@anything.hu",
                    Name = "Pickup Address",
                    Street = "Európa u. ",
                    HouseNumber = "2",
                    City = "Alsónémedi",
                    ZipCode = "2351",
                    CountryIsoCode = "HU",
                    HouseNumberInfo = "/a"
                },
                DeliveryAddress = new GLS.MyGLS.ServiceData.APIDTOs.LabelOperations.Address()
                {
                    ContactName = "Contact Name",
                    ContactPhone = "+36701234567",
                    ContactEmail = "something@anything.hu",

                    Name = "Delivery Address",
                    Street = "Európa u. ",
                    HouseNumber = "2",
                    City = "Alsónémedi",
                    ZipCode = "2351",
                    CountryIsoCode = "HU",
                    HouseNumberInfo = "/b"
                },
                PickupDate = new DateTime(2019, 12, 14, 0, 0, 0),
                ServiceList = new List<Service>()
                {
                    new Service() { Code = "PSD", PSDParameter = new ServiceParameterStringInteger() { StringValue = "2351-CSOMAGPONT" } }
                }
            };

            parcelList.Add(parcel);

            return parcelList;
        }
        #endregion
    }
}