namespace DodoSSH.Client.Session.Tests;

/// A device key store that keeps its key in a field.
/// </summary>
/// <remarks>
/// Stands in for whatever guards the key on a real machine. The gesture is the entire security value of the
/// real thing, so what this fake models is the two ways the gesture ends: it hands the key over, or it does
/// not. <see cref="Decline"/> is a cancelled prompt and an invalidated key at once, which is exactly how much
/// the caller is allowed to know.
/// </remarks>
internal sealed class FakeDeviceKeyStore : IDeviceKeyStore
{
    private byte[]? key;

    /// <summary>When set, the next load refuses, as a cancelled gesture does.</summary>
    internal bool Decline { get; set; }

    /// <summary>Whether this machine can keep a key at all.</summary>
    internal bool IsAvailable { get; set; } = true;

    /// <summary>Reads the stored key without a gesture, for assertions only.</summary>
    internal byte[]? Peek() => key;

    /// <summary>Replaces the stored key, standing in for a rotated or corrupted keystore entry.</summary>
    internal void Overwrite(byte[] replacement) => key = replacement;

    /// <inheritdoc />
    public ValueTask<bool> IsAvailableAsync(CancellationToken cancellationToken) =>
        ValueTask.FromResult(IsAvailable);

    /// <inheritdoc />
    public ValueTask SaveAsync(ReadOnlyMemory<byte> devicePrivateKey, CancellationToken cancellationToken)
    {
        key = devicePrivateKey.ToArray();
        return ValueTask.CompletedTask;
    }

    /// <inheritdoc />
    public ValueTask<byte[]?> TryLoadAsync(CancellationToken cancellationToken) =>
        ValueTask.FromResult(Decline ? null : key?.ToArray());

    /// <inheritdoc />
    public ValueTask ForgetAsync(CancellationToken cancellationToken)
    {
        key = null;
        return ValueTask.CompletedTask;
    }
}
