﻿using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;

namespace Frank.Core.Client
{
    /// <summary>
    /// Base class for services clients
    /// </summary>
    public abstract class ClientBase
    {
        private readonly HttpClient _client;
        private readonly Uri _baseUrl;

        /// <summary>
        /// Initializes a new instance of the <see cref="ClientBase"/> class with a specific <see cref="HttpClient"/>.
        /// </summary>
        /// <param name="client">The <see cref="HttpClient"/> that will be used to send http requests.</param>
        /// <param name="baseUrl">The base url of the target service.</param>
        protected ClientBase(HttpClient client, Uri baseUrl)
        {
            _client = client;
            _baseUrl = baseUrl;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ClientBase"/> class with a base URL.
        /// </summary>
        /// <param name="baseUrl">The base URL to use for requests.</param>
        protected ClientBase(Uri baseUrl)
            : this(new HttpClient(), baseUrl)
        {
        }

        /// <summary>
        /// Used for sending HTTP requests.
        /// </summary>
        /// <typeparam name="T">The type to deserialize the response content to.</typeparam>
        /// <param name="method">The request method.</param>
        /// <param name="relativeUri">The relative request URI</param>
        /// <param name="content">The content to send as JSON.</param>
        /// <param name="headers">The additional headers to send in the request.</param>
        /// <param name="success">The expected success <see cref="HttpStatusCode"/>.</param>
        /// <returns>A <see cref="Response{T}"/> that contains the response <see cref="HttpStatusCode"/> and the response content.</returns>
        [Obsolete]
        protected async Task<Response<T>> SendAsync<T>(HttpMethod method, string relativeUri, object content, IEnumerable<KeyValuePair<string, string>> headers, HttpStatusCode success)
        {
            var response = await SendAsync(method, relativeUri, content, headers);

            return new Response<T>(response);
        }

        /// <summary>
        /// Used for sending HTTP requests.
        /// </summary>
        /// <param name="method">The request method.</param>
        /// <param name="relativeUri">The relative request URI</param>
        /// <param name="content">The content to send as JSON.</param>
        /// <param name="headers">The additional headers to send in the request.</param>
        /// <param name="success">The expected success <see cref="HttpStatusCode"/>.</param>
        /// <returns>A <see cref="Response{T}"/> that contains the response <see cref="HttpStatusCode"/> and the response content.</returns>
        [Obsolete]
        protected async Task<Response> SendAsync(HttpMethod method, string relativeUri, object content, IEnumerable<KeyValuePair<string, string>> headers, HttpStatusCode success)
        {
            var response = await SendAsync(method, relativeUri, content, headers);

            return new Response(response);
        }

        /// <summary>
        /// Used for sending HTTP requests that return an empty response contnet.
        /// </summary>
        /// <param name="request">The request to send.</param>
        /// <returns>A <see cref="Response"/> that contains the response.</returns>
        protected async Task<Response> SendAsync(HttpRequestMessage request)
        {
            request.RequestUri = new Uri(_baseUrl, request.RequestUri);
            var response = await _client.SendAsync(request);

            return new Response(response);
        }

        /// <summary>
        /// Used for sending HTTP requests that return a Strongly typed response content on success.
        /// </summary>
        /// <param name="request">The request to send.</param>
        /// <returns>A <see cref="Response{T}"/> that contains the response.</returns>
        protected async Task<Response<T>> SendAsync<T>(HttpRequestMessage request)
        {
            request.RequestUri = new Uri(_baseUrl, request.RequestUri);
            var response = await _client.SendAsync(request);

            return new Response<T>(response);
        }

        private async Task<HttpResponseMessage> SendAsync(HttpMethod method, string relativeUri, object content, IEnumerable<KeyValuePair<string, string>> headers)
        {
            var request = new HttpRequestMessage(method, new Uri(_baseUrl, relativeUri));
            if (content != null)
            {
                request.Content = new StringContent(content.ToJson(), Encoding.UTF8, "application/json");
            }
            if (headers != null)
            {
                foreach (var header in headers)
                {
                    request.Headers.Add(header.Key, header.Value);
                }
            }
            return await _client.SendAsync(request);
        }
    }
}
