using DigitalData.Core.Security.RSAKey.Auth;
using DigitalData.Core.Security.RSAKey.Crypto;
using System.Reflection;
using System.Security.Cryptography;

namespace DigitalData.Core.Security.Config;

public class RSAParams
{
    #region Factory Params
    public int KeySizeInBits { get; init; } = Default.KeySizeInBits;

    public string PbePassword { internal get; init; } = Default.PbePassword;

    public PbeEncryptionAlgorithm PbeEncryptionAlgorithm { get; init; } = Default.PbeEncryptionAlgorithm;

    public HashAlgorithmName PbeHashAlgorithm { get; init; } = Default.PbeHashAlgorithm;

    // TODO: add as json converter to IConfigurIConfiguration.Config
    public string PbeHashAlgorithmName
    {
        get => PbeHashAlgorithm.ToString();
        init => PbeHashAlgorithm = (typeof(HashAlgorithmName).GetProperty(value, BindingFlags.Public | BindingFlags.Static)?.GetValue(null) is HashAlgorithmName hashAlgorithmName)
            ? hashAlgorithmName
            : new(value);
    }

    public int PbeIterationCount { get; init; } = Default.PbeIterationCount;

    public string EncryptedPrivateKeyPemLabel { get; init; } = Default.EncryptedPrivateKeyPemLabel;

    public PbeParameters PbeParameters => new(PbeEncryptionAlgorithm, PbeHashAlgorithm, PbeIterationCount);

    public static class Default
    {
        public static readonly int KeySizeInBits = 2048;

        public static readonly string PbePassword = Secrets.PBE_PASSWORD;

        public static readonly PbeEncryptionAlgorithm PbeEncryptionAlgorithm = PbeEncryptionAlgorithm.Aes256Cbc;

        public static readonly HashAlgorithmName PbeHashAlgorithm = HashAlgorithmName.SHA256;

        public static readonly int PbeIterationCount = 100_000;

        public static readonly string EncryptedPrivateKeyPemLabel = "ENCRYPTED PRIVATE KEY";

        public static readonly PbeParameters PbeParameters = new(PbeEncryptionAlgorithm, PbeHashAlgorithm, PbeIterationCount);
    }
    #endregion

    #region Pool Params
    public string PemDirectory { get; init; } = string.Empty;

    /// <summary>
    /// Represents the separator used to concatenate the components of a file-related token string.
    /// </summary>
    /// <remarks>
    /// The resulting file-related token string is constructed as follows:
    /// <c>string.Join(FileNameSeparator, Issuer, Audience, Secret_version)</c>.
    /// If <c>Secret_version</c> is not null, it will be included in the concatenation.
    /// </remarks>
    /// <example>
    /// For example, if <c>FileNameSeparator = "_-_"</c>, the output might look like:
    /// <c>"Issuer_-_Audience_-_Secret_version"</c>.
    /// </example>
    public string FileNameSeparator { get; init; } = "_-_";

    public string FileExtension { get; init; } = "pem";

    /// <summary>
    ///This is the subtext of the pem file name. For the file to be automatically renewed, the name must be assigned to change periodically. For example, by default MM/2 will be refreshed every 2 months.
    /// <br />
    /// - <see cref="StringExtensions.ToTag(DateTime, string)" /> is used when converting to tag.
    /// <br />
    /// - If the format contains the symbol “//”, the method divides the numeric value obtained from the left side of the format
    ///   by one minus the numeric value obtained from the right side of the format string and adds one. For instance:
    /// <br />
    /// - If the date is 02.03.2024 and the format is "MM//2", it extracts the month (02), subtracts one (3), divides it by 2,
    ///   rounds down the outgoing number (1), adds one to the number (resulting in 2).
    ///   <br />
    /// - If the format does not contain "//", the method uses the default <see cref="DateTime.ToString"/> format.
    /// <br />
    /// This method provides a way to format the date based on typical or customized rules, including mathematical operations like division.
    /// </summary>
    public string DateTagFormat { get; init; } = "MM//2";

    public IEnumerable<RSADecryptor> Decryptors { get; init; } = new List<RSADecryptor>();

    public IEnumerable<RSATokenDescriptor> TokenDescriptors { get; init; } = new List<RSATokenDescriptor>();

    public RSADecryptor? VaultDecryptor { get; init; }
    #endregion
}