using System.Security.Claims;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using ZB.MOM.WW.MxGateway.Server;
using ZB.MOM.WW.MxGateway.Server.Dashboard;

namespace ZB.MOM.WW.MxGateway.Tests.Gateway;

/// <summary>
/// Verifies the shared Secrets UI is wired into the dashboard: the
/// <c>AddSecretsAuthorization</c> policies are registered so the mounted
/// <c>/admin/secrets</c> Blazor page (<c>[Authorize(Policy = "secrets:manage")]</c>) and its
/// reveal action resolve, AND that the dashboard's real Administrator principal shape actually
/// satisfies them (the claim-type-compatibility risk class that bit HistorianGateway). The
/// browser reveal/redirect behaviour is verified separately on the box.
/// </summary>
public sealed class DashboardSecretsMountTests
{
    private const string ManagePolicy = "secrets:manage";
    private const string RevealPolicy = "secrets:reveal";

    /// <summary>
    /// Verifies that Build registers the shared Secrets authorization policies
    /// (<c>secrets:manage</c> + <c>secrets:reveal</c>) added additively via
    /// <c>Configure&lt;AuthorizationOptions&gt;(o =&gt; o.AddSecretsAuthorization())</c>.
    /// </summary>
    /// <returns>A task that represents the asynchronous operation.</returns>
    [Fact]
    public async Task Build_RegistersSecretsAuthorizationPolicies()
    {
        await using WebApplication app = GatewayApplication.Build([]);

        IAuthorizationPolicyProvider policyProvider =
            app.Services.GetRequiredService<IAuthorizationPolicyProvider>();

        AuthorizationPolicy? managePolicy = await policyProvider.GetPolicyAsync(ManagePolicy);
        AuthorizationPolicy? revealPolicy = await policyProvider.GetPolicyAsync(RevealPolicy);

        Assert.NotNull(managePolicy);
        Assert.NotNull(revealPolicy);
    }

    /// <summary>
    /// Verifies that the dashboard's Administrator principal — constructed exactly the way
    /// <see cref="DashboardAutoLoginAuthenticationHandler.CreatePrincipal(string?)"/> mints it
    /// (role claim on <c>ZbClaimTypes.Role</c>, carrying <c>Administrator</c>, authenticated) —
    /// satisfies BOTH secrets policies. This locks claim-type compatibility: if roles were ever
    /// issued on a divergent claim type, <c>RequireRole("Administrator")</c> would no longer match
    /// and this test would fail.
    /// </summary>
    /// <returns>A task that represents the asynchronous operation.</returns>
    [Fact]
    public async Task AdministratorPrincipal_SatisfiesSecretsPolicies()
    {
        await using WebApplication app = GatewayApplication.Build([]);
        IAuthorizationService authorization =
            app.Services.GetRequiredService<IAuthorizationService>();
        ClaimsPrincipal administrator = DashboardAutoLoginAuthenticationHandler.CreatePrincipal("multi-role");

        AuthorizationResult manage = await authorization.AuthorizeAsync(administrator, resource: null, ManagePolicy);
        AuthorizationResult reveal = await authorization.AuthorizeAsync(administrator, resource: null, RevealPolicy);

        Assert.True(manage.Succeeded);
        Assert.True(reveal.Succeeded);
    }

    /// <summary>
    /// Verifies that an anonymous, role-less principal is DENIED both secrets policies, proving the
    /// policies actually gate on the Administrator role rather than admitting everyone.
    /// </summary>
    /// <returns>A task that represents the asynchronous operation.</returns>
    [Fact]
    public async Task AnonymousPrincipal_IsDeniedSecretsPolicies()
    {
        await using WebApplication app = GatewayApplication.Build([]);
        IAuthorizationService authorization =
            app.Services.GetRequiredService<IAuthorizationService>();
        ClaimsPrincipal anonymous = new(new ClaimsIdentity());

        AuthorizationResult manage = await authorization.AuthorizeAsync(anonymous, resource: null, ManagePolicy);
        AuthorizationResult reveal = await authorization.AuthorizeAsync(anonymous, resource: null, RevealPolicy);

        Assert.False(manage.Succeeded);
        Assert.False(reveal.Succeeded);
    }
}
