using System.Security.Claims;
using System.Text.Encodings.Web;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using ZB.MOM.WW.Auth.AspNetCore;   // ZbClaimTypes — same source AuthEndpoints mints claims from.

namespace ZB.MOM.WW.OtOpcUa.Security.Auth;

/// <summary>
///     Auth handler used ONLY when <see cref="AuthDisableLoginOptions.DisableLogin"/> is true.
///     Registered under the cookie scheme name, it authenticates EVERY request as the configured
///     dev user with all <see cref="DevAuthRoles.All"/> roles — no credential check, no cookie.
///     The minted principal mirrors the shape the real login (AuthEndpoints) produces.
/// </summary>
public sealed class AutoLoginAuthenticationHandler
    : AuthenticationHandler<AuthenticationSchemeOptions>, IAuthenticationSignInHandler
{
    private readonly AuthDisableLoginOptions _opts;

    /// <summary>Initializes the handler with the scheme plumbing and the disable-login 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="disableLoginOptions">The disable-login dev flag and configured username.</param>
    public AutoLoginAuthenticationHandler(
        IOptionsMonitor<AuthenticationSchemeOptions> options,
        ILoggerFactory logger,
        UrlEncoder encoder,
        IOptions<AuthDisableLoginOptions> disableLoginOptions)
        : base(options, logger, encoder)
        => _opts = disableLoginOptions.Value;

    /// <summary>No-op: auto-login writes no cookie, so an explicit sign-in has nothing to persist.</summary>
    /// <param name="user">The principal that would be signed in.</param>
    /// <param name="properties">Authentication properties that would be persisted.</param>
    /// <returns>A task that represents the asynchronous operation.</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 via this handler.</summary>
    /// <param name="properties">Authentication properties that would be cleared.</param>
    /// <returns>A task that represents the asynchronous operation.</returns>
    public Task SignOutAsync(AuthenticationProperties? properties) => Task.CompletedTask;

    /// <inheritdoc />
    protected override Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        var user = string.IsNullOrWhiteSpace(_opts.User) ? "multi-role-test" : _opts.User;

        var claims = new List<Claim>
        {
            // ZbClaimTypes.Name = ClaimTypes.Name — populates Identity.Name canonically.
            new(ZbClaimTypes.Name, user),
            new(ZbClaimTypes.Username, user),
            new(ZbClaimTypes.DisplayName, user),
        };
        foreach (var role in DevAuthRoles.All)
            // ZbClaimTypes.Role = ClaimTypes.Role — framework [Authorize(Roles=...)] + IsInRole work.
            claims.Add(new Claim(ZbClaimTypes.Role, role));

        var identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
        var principal = new ClaimsPrincipal(identity);
        var ticket = new AuthenticationTicket(principal, Scheme.Name);
        return Task.FromResult(AuthenticateResult.Success(ticket));
    }
}
