using Microsoft.AspNetCore.Identity;
using OpenIddict.Abstractions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using static OpenIddict.Abstractions.OpenIddictConstants;

namespace Ablera.Serdica.Authentication.Extensions
{
    public static class ClaimExtensions
    {
        public static IReadOnlyCollection<Claim> BuildClaims<TKeyType>(
            this IdentityUser<TKeyType> identity,
            string? userName = null, string? givenName = null, string? surname = null)
            where TKeyType : IEquatable<TKeyType> => new[]
        {
            new Claim(ClaimTypes.NameIdentifier,            identity.Id?.ToString() ?? string.Empty),
            new Claim(Claims.Subject,                       identity.Id?.ToString() ?? string.Empty),
            new Claim(ClaimTypes.Name,                      userName ?? identity.UserName   ?? string.Empty),
            new Claim(ClaimTypes.GivenName,                 givenName                       ?? string.Empty),
            new Claim(ClaimTypes.Surname,                   surname                         ?? string.Empty),
            new Claim(ClaimTypes.Email,                     identity.Email                  ?? string.Empty)
        };

        public static IEnumerable<string> DestinationsSelector(this Claim c) => c.Type switch
        {
            Claims.Name or Claims.PreferredUsername
                => new[] { Destinations.AccessToken, Destinations.IdentityToken },

            Claims.Email when c.Subject?.HasScope(Scopes.Email) == true
                => new[] { Destinations.AccessToken, Destinations.IdentityToken },

            Claims.Role when c.Subject?.HasScope(Scopes.Roles) == true
                => new[] { Destinations.AccessToken, Destinations.IdentityToken },

            _ => new[] { Destinations.AccessToken }
        };

        public static string? GetUserId(this ClaimsPrincipal user)
            => user.Claims.GetUserId() ?? Guid.Empty.ToString();

        public static string? GetUserEmail(this ClaimsPrincipal user)
            => user.Claims
                .FirstOrDefault(x => x.Type == ClaimTypes.Email)
                ?.Value?.ToString();

        private static string? GetUserId(this IEnumerable<Claim> claims)
            => claims
                .FirstOrDefault(x => x.Type == ClaimTypes.NameIdentifier)
                ?.Value?.ToString()
            ?? claims
                .FirstOrDefault(x => x.Type == ClaimTypes.Name)
                ?.Value?.ToString();

        public static string? GetClientApplicationId(this ClaimsPrincipal user)
            => user.Claims.GetClientApplicationId();

        private static string? GetClientApplicationId(this IEnumerable<Claim> claims)
            => claims
                .FirstOrDefault(x => x.Type == Claims.Subject)
                ?.Value?.ToString()
            ?? claims
                .FirstOrDefault(x => x.Type == Claims.ClientId)
                ?.Value?.ToString();
    }
}
