using System.Net.Http.Headers;
using System.Threading;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.WebAssembly.Http;
using Microsoft.Extensions.DependencyInjection;
using NexusReader.Application.Abstractions.Services;
using NexusReader.UI.Shared.Services;

namespace NexusReader.Web.Client.Handlers;

/// <summary>
/// A secure HTTP message delegating handler that automatically appends JWT tokens
/// to trusted origin requests and transparently refreshes expired tokens in a thread-safe manner.
/// </summary>
public class AuthenticationHeaderHandler : DelegatingHandler
{
    private readonly INativeStorageService _storageService;
    private readonly IServiceProvider _serviceProvider;
    private const string TokenKey = "nexus_auth_token";
    private static readonly SemaphoreSlim _refreshSemaphore = new(1, 1);

    public AuthenticationHeaderHandler(INativeStorageService storageService, IServiceProvider serviceProvider)
    {
        _storageService = storageService;
        _serviceProvider = serviceProvider;
    }

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        // Force browser to forward credentials (cookies) for SSR hydration sync
        request.SetBrowserRequestCredentials(BrowserRequestCredentials.Include);

        var path = request.RequestUri?.AbsolutePath ?? "";
        bool isAuthEndpoint = path.Contains("identity/login") || 
                             path.Contains("identity/register") || 
                             path.Contains("identity/refresh");

        // SECURITY FIX (CWE-200): Ensure we only append JWT tokens to local or trusted base origin requests
        var navigationManager = _serviceProvider.GetRequiredService<NavigationManager>();
        bool isTrustedHost = request.RequestUri != null && 
                             (!request.RequestUri.IsAbsoluteUri || 
                              request.RequestUri.ToString().StartsWith(navigationManager.BaseUri, StringComparison.OrdinalIgnoreCase));

        string? originalToken = null;

        if (!isAuthEndpoint && isTrustedHost)
        {
            var tokenResult = await _storageService.GetSecureString(TokenKey);
            if (tokenResult.IsSuccess && !string.IsNullOrEmpty(tokenResult.Value))
            {
                originalToken = tokenResult.Value;
                
                // Only attach the Bearer token if it is not expired
                if (!JwtTokenValidator.IsExpired(originalToken))
                {
                    request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", originalToken);
                }
            }
        }

        var response = await base.SendAsync(request, cancellationToken);

        // Transparent JWT Auto-Refresh on 401 Unauthorized
        if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized && !isAuthEndpoint)
        {
            await _refreshSemaphore.WaitAsync(cancellationToken);
            try
            {
                // Re-read token to verify if another concurrent request already refreshed it
                var tokenResult = await _storageService.GetSecureString(TokenKey);
                var currentToken = tokenResult.IsSuccess ? tokenResult.Value : null;

                bool refreshed = false;

                if (!string.IsNullOrEmpty(currentToken) && currentToken != originalToken)
                {
                    refreshed = true;
                }
                else
                {
                    // SECURITY FIX (CWE-400): Resolve scoped services within an explicit using scope to prevent memory leaks
                    using var scope = _serviceProvider.CreateScope();
                    var identityService = scope.ServiceProvider.GetRequiredService<IIdentityService>();
                    var refreshResult = await identityService.RefreshTokenAsync();
                    if (refreshResult.IsSuccess)
                    {
                        var newTokenResult = await _storageService.GetSecureString(TokenKey);
                        currentToken = newTokenResult.IsSuccess ? newTokenResult.Value : null;
                        refreshed = !string.IsNullOrEmpty(currentToken);
                    }
                    else
                    {
                        await identityService.LogoutAsync();
                    }
                }

                if (refreshed && !string.IsNullOrEmpty(currentToken))
                {
                    var newRequest = await CloneHttpRequestMessageAsync(request);
                    newRequest.Headers.Authorization = new AuthenticationHeaderValue("Bearer", currentToken);
                    return await base.SendAsync(newRequest, cancellationToken);
                }
            }
            catch (Exception ex)
            {
                // Write standard security audit safe debug log
                Console.WriteLine($"[AuthHeaderHandler] Automated token renewal failed: {ex.Message}");
            }
            finally
            {
                _refreshSemaphore.Release();
            }
        }

        return response;
    }

    private async Task<HttpRequestMessage> CloneHttpRequestMessageAsync(HttpRequestMessage req)
    {
        var clone = new HttpRequestMessage(req.Method, req.RequestUri)
        {
            Version = req.Version
        };

        if (req.Content != null)
        {
            var ms = new System.IO.MemoryStream();
            await req.Content.CopyToAsync(ms);
            ms.Position = 0;
            clone.Content = new StreamContent(ms);

            foreach (var h in req.Content.Headers)
            {
                clone.Content.Headers.TryAddWithoutValidation(h.Key, h.Value);
            }
        }

        foreach (var h in req.Headers)
        {
            clone.Headers.TryAddWithoutValidation(h.Key, h.Value);
        }

        clone.SetBrowserRequestCredentials(BrowserRequestCredentials.Include);

        return clone;
    }
}
