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;

namespace ZB.MOM.WW.ScadaBridge.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="Roles.All"/> roles, system-wide — no credential check, no cookie.
/// The minted principal mirrors a real login (it reuses <see cref="SessionClaimBuilder"/>).
/// Dev/test ONLY.
/// </summary>
public sealed class AutoLoginAuthenticationHandler
    : AuthenticationHandler<AuthenticationSchemeOptions>, IAuthenticationSignInHandler
{
    // Only _opts.User is consumed here. The DisableLogin flag is gated at registration time
    // (AddSecurity); if DisableLogin is false this handler is never registered, so it is
    // never reached and the flag itself is irrelevant inside the handler.
    private readonly AuthDisableLoginOptions _opts;
    private readonly TimeProvider _clock;

    /// <summary>Initializes the handler with the scheme plumbing, the disable-login options, and the clock.</summary>
    /// <param name="options">The authentication scheme options monitor.</param>
    /// <param name="logger">The logger factory used by the base handler.</param>
    /// <param name="encoder">The URL encoder used by the base handler.</param>
    /// <param name="disableLoginOptions">The disable-login configuration, including the dev user to impersonate.</param>
    /// <param name="clock">The time provider used to stamp the minted principal's refresh timestamp.</param>
    public AutoLoginAuthenticationHandler(
        IOptionsMonitor<AuthenticationSchemeOptions> options,
        ILoggerFactory logger,
        UrlEncoder encoder,
        IOptions<AuthDisableLoginOptions> disableLoginOptions,
        TimeProvider clock)
        : base(options, logger, encoder)
    {
        _opts = disableLoginOptions.Value;
        _clock = clock;
    }

    /// <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 accompany the sign-in.</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 accompany the sign-out.</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" : _opts.User;

        // All roles, system-wide (no site-scope claims). Reuse the canonical builder so the
        // principal is shape-identical to a real all-roles system-wide login.
        var mapping = new RoleMappingResult(Roles.All, [], IsSystemWideDeployment: true);
        var principal = SessionClaimBuilder.Build(
            username: user,
            displayName: user,
            groups: [],
            mapping: mapping,
            refreshTimestamp: _clock.GetUtcNow());

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