using System.Security.Cryptography;
using System.Text;
using Konscious.Security.Cryptography;

namespace Hc900Crawler.Infrastructure.Kb;

public static class PasswordHasher
{
    private const int SaltBytes = 16;
    private const int HashBytes = 32;
    private const int DegreeOfParallelism = 4;
    private const int MemoryKb = 65536;
    private const int Iterations = 3;

    public static (string HashB64, string SaltB64) Hash(string password)
    {
        var salt = RandomNumberGenerator.GetBytes(SaltBytes);
        return (HashWithSalt(password, salt), Convert.ToBase64String(salt));
    }

    public static bool Verify(string password, string hashB64, string saltB64)
    {
        try
        {
            var salt = Convert.FromBase64String(saltB64);
            var expected = Convert.FromBase64String(hashB64);
            var actualB64 = HashWithSalt(password, salt);
            var actual = Convert.FromBase64String(actualB64);
            return CryptographicOperations.FixedTimeEquals(actual, expected);
        }
        catch
        {
            return false;
        }
    }

    private static string HashWithSalt(string password, byte[] salt)
    {
        using var argon = new Argon2id(Encoding.UTF8.GetBytes(password))
        {
            Salt = salt,
            DegreeOfParallelism = DegreeOfParallelism,
            MemorySize = MemoryKb,
            Iterations = Iterations
        };
        return Convert.ToBase64String(argon.GetBytes(HashBytes));
    }

    public static string NewSessionToken()
    {
        Span<byte> buf = stackalloc byte[32];
        RandomNumberGenerator.Fill(buf);
        return Convert.ToHexString(buf).ToLowerInvariant();
    }
}
