using System;

namespace StellaOps.Cryptography;

/// <summary>
/// Supported password hashing algorithms.
/// </summary>
public enum PasswordHashAlgorithm
{
    Argon2id,
    Pbkdf2
}

/// <summary>
/// Options describing password hashing requirements.
/// Values follow OWASP baseline guidance by default.
/// </summary>
public sealed record PasswordHashOptions
{
    /// <summary>
    /// Algorithm to use when hashing new passwords.
    /// </summary>
    public PasswordHashAlgorithm Algorithm { get; init; } = PasswordHashAlgorithm.Argon2id;

    /// <summary>
    /// Memory cost in KiB (default 19 MiB).
    /// </summary>
    public int MemorySizeInKib { get; init; } = 19 * 1024;

    /// <summary>
    /// Iteration count / time cost.
    /// </summary>
    public int Iterations { get; init; } = 2;

    /// <summary>
    /// Parallelism / degree of concurrency.
    /// </summary>
    public int Parallelism { get; init; } = 1;

    /// <summary>
    /// Validates the option values and throws when invalid.
    /// </summary>
    public void Validate()
    {
        if (MemorySizeInKib <= 0)
        {
            throw new InvalidOperationException("Password hashing memory cost must be greater than zero.");
        }

        if (Iterations <= 0)
        {
            throw new InvalidOperationException("Password hashing iteration count must be greater than zero.");
        }

        if (Parallelism <= 0)
        {
            throw new InvalidOperationException("Password hashing parallelism must be greater than zero.");
        }
    }
}

/// <summary>
/// Abstraction for password hashing implementations.
/// </summary>
public interface IPasswordHasher
{
    /// <summary>
    /// Produces an encoded hash for the supplied password.
    /// </summary>
    string Hash(string password, PasswordHashOptions options);

    /// <summary>
    /// Verifies the supplied password against a stored hash.
    /// </summary>
    bool Verify(string password, string encodedHash);

    /// <summary>
    /// Detects when an existing encoded hash no longer satisfies the desired options.
    /// </summary>
    bool NeedsRehash(string encodedHash, PasswordHashOptions desired);
}
