using Knoks.Api.Client.Invoker;
using Newtonsoft.Json;
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;

namespace Knoks.Api.Client
{
    public class ApiClient : IDisposable
    {
        public readonly string ApiIndetifier;
        public HttpClient HttpClient { get; }

        public ApiClient(string apiIndetifier, string baseAddress = null)
        {
            ApiIndetifier = apiIndetifier;
            HttpClient = new HttpClient();

            if(!string.IsNullOrEmpty(baseAddress))
                HttpClient.BaseAddress = new Uri(baseAddress);
        }

        public ApiClient(string apiIndetifier, HttpClient httpClient)
        {
            ApiIndetifier = apiIndetifier;
            HttpClient = httpClient;
        }

        public void Dispose()
        {
            if (HttpClient != null)
                HttpClient.Dispose();
        }

        public async Task<ApiResult<TokenResponse>> GetApiConsumerToken()
        {
            return await Request<TokenResponse>($"token/{ApiIndetifier}");
        }

        public async Task<ApiResult<TokenResponse>> GetApiUserToken(string email, string password)
        {
            return await Request<TokenResponse>($"token/{ApiIndetifier}", new { Email = email, Password = password });
        }

        public async Task<ApiResult<TokenResponse>> GetApiOperatorToken(string operatorName, string operatorPassword)
        {
            return await Request<TokenResponse>($"token/{ApiIndetifier}/operator", new { OperatorName = operatorName, OperatorPassword = operatorPassword });
        }

        public async Task<ApiResult<TokenResponse>> AuthorizeApiConsumer()
        {
            var result = await GetApiConsumerToken();
            SetAuthorization(result.Object);
            return result;
        }

        public async Task<ApiResult<TokenResponse>> AuthorizeApiUser(string username, string password)
        {
            var result = await GetApiUserToken(username, password);
            SetAuthorization(result.Object);
            return result;
        }

        public async Task<ApiResult<TokenResponse>> AuthorizeApiOperate(string operatorName, string operatorPassword)
        {
            var result = await GetApiOperatorToken(operatorName, operatorPassword);
            SetAuthorization(result.Object);
            return result;
        }

        public void SetAuthorization(TokenResponse tokenResponce)
        {
            HttpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", tokenResponce.Token);
        }

        public async Task<ApiResult<T>> Request<T>(string url, object bodyObj = null, string httpMethod = null)
        {
            httpMethod = httpMethod ?? (bodyObj == null ? "GET" : "POST");

            HttpResponseMessage response = null;
            if (httpMethod == "GET")
            {
                if (bodyObj != null)
                    throw new InvalidOperationException("HTTP Method 'GET' with body object");

                response = await HttpClient.GetAsync(url);
            }
            else if (httpMethod == "PUT" || httpMethod == "POST")
            {
                if (bodyObj == null)
                    throw new InvalidOperationException($"HTTP Method '{httpMethod}' with no body object");

                var httpContent = bodyObj is HttpContent ? (HttpContent)bodyObj : 
                    new StringContent(JsonConvert.SerializeObject(bodyObj), Encoding.UTF8, "application/json");

                response = httpMethod == "PUT" ? await HttpClient.PutAsync(url, httpContent) : await HttpClient.PostAsync(url, httpContent);
            }
            else throw new InvalidOperationException("HTTP Method is not detected");

            return new ApiResult<T>(response, await response.Content.ReadAsStringAsync());
        }

        public async Task<ApiResult<object>> Request(string url, object bodyObj = null, string httpMethod = null)
        {
            return await Request<object>(url, bodyObj, httpMethod);
        }
    }
}
