using System.Security.Claims;
using System.Text.Encodings.Web;
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using ZB.MOM.WW.Auth.AspNetCore;
using ZB.MOM.WW.MxGateway.Server.Configuration;

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

/// <summary>
/// Authentication handler used ONLY when <c>MxGateway:Dashboard:DisableLogin</c> is true.
/// Registered under the dashboard cookie scheme name
/// (<see cref="DashboardAuthenticationDefaults.AuthenticationScheme"/>), it authenticates
/// EVERY request as the configured dev user with both dashboard roles — no credential check,
/// no cookie, no LDAP bind. The minted principal mirrors the shape the real login
/// (<see cref="DashboardAuthenticator"/>) produces, so policies and the UI cannot tell it
/// apart. DEV/TEST ONLY; never enable in production.
/// </summary>
public sealed class DashboardAutoLoginAuthenticationHandler
    : AuthenticationHandler<AuthenticationSchemeOptions>, IAuthenticationSignInHandler
{
    /// <summary>Username used when <c>AutoLoginUser</c> is null or blank.</summary>
    public const string DefaultUser = "multi-role";

    private readonly string _user;

    /// <summary>Initializes the handler with scheme plumbing and the dashboard options.</summary>
    /// <param name="options">The per-scheme authentication options monitor.</param>
    /// <param name="logger">The logger factory the base handler uses.</param>
    /// <param name="encoder">The URL encoder the base handler uses.</param>
    /// <param name="gatewayOptions">Gateway options carrying the dashboard auto-login user.</param>
    public DashboardAutoLoginAuthenticationHandler(
        IOptionsMonitor<AuthenticationSchemeOptions> options,
        ILoggerFactory logger,
        UrlEncoder encoder,
        IOptions<GatewayOptions> gatewayOptions)
        : base(options, logger, encoder)
    {
        string? configured = gatewayOptions.Value.Dashboard.AutoLoginUser;
        _user = string.IsNullOrWhiteSpace(configured) ? DefaultUser : configured.Trim();
    }

    /// <summary>No-op: auto-login writes no cookie, so a sign-in has nothing to persist.</summary>
    /// <param name="user">Ignored.</param>
    /// <param name="properties">Ignored.</param>
    /// <returns>A completed task.</returns>
    public Task SignInAsync(ClaimsPrincipal user, AuthenticationProperties? properties) => Task.CompletedTask;

    /// <summary>No-op: there is no auth cookie to clear; the next request re-authenticates.</summary>
    /// <param name="properties">Ignored.</param>
    /// <returns>A completed task.</returns>
    public Task SignOutAsync(AuthenticationProperties? properties) => Task.CompletedTask;

    /// <inheritdoc />
    protected override Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        ClaimsPrincipal principal = CreatePrincipal(_user);
        AuthenticationTicket ticket = new(principal, Scheme.Name);

        return Task.FromResult(AuthenticateResult.Success(ticket));
    }

    /// <summary>
    /// Builds the multi-role dev principal. Null/blank <paramref name="user"/> falls back to
    /// <see cref="DefaultUser"/>. The authorization-relevant claim shape mirrors
    /// <see cref="DashboardAuthenticator"/>; LDAP group claims (<c>LdapGroupClaimType</c>) are
    /// intentionally omitted because auto-login has no real LDAP context.
    /// </summary>
    /// <param name="user">The configured auto-login username (may be null/blank).</param>
    /// <returns>An authenticated principal holding both dashboard roles.</returns>
    internal static ClaimsPrincipal CreatePrincipal(string? user)
    {
        string name = string.IsNullOrWhiteSpace(user) ? DefaultUser : user.Trim();

        // LdapGroupClaimType claims are omitted — no LDAP groups exist in the auto-login context.
        Claim[] claims =
        [
            new Claim(ClaimTypes.NameIdentifier, name),
            new Claim(ZbClaimTypes.Username, name),
            new Claim(ZbClaimTypes.Name, name),
            new Claim(ZbClaimTypes.DisplayName, name),
            new Claim(ZbClaimTypes.Role, DashboardRoles.Admin),
            new Claim(ZbClaimTypes.Role, DashboardRoles.Viewer),
        ];

        ClaimsIdentity identity = new(
            claims,
            DashboardAuthenticationDefaults.AuthenticationScheme,
            ZbClaimTypes.Name,
            ZbClaimTypes.Role);

        return new ClaimsPrincipal(identity);
    }
}
