using System.Threading.Channels;
using DodoSSH.Client.Api;
using DodoSSH.Contracts;

namespace DodoSSH.Client.App.Tests;

/// <summary>
/// A server's push channel, driven by a test rather than by a socket.
/// </summary>
/// <remarks>
/// The real <c>VaultEventStream</c> is a reconnection policy wrapped round a WebSocket, and none of
/// that is what the shell's behaviour depends on: what the shell does with a notice is the same
/// whether it arrived over a healthy socket, after four reconnections, or from this. Driving it by
/// hand is what makes "the loop synchronised because it was told to, not because a minute passed" a
/// test that finishes in milliseconds and cannot flake.
/// </remarks>
internal sealed class FakeVaultEventStream : IVaultEventStream
{
    private readonly Channel<VaultEvent> notices = Channel.CreateUnbounded<VaultEvent>();

    /// <inheritdoc />
    public bool IsConnected => true;

    /// <summary>How many times the shell has waited on this. Proves the loop is watching at all.</summary>
    internal int Reads { get; private set; }

    /// <summary>Delivers a notice, as a server would.</summary>
    internal void Push(Guid vaultId, long sequence = 1) =>
        notices.Writer.TryWrite(
            new VaultEvent(VaultEventKinds.VaultChanged, vaultId, sequence));

    /// <summary>Delivers the notice that says the caller's vault list has changed.</summary>
    internal void PushAccessChanged() =>
        notices.Writer.TryWrite(new VaultEvent(VaultEventKinds.VaultsChanged));

    /// <inheritdoc />
    public ValueTask<VaultEvent> ReadAsync(CancellationToken cancellationToken)
    {
        Reads++;

        return notices.Reader.ReadAsync(cancellationToken);
    }

    /// <inheritdoc />
    public bool TryRead(out VaultEvent notice) => notices.Reader.TryRead(out notice!);

    /// <inheritdoc />
    public void Dispose() => notices.Writer.TryComplete();
}
