using AutoMapper;
using DigitalData.Core.Abstractions.Security.Extensions;
using DigitalData.Core.Abstractions.Security.Key;
using DigitalData.Core.Abstractions.Security.Services;
using DigitalData.Core.Security.Config;
using DigitalData.Core.Security.Extensions;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;

namespace DigitalData.Core.Security.Services;

public class JwtSignatureHandler<TPrincipal> : JwtSecurityTokenHandler, IJwtSignatureHandler<TPrincipal>
{
    private readonly ClaimDescriptor<TPrincipal> _claimDescriptor;

    private readonly IMapper _mapper;

    private readonly IAsymmetricKeyPool _cryptoFactory;

    public JwtSignatureHandler(IOptions<ClaimDescriptor<TPrincipal>> claimDescriptorOptions, IMapper mapper, IAsymmetricKeyPool cryptoFactory)
    {
        _claimDescriptor = claimDescriptorOptions.Value;
        _mapper = mapper;
        _cryptoFactory = cryptoFactory;
    }

    public SecurityToken CreateToken(TPrincipal subject, IAsymmetricTokenDescriptor descriptor)
    {
        var sDescriptor = _mapper.Map(descriptor);
        sDescriptor.Claims = _claimDescriptor.CreateClaims?.Invoke(subject);
        sDescriptor.Subject = _claimDescriptor.CreateSubject?.Invoke(subject);
        return CreateToken(sDescriptor);
    }

    public SecurityToken CreateToken(TPrincipal subject, string issuer, string audience)
    {
        var descriptor = _cryptoFactory.TokenDescriptors.Get(issuer: issuer, audience: audience)
            ?? throw new InvalidOperationException($"No or multiple token description found for issuer '{issuer}' and audience '{audience}'.");
        return CreateToken(subject: subject, descriptor: descriptor);
    }
}