using JobsJobsJobs.Shared;
using JobsJobsJobs.Shared.Api;
using NodaTime;
using NodaTime.Serialization.SystemTextJson;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text.Json;
using System.Threading.Tasks;
namespace JobsJobsJobs.Client
{
    /// 
    /// Functions used to access the API
    /// 
    public static class ServerApi
    {
        /// 
        /// System.Text.Json options configured for NodaTime
        /// 
        private static readonly JsonSerializerOptions _serializerOptions;
        /// 
        /// Static constructor
        /// 
        static ServerApi()
        {
            var options = new JsonSerializerOptions
            {
                PropertyNamingPolicy = JsonNamingPolicy.CamelCase
            };
            options.ConfigureForNodaTime(DateTimeZoneProviders.Tzdb);
            _serializerOptions = options;
        }
        /// 
        /// Create an API URL
        /// 
        /// The URL to append to the API base URL
        /// The full URL to be used in HTTP requests
        private static string ApiUrl(string url) => $"/api/{url}";
        /// 
        /// Create an HTTP request with an authorization header
        /// 
        /// The current application state
        /// The URL for the request (will be appended to the API root)
        /// The request method (optional, defaults to GET)
        /// A request with the header attached, ready for further manipulation
        private static HttpRequestMessage WithHeader(AppState state, string url, HttpMethod? method = null)
        {
            var req = new HttpRequestMessage(method ?? HttpMethod.Get, ApiUrl(url));
            req.Headers.Authorization = new AuthenticationHeaderValue("Bearer", state.Jwt);
            return req;
        }
        /// 
        /// Set the JSON Web Token (JWT) bearer header for the given HTTP client
        /// 
        /// The HTTP client whose authentication header should be set
        /// The current application state
        public static void SetJwt(HttpClient http, AppState state)
        {
            if (state.User != null)
            {
                http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", state.Jwt);
            }
        }
        /// 
        /// Log on a user with the authorization code received from No Agenda Social
        /// 
        /// The HTTP client to use for server communication
        /// The authorization code received from NAS
        /// The log on details if successful, an error if not
        public static async Task> LogOn(HttpClient http, string authCode)
        {
            try
            {
                var logOn = await http.GetFromJsonAsync(ApiUrl($"citizen/log-on/{authCode}"));
                if (logOn == null) {
                    return Result.AsError(
                        "Unable to log on with No Agenda Social. This should never happen; contact @danieljsummers");
                }
                return Result.AsOk(logOn);
            }
            catch (HttpRequestException ex)
            {
                return Result.AsError($"Unable to log on with No Agenda Social: {ex.Message}");
            }
        }
        /// 
        /// Retrieve a citizen's profile
        /// 
        /// The HTTP client to use for server communication
        /// The current application state
        /// The citizen's profile, null if it is not found, or an error message if one occurs
        public static async Task> RetrieveProfile(HttpClient http, AppState state)
        {
            var req = WithHeader(state, "profile/");
            var res = await http.SendAsync(req);
            return true switch
            {
                _ when res.StatusCode == HttpStatusCode.NoContent => Result.AsOk(null),
                _ when res.IsSuccessStatusCode => Result.AsOk(
                    await res.Content.ReadFromJsonAsync(_serializerOptions)),
                _ => Result.AsError(await res.Content.ReadAsStringAsync()),
            };
        }
        /// 
        /// Retrieve all continents
        /// 
        /// The HTTP client to use for server communication
        /// The current application state
        /// The continents, or an error message if one occurs
        public static async Task>> AllContinents(HttpClient http, AppState state)
        {
            var req = WithHeader(state, "continent/all");
            var res = await http.SendAsync(req);
            if (res.IsSuccessStatusCode) {
                var continents = await res.Content.ReadFromJsonAsync>();
                return Result>.AsOk(continents ?? Enumerable.Empty());
            }
            return Result>.AsError(await res.Content.ReadAsStringAsync());
        }
        /// 
        /// Retrieve many items from the given URL
        /// 
        /// The type of item expected
        /// The HTTP client to use for server communication
        /// The API URL to call
        /// A result with the items, or an error if one occurs
        /// The caller is responsible for setting the JWT on the HTTP client
        public static async Task>> RetrieveMany(HttpClient http, string url)
        {
            try
            {
                var results = await http.GetFromJsonAsync>($"/api/{url}", _serializerOptions);
                return Result>.AsOk(results ?? Enumerable.Empty());
            }
            catch (HttpRequestException ex)
            {
                return Result>.AsError(ex.Message);
            }
            catch (JsonException ex)
            {
                return Result>.AsError($"Unable to parse result: {ex.Message}");
            }
        }
        /// 
        /// Retrieve one item from the given URL
        /// 
        /// The type of item expected
        /// The HTTP client to use for server communication
        /// The API URL to call
        /// A result with the item (possibly null), or an error if one occurs
        /// The caller is responsible for setting the JWT on the HTTP client
        public static async Task> RetrieveOne(HttpClient http, string url)
        {
            try
            {
                return Result.AsOk(await http.GetFromJsonAsync($"/api/{url}", _serializerOptions));
            }
            catch (HttpRequestException ex)
            {
                return Result.AsError(ex.Message);
            }
            catch (JsonException ex)
            {
                return Result.AsError($"Unable to parse result: {ex.Message}");
            }
        }
    }
}