using Newtonsoft.Json;
using System;
using System.IO;
using System.Net;
using System.Text;
using TINK.Repository;
using TINK.Repository.Exception;
using TINK.Repository.Response;

namespace UITest.Fixtures.Connector
{
    /// <summary>
    /// Object which manages calls to copri.
    /// </summary>
    public class CopriCallsHttpsReference
    {
        /// <summary> Logs user in. </summary>
        /// <param name="copriHost">Host to connect to. </param>
        /// <param name="merchantId">Id of the merchant.</param>
        /// <param name="mailAddress">Mailaddress of user to log in.</param>
        /// <param name="password">Password to log in.</param>
        /// <param name="deviceId">Id specifying user and hardware.</param>
        /// <remarks>Response which holds auth cookie <see cref="ResponseBase.authcookie"/></remarks>
        public static AuthorizationResponse DoAuthorizeCall(
            string copriHost,
            string merchantId,
            string mailAddress,
            string password,
            string deviceId)
        {
#if !WINDOWS_UWP

            var l_oCommand = string.Format(
                "request=authorization&merchant_id={0}&user_id={1}&user_pw={2}&hw_id={3}",
                merchantId,
                mailAddress,
                password,
                deviceId);

            /// Extract session cookie from response.

            string response = string.Empty;

            response = Post(l_oCommand, copriHost);

            return JsonConvert.DeserializeObject<ResponseContainer<AuthorizationResponse>>(response)?.shareejson;
#else
            return null;
#endif
        }

        /// <summary> Logs user out. </summary>
        /// <param name="copriHost">Host to connect to. </param>
        /// <param name="merchantId">Id of the merchant.</param>
        /// <param name="sessionCookie"> Cookie which identifies user.</param>
        public static AuthorizationoutResponse DoAuthoutCall(
            string copriHost,
            string merchantId,
            string sessionCookie)
        {
#if !WINDOWS_UWP

            var l_oCommand = string.Format(
                "request=authout&authcookie={0}{1}",
                sessionCookie,
                merchantId);

            string l_oLogoutResponse;

            l_oLogoutResponse = Post(l_oCommand, copriHost);

            /// Extract session cookie from response.
            return JsonConvert.DeserializeObject<ResponseContainer<AuthorizationoutResponse>>(l_oLogoutResponse)?.shareejson;
#else
            return null;
#endif
        }

        /// <summary>
        /// Get list of stations from file.
        /// </summary>
        /// <param name="p_strCopriHost">URL of the copri host to connect to.</param>
        /// <param name="p_strMerchantId">Id of the merchant.</param>
        /// <param name="p_strCookie">Auto cookie of user if user is logged in.</param>
        /// <returns>List of files.</returns>
        public static StationsAvailableResponse GetStationsAllCall(
            string p_strCopriHost,
            string p_strMerchantId, 
            string p_strCookie = null)
        {
            var l_oCommand = string.Format(
                "request=stations_all&authcookie={0}{1}",
                p_strCookie,
                p_strMerchantId);

#if !WINDOWS_UWP
            string l_oStationsAllResponse;

            l_oStationsAllResponse = Post(l_oCommand, p_strCopriHost);

            // Extract bikes from response.
            return JsonConvert.DeserializeObject<ResponseContainer<StationsAvailableResponse>>(l_oStationsAllResponse)?.shareejson;
#else
            return null;
#endif
        }

        /// <summary>
        /// Gets a list of bikes from Copri.
        /// </summary>
        /// <param name="copriHost">URL of the copri host to connect to.</param>
        /// <param name="p_strMerchantId">Id of the merchant.</param>
        /// <param name="p_strSessionCookie">Cookie to authenticate user.</param>
        /// <returns>Response holding list of bikes.</returns>
        public static BikesAvailableResponse GetBikesAvailableCall(
            string copriHost,
            string merchantId, 
            string sessionCookie = null)
        {
#if !WINDOWS_UWP
            string l_oCommand = 
                $"request=bikes_available&system=all&authcookie={sessionCookie ?? string.Empty}{merchantId}";

            string response;

            response = Post(l_oCommand, copriHost);

            // Extract bikes from response.
            return CopriCallsStatic.DeserializeResponse<BikesAvailableResponse>(response);
#else
            return null;
#endif
        }

        /// <summary>
        /// Gets a list of bikes reserved/ booked by acctive user from Copri.
        /// </summary>
        /// <param name="p_strMerchantId">Id of the merchant.</param>
        /// <param name="p_strSessionCookie">Cookie to authenticate user.</param>
        /// <returns>Response holding list of bikes.</returns>
        public static BikesReservedOccupiedResponse GetBikesOccupiedCall(
            string copriHost,
            string merchantId,
            string sessionCookie)
        {
#if !WINDOWS_UWP
            string l_oCommand = !string.IsNullOrEmpty(sessionCookie)
                ? $"request=user_bikes_occupied&system=all&authcookie={sessionCookie}{merchantId}"
                : "request=bikes_available"; 

            string l_oBikesOccupiedResponse;

            l_oBikesOccupiedResponse = Post(l_oCommand, copriHost);

            // Extract bikes from response.
            return CopriCallsStatic.DeserializeResponse<BikesReservedOccupiedResponse>(l_oBikesOccupiedResponse);
#else
            return null;
#endif
        }

        /// <summary>
        /// Gets booking request response.
        /// </summary>
        /// <param name="p_strMerchantId">Id of the merchant.</param>
        /// <param name="p_iBikeId">Id of the bike to book.</param>
        /// <param name="p_strSessionCookie">Cookie identifying the user.</param>
        /// <returns>Response on booking request.</returns>
        public static ReservationBookingResponse DoReserveCall(
            string copriHost,
            string p_strMerchantId,
            string p_iBikeId,
            string p_strSessionCookie)
        {
#if !WINDOWS_UWP
            string l_oCommand = string.Format(
                "request=booking_request&bike={0}&authcookie={1}{2}",
                p_iBikeId,
                p_strSessionCookie,
                p_strMerchantId);

            string l_oBikesAvaialbeResponse  = Post(l_oCommand, copriHost);

            // Extract bikes from response.
            return JsonConvert.DeserializeObject<ResponseContainer<ReservationBookingResponse>>(l_oBikesAvaialbeResponse)?.shareejson;
#else
            return null;
#endif
        }

        /// <summary>
        /// Gets canel booking request response.
        /// </summary>
        /// <param name="p_strMerchantId">Id of the merchant.</param>
        /// <param name="p_iBikeId">Id of the bike to book.</param>
        /// <param name="p_strSessionCookie">Cookie of the logged in user.</param>
        /// <returns>Response on cancel booking request.</returns>
        public static ReservationCancelReturnResponse DoCancelReservationCall(
            string copriHost,
            string p_strMerchantId,
            string p_iBikeId,
            string p_strSessionCookie)
        {
#if !WINDOWS_UWP
            string l_oCommand = string.Format(
                "request=booking_cancel&bike={0}&authcookie={1}{2}",
                p_iBikeId,
                p_strSessionCookie,
                p_strMerchantId);
            string l_oBikesAvaialbeResponse;

            l_oBikesAvaialbeResponse = Post(l_oCommand, copriHost);

            // Extract bikes from response.
            return JsonConvert.DeserializeObject<ResponseContainer<ReservationCancelReturnResponse>>(l_oBikesAvaialbeResponse)?.shareejson;
#else
            return null;
#endif
        }

        /// <summary>
        /// Gets a list of bikes from Copri.
        /// </summary>
        /// <param name="p_strSessionCookie">Cookie to authenticate user.</param>
        /// <returns></returns>
        private static string Post(
            string p_strCommand,
            string p_strURL)
        {
#if !WINDOWS_UWP
            var l_strHost = p_strURL;

            // Returns a http request.
            var l_oRequest = WebRequest.Create(l_strHost);

            l_oRequest.Method = "POST";
            l_oRequest.ContentType = "application/x-www-form-urlencoded";

            byte[] l_oPostData = Encoding.UTF8.GetBytes(p_strCommand);

            l_oRequest.ContentLength = l_oPostData.Length;

            // Get the request stream.
            using (Stream l_oDataStream = l_oRequest.GetRequestStream())
            {
                // Write the data to the request stream.
                l_oDataStream.Write(l_oPostData, 0, l_oPostData.Length);
            }

            // Get the response.
            var l_oResponse = l_oRequest.GetResponse() as HttpWebResponse;

            if (l_oResponse == null)
            {
                throw new Exception(string.Format("Reserve request failed. Response form from server was not of expected type."));
            }

            if (l_oResponse.StatusCode != HttpStatusCode.OK)
            {
                throw new CommunicationException(string.Format(
                    "Posting request {0} failed. Expected status code is {1} but was {2}.",
                    p_strCommand,
                    HttpStatusCode.OK,
                    l_oResponse.StatusCode));
            }

            string responseFromServer = string.Empty;

            // Get the request stream.
            using (Stream l_oDataStream = l_oResponse.GetResponseStream())
            using (StreamReader l_oReader = new StreamReader(l_oDataStream))
            {
                // Read the content.
                responseFromServer = l_oReader.ReadToEnd();

                // Display the content.
                Console.WriteLine(responseFromServer);

                // Clean up the streams.
                l_oResponse.Close();
            }

            return responseFromServer;
#else
            return null;
#endif
        }
    }
}