using System.Security.Claims;
using System.Text.Json;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication.Internal;

namespace ChessCubing.App.Services;

public sealed class KeycloakAccountFactory(IAccessTokenProviderAccessor accessor)
    : AccountClaimsPrincipalFactory<RemoteUserAccount>(accessor)
{
    public override async ValueTask<ClaimsPrincipal> CreateUserAsync(
        RemoteUserAccount account,
        RemoteAuthenticationUserOptions options)
    {
        var user = await base.CreateUserAsync(account, options);
        if (user.Identity is not ClaimsIdentity identity || !identity.IsAuthenticated)
        {
            return user;
        }

        var roleClaimType = options.RoleClaim ?? "role";
        var existingRoles = identity.FindAll(roleClaimType)
            .Select(claim => claim.Value)
            .ToHashSet(StringComparer.OrdinalIgnoreCase);

        foreach (var role in ReadRealmRoles(account))
        {
            if (existingRoles.Add(role))
            {
                identity.AddClaim(new Claim(roleClaimType, role));
            }
        }

        return user;
    }

    private static IEnumerable<string> ReadRealmRoles(RemoteUserAccount account)
    {
        if (!account.AdditionalProperties.TryGetValue("realm_access", out var realmAccessValue) ||
            realmAccessValue is not JsonElement realmAccessElement ||
            realmAccessElement.ValueKind != JsonValueKind.Object ||
            !realmAccessElement.TryGetProperty("roles", out var rolesElement) ||
            rolesElement.ValueKind != JsonValueKind.Array)
        {
            return [];
        }

        return rolesElement.EnumerateArray()
            .Where(item => item.ValueKind == JsonValueKind.String)
            .Select(item => item.GetString())
            .OfType<string>();
    }
}
