using DigitalData.Core.Security.Config;
using DigitalData.Core.Security.Extensions;
using DigitalData.Core.Security.RSAKey.Auth;
using DigitalData.Core.Security.RSAKey.Base;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace DigitalData.Core.Security.Services;

public class PemFileInitalizer : BackgroundService
{
    private readonly RSAParams _factoryParams;

    private readonly ILogger<PemFileInitalizer> _logger;

    public PemFileInitalizer(IOptions<RSAParams> factoryParamsOptions, ILogger<PemFileInitalizer> logger)
    {
        _factoryParams = factoryParamsOptions.Value;
        _logger = logger;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        try
        {
            _logger.LogInformation("Pem file initalizer launched.");
            await InitPemFiles(stoppingToken);
        }
        catch(Exception ex)
        { 
            _logger.LogError(ex, "Pem files cannot be initialized.");
        }
    }

    private async Task InitPemFiles(CancellationToken stoppingToken = default)
    {
        _logger.LogInformation("Initializing pem files. PemDirectory: {dir}", _factoryParams.PemDirectory);
        // Create root folder if it does not exist
        if (!Directory.Exists(_factoryParams.PemDirectory))
            Directory.CreateDirectory(_factoryParams.PemDirectory);

        var privateKeys = new List<RSAPrivateKey>();
        privateKeys.AddRange(_factoryParams.Decryptors);
        privateKeys.AddRange(_factoryParams.TokenDescriptors);
        if (_factoryParams.VaultDecryptor is not null)
            privateKeys.Add(_factoryParams.VaultDecryptor);

        foreach (var privateKey in privateKeys)
        {
            // set default path
            if (privateKey.IsPemNull)
            {
                // file name 
                var file_name_params = new List<object>();

                if (privateKey.Id is not null)
                    file_name_params.Add(privateKey.Id);
                else if (privateKey is RSATokenDescriptor descriptor)
                    file_name_params.Add(descriptor.Issuer);

                file_name_params.Add(_factoryParams.KeySizeInBits);
                file_name_params.Add(DateTime.Now.ToTag(_factoryParams.DateTagFormat));

                if (privateKey.IsEncrypted)
                    file_name_params.Add(Secrets.Version);

                var file_name = $"{string.Join(_factoryParams.FileNameSeparator, file_name_params)}.{_factoryParams.FileExtension}";

                var path = Path.Combine(_factoryParams.PemDirectory, file_name);

                if (File.Exists(path))
                    privateKey.SetPem(File.ReadAllText(path));
                else
                {
                    var pem = privateKey.IsEncrypted
                        ? RSAFactory.Static.CreateEncryptedPrivateKeyPem(pbeParameters: _factoryParams.PbeParameters, keySizeInBits: _factoryParams.KeySizeInBits, password: Secrets.PBE_PASSWORD)
                        : RSAFactory.Static.CreatePrivateKeyPem(keySizeInBits: _factoryParams.KeySizeInBits);

                    privateKey.SetPem(pem);

                    // Save file in background
                    await File.WriteAllTextAsync(path: path, pem, stoppingToken);
                }
            }
        }
    }
}
