using System.Net;
using System.Text.Json;
using System.Text;

using MesaFabApproval.Shared.Models;

using Microsoft.Extensions.Caching.Memory;
using System.Net.Http.Headers;
using MesaFabApproval.Client.Services;
using Microsoft.AspNetCore.Components;

namespace MesaFabApproval.Client.Utilities;

public class ApiHttpClientHandler : DelegatingHandler {
    private readonly IMemoryCache _cache;
    private readonly IAuthenticationService _authService;
    private readonly MesaFabApprovalAuthStateProvider _authStateProvider;
    private readonly NavigationManager _navigationManager;

    private readonly string _apiBaseUrl;

    public ApiHttpClientHandler(IMemoryCache cache,
                                IConfiguration config,
                                IAuthenticationService authService,
                                MesaFabApprovalAuthStateProvider authStateProvider,
                                NavigationManager navigationManager) {
        _cache = cache ?? throw new ArgumentNullException("IMemoryCache not injected");
        _apiBaseUrl = config["FabApprovalApiBaseUrl"] ??
            throw new NullReferenceException("FabApprovalApiBaseUrl not found in config");
        _authService = authService ??
            throw new ArgumentNullException("IAuthenticationService not injected");
        _authStateProvider = authStateProvider ??
            throw new ArgumentNullException("MesaFabApprovalAuthStateProvider not injected");
        _navigationManager = navigationManager ??
            throw new ArgumentNullException("NavigationManager not injected");
    }

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage requestMessage,
                                                                 CancellationToken cancellationToken) {
        AuthTokens? authTokens = await _authService.GetAuthTokens();

        if (authTokens is not null) requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", authTokens.JwtToken);

        HttpResponseMessage responseMessage = await base.SendAsync(requestMessage, cancellationToken);

        if (responseMessage.StatusCode.Equals(HttpStatusCode.Unauthorized)) {
            string? loginId = await _authService.GetLoginId();

            if (!string.IsNullOrWhiteSpace(loginId) && authTokens is not null) {
                AuthAttempt authAttempt = new() {
                    LoginID = loginId,
                    AuthTokens = authTokens
                };

                HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "auth/refresh");

                request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", authTokens.JwtToken);

                request.Content = new StringContent(JsonSerializer.Serialize(authAttempt),
                                                    Encoding.UTF8,
                                                    "application/json");

                HttpClient httpClient = new HttpClient() {
                    BaseAddress = new Uri(_apiBaseUrl)
                };

                httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("*/*"));
                
                HttpResponseMessage httpResponseMessage = await httpClient.SendAsync(request, cancellationToken);

                if (httpResponseMessage.IsSuccessStatusCode) {
                    string responseContent = await httpResponseMessage.Content.ReadAsStringAsync();

                    JsonSerializerOptions jsonSerializerOptions = new() {
                        PropertyNameCaseInsensitive = true
                    };

                    LoginResult loginResult = JsonSerializer.Deserialize<LoginResult>(responseContent, jsonSerializerOptions) ??
                        throw new Exception("Unable to parse login result from API response");

                    if (!loginResult.IsAuthenticated) throw new Exception($"User with Login ID {loginId} not authorized");

                    if (loginResult.AuthTokens is not null) {
                        requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", loginResult.AuthTokens.JwtToken);
                        await _authService.SetTokens(loginResult.AuthTokens.JwtToken, loginResult.AuthTokens.RefreshToken);
                    }

                    if (loginResult.User is not null)
                        await _authService.SetCurrentUser(loginResult.User);
                } else {
                    await _authStateProvider.Logout();

                    string? redirectUrl = _cache.Get<string>("redirectUrl");
                    if (!string.IsNullOrWhiteSpace(redirectUrl)) {
                        _navigationManager.NavigateTo($"login/{redirectUrl}");
                    } else {
                        _navigationManager.NavigateTo("login");
                    }
                }
            }

            return await base.SendAsync(requestMessage, cancellationToken);
        }

        return responseMessage;
    }
}