using System.Security.Claims;
using ErsatzTV.Application.Auth;
using LanguageExt;
using MediatR;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;

namespace ErsatzTV.Auth;

/// <summary>
///     Cookie <c>OnValidatePrincipal</c> handler enforcing local-admin security-stamp revocation (#295):
///     a local-login session carries the admin's security stamp as a claim; a password change rotates the
///     stored stamp, so a mismatch here rejects the (now stale) session on its next request. OIDC sessions
///     carry no stamp claim and are governed by the IdP, so they are skipped.
/// </summary>
public static class CookieSecurityStampValidator
{
    public static async Task ValidateAsync(CookieValidatePrincipalContext context)
    {
        ClaimsPrincipal principal = context.Principal;
        string method = principal?.FindFirst(AuthConstants.AuthMethodClaim)?.Value;
        if (!string.Equals(method, AuthConstants.MethodLocal, StringComparison.Ordinal))
        {
            return;
        }

        string presented = principal.FindFirst(AuthConstants.SecurityStampClaim)?.Value;
        IMediator mediator = context.HttpContext.RequestServices.GetRequiredService<IMediator>();
        Option<string> stored = await mediator.Send(new GetLocalAdminSecurityStamp());
        string current = stored.IfNone(string.Empty);

        if (string.IsNullOrEmpty(presented) || !string.Equals(presented, current, StringComparison.Ordinal))
        {
            context.RejectPrincipal();
            await context.HttpContext.SignOutAsync(AuthConstants.CookieScheme);
        }
    }
}
