using System.Text.Encodings.Web;
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Options;

namespace ZB.MOM.WW.MxGateway.Server.Dashboard;

/// <summary>
/// Authenticates SignalR hub connections by extracting a bearer token from
/// either the <c>Authorization</c> header or the <c>access_token</c> query
/// string (used by WebSocket upgrade requests that can't carry custom headers)
/// and validating it via <see cref="HubTokenService"/>.
/// </summary>
public sealed class HubTokenAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
    private readonly HubTokenService _tokens;

    public HubTokenAuthenticationHandler(
        IOptionsMonitor<AuthenticationSchemeOptions> options,
        ILoggerFactory logger,
        UrlEncoder encoder,
        HubTokenService tokens)
        : base(options, logger, encoder)
    {
        _tokens = tokens;
    }

    protected override Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        string? token = ExtractToken();
        if (string.IsNullOrEmpty(token))
        {
            return Task.FromResult(AuthenticateResult.NoResult());
        }

        System.Security.Claims.ClaimsPrincipal? principal = _tokens.Validate(token);
        if (principal is null)
        {
            return Task.FromResult(AuthenticateResult.Fail("Invalid or expired hub token."));
        }

        return Task.FromResult(AuthenticateResult.Success(
            new AuthenticationTicket(principal, Scheme.Name)));
    }

    private string? ExtractToken()
    {
        string header = Request.Headers.Authorization.ToString();
        if (header.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
        {
            return header["Bearer ".Length..].Trim();
        }

        return Request.Query.TryGetValue("access_token", out Microsoft.Extensions.Primitives.StringValues queryToken)
            ? queryToken.ToString()
            : null;
    }
}
