using Blazored.LocalStorage;
using System.Globalization;
using System.Net.Http.Json;
using System.Net;
using decePubClient.Models;

namespace decePubClient.Services
{
	public interface IHttpService
	{
		Task<HttpResponseMessage> Get(string uri, object payload = default, string[] queryParams = default);

		Task<HttpResponseMessage> GetAnon(string uri, object payload = default, string[] queryParams = default);

		Task<HttpResponseMessage> Post(string uri, object payload = default);

		Task<HttpResponseMessage> PostAnon(string uri, object payload = default);

		Task<HttpResponseMessage> Delete(string uri, string[] queryParams = default, object payload = default);
	}

	public class HttpService : IHttpService
	{
		readonly IHttpClientFactory HttpClientFactory;
		readonly TokenAuthStateProvider AuthStateProvider;
		readonly ILogger<HttpService> Logger;
		readonly ILocalStorageService Storage;
		readonly IStorage DbStorage;

		public HttpService(
						IHttpClientFactory httpClientFactory,
						TokenAuthStateProvider authStateProvider,
						ILogger<HttpService> logger,
						ILocalStorageService storage,
						IStorage dbStorage)
		{
			HttpClientFactory = httpClientFactory;
			AuthStateProvider = authStateProvider;
			Logger = logger;
			Storage = storage;
			DbStorage = dbStorage;
		}

		public async Task<HttpResponseMessage> Get(string uri, object payload = default, string[] queryParams = default)
		{
			try
			{
				uri = $"{CultureInfo.DefaultThreadCurrentCulture.TwoLetterISOLanguageName}/{uri}";
				var request = default(HttpRequestMessage);
				if (queryParams != null)
					request = new(HttpMethod.Get, string.Join('?', uri, string.Join('&', queryParams)));
				else
					request = new(HttpMethod.Get, uri);
				if (payload != null)
					request.Content = JsonContent.Create(payload);
				return await SendRequest(request);
			}
			catch (Exception ex)
			{
				Logger.LogError(ex, $"{nameof(Get)}:{uri}");
				await DbStorage.AddLog(ex, $"{nameof(Get)}:{uri}");
				return new(HttpStatusCode.ServiceUnavailable);
			}
		}

		public async Task<HttpResponseMessage> Post(string uri, object payload = default)
		{
			try
			{
				uri = $"{CultureInfo.DefaultThreadCurrentCulture.TwoLetterISOLanguageName}/{uri}";
				var request = new HttpRequestMessage(HttpMethod.Post, uri)
				{
					Content = JsonContent.Create(payload)
				};
				return await SendRequest(request);
			}
			catch (Exception ex)
			{
				Logger.LogError(ex, $"{nameof(Post)}:{uri}");
				await DbStorage.AddLog(ex, $"{nameof(Post)}:{uri}");
				return new(HttpStatusCode.ServiceUnavailable);
			}
		}

		public async Task<HttpResponseMessage> GetAnon(string uri, object payload = default, string[] queryParams = default)
		{
			try
			{
				uri = $"{CultureInfo.DefaultThreadCurrentCulture.TwoLetterISOLanguageName}/{uri}";
				var request = default(HttpRequestMessage);
				if (queryParams != null)
					request = new(HttpMethod.Get, string.Join('?', uri, string.Join('&', queryParams)));
				else
					request = new(HttpMethod.Get, uri);
				if (payload != null)
					request.Content = JsonContent.Create(payload);
				return await SendAnonRequest(request);
			}
			catch (Exception ex)
			{
				Logger.LogError(ex, $"{nameof(GetAnon)}:{uri}");
				await DbStorage.AddLog(ex, $"{nameof(GetAnon)}:{uri}");
				return new(HttpStatusCode.ServiceUnavailable);
			}
		}

		public async Task<HttpResponseMessage> PostAnon(string uri, object payload = default)
		{
			try
			{
				uri = $"{CultureInfo.DefaultThreadCurrentCulture.TwoLetterISOLanguageName}/{uri}";
				var request = new HttpRequestMessage(HttpMethod.Post, uri)
				{
					Content = JsonContent.Create(payload)
				};
				return await SendAnonRequest(request);
			}
			catch (Exception ex)
			{
				Logger.LogError(ex, $"{nameof(PostAnon)}:{uri}");
				await DbStorage.AddLog(ex, $"{nameof(PostAnon)}:{uri}");
				return new(HttpStatusCode.ServiceUnavailable);
			}
		}

		public async Task<HttpResponseMessage> Delete(string uri, string[] queryParams = default, object payload = default)
		{
			try
			{
				uri = $"{CultureInfo.DefaultThreadCurrentCulture.TwoLetterISOLanguageName}/{uri}";
				var request = default(HttpRequestMessage);
				if (queryParams != null)
					request = new(HttpMethod.Delete, string.Join('?', uri, string.Join('&', queryParams)));
				else
					request = new(HttpMethod.Delete, uri);
				if (payload != null)
					request.Content = JsonContent.Create(payload);
				return await SendRequest(request);
			}
			catch (Exception ex)
			{
				Logger.LogError(ex, $"{nameof(Delete)}:{uri}");
				await DbStorage.AddLog(ex, $"{nameof(Delete)}:{uri}");
				return new(HttpStatusCode.ServiceUnavailable);
			}
		}

		// helper methods
		async Task<HttpResponseMessage> SendRequest(HttpRequestMessage request)
		{
			try
			{
				var authData = await Storage.GetItemAsync<AuthData>(nameof(AuthData));
				var isApiUrl = !request.RequestUri?.IsAbsoluteUri;
				if (isApiUrl.HasValue && isApiUrl.Value)
					request.Headers.Authorization = new("Bearer", authData.Token);

				var response = await HttpClientFactory.CreateClient().SendAsync(request);
				if (response.StatusCode is HttpStatusCode.Unauthorized or HttpStatusCode.Forbidden)
				{
					Logger.LogWarning($"{nameof(SendRequest)}:401/403:{request.RequestUri?.OriginalString}:{await response.Content.ReadAsStringAsync()}");
					await DbStorage.AddLog($"{nameof(SendRequest)}:401/403:{request.RequestUri?.OriginalString}:{await response.Content.ReadAsStringAsync()}", $"{nameof(SendRequest)}:{request.RequestUri?.OriginalString}");
					await AuthStateProvider.LogoutAsync();
				}

				return response;
			}
			catch (Exception ex)
			{
				Logger.LogError(ex, $"{nameof(SendRequest)}:{request.RequestUri?.OriginalString}");
				await DbStorage.AddLog(ex, $"{nameof(SendRequest)}:{request.RequestUri?.OriginalString}");
				return new(HttpStatusCode.ServiceUnavailable);
			}
		}

		async Task<HttpResponseMessage> SendAnonRequest(HttpRequestMessage request)
		{
			try
			{
				var authData = await Storage.GetItemAsync<AuthData>(nameof(AuthData));
				if (!string.IsNullOrEmpty(authData?.Token))
					request.Headers.Authorization = new("Bearer", authData.Token);

				var response = await HttpClientFactory.CreateClient().SendAsync(request);

				return response;
			}
			catch (Exception ex)
			{
				Logger.LogError(ex, $"{nameof(SendAnonRequest)}:{request.RequestUri?.OriginalString}");
				await DbStorage.AddLog(ex, $"{nameof(SendAnonRequest)}:{request.RequestUri?.OriginalString}");
				return new(HttpStatusCode.ServiceUnavailable);
			}
		}
	}
}
