using System.Text;
using Geralt;
using JetBrains.Annotations;

namespace Femto.Modules.Auth.Models;

internal class Password
{
    private const int Iterations = 3;
    private const int MemorySize = 67108864;
    
    public byte[] Hash { get; private set; }
    
    public byte[] Salt { get; private set; }
    
    [UsedImplicitly]
    private Password() {}
    
    public Password(string password)
    {
        this.Salt = ComputeSalt();
        this.Hash = ComputePasswordHash(password, Salt);
    }

    public bool Check(string password)
    {
        var matches = Argon2id.VerifyHash(
            Hash,
            Combine(password, Salt)
        );

        if (!matches)
            return false;

        if (Argon2id.NeedsRehash(Hash, Iterations, MemorySize))
        {
            this.Salt = ComputeSalt();
            this.Hash = ComputePasswordHash(password, Salt);
        }

        return true;
    }
    
    private static byte[] ComputeSalt() => System.Security.Cryptography.RandomNumberGenerator.GetBytes(32);
    
    private static byte[] ComputePasswordHash(string password, byte[] salt)
    {
        var hash = new byte[128];
        try
        {
            Argon2id.ComputeHash(hash, Combine(password, salt), Iterations, MemorySize);
        }
        catch (Exception e)
        {
            throw new SetPasswordError("Failed to hash password", e);
        }

        return hash;
    }

    private static byte[] Combine(string password, byte[] salt)
    {
        var passwordBytes = Encoding.UTF8.GetBytes(password);
        var hashInput = new byte[passwordBytes.Length + salt.Length];
        passwordBytes.CopyTo(hashInput, 0);
        salt.CopyTo(hashInput, passwordBytes.Length);
        
        return hashInput;
    }
}