namespace NATS.Server.Raft;

public interface IRaftTransport
{
    Task<IReadOnlyList<AppendResult>> AppendEntriesAsync(string leaderId, IReadOnlyList<string> followerIds, RaftLogEntry entry, CancellationToken ct);
    Task<VoteResponse> RequestVoteAsync(string candidateId, string voterId, VoteRequest request, CancellationToken ct);
    Task InstallSnapshotAsync(string leaderId, string followerId, RaftSnapshot snapshot, CancellationToken ct);
}

public sealed class InMemoryRaftTransport : IRaftTransport
{
    private readonly Dictionary<string, RaftNode> _nodes = new(StringComparer.Ordinal);

    public void Register(RaftNode node)
    {
        _nodes[node.Id] = node;
    }

    public Task<IReadOnlyList<AppendResult>> AppendEntriesAsync(string leaderId, IReadOnlyList<string> followerIds, RaftLogEntry entry, CancellationToken ct)
    {
        var results = new List<AppendResult>(followerIds.Count);
        foreach (var followerId in followerIds)
        {
            if (_nodes.TryGetValue(followerId, out var node))
            {
                node.ReceiveReplicatedEntry(entry);
                results.Add(new AppendResult { FollowerId = followerId, Success = true });
            }
            else
            {
                results.Add(new AppendResult { FollowerId = followerId, Success = false });
            }
        }

        return Task.FromResult<IReadOnlyList<AppendResult>>(results);
    }

    public Task<VoteResponse> RequestVoteAsync(string candidateId, string voterId, VoteRequest request, CancellationToken ct)
    {
        if (_nodes.TryGetValue(voterId, out var node))
            return Task.FromResult(node.GrantVote(request.Term, string.IsNullOrWhiteSpace(request.CandidateId) ? candidateId : request.CandidateId));

        return Task.FromResult(new VoteResponse { Granted = false });
    }

    public async Task InstallSnapshotAsync(string leaderId, string followerId, RaftSnapshot snapshot, CancellationToken ct)
    {
        _ = leaderId;
        if (_nodes.TryGetValue(followerId, out var node))
            await node.InstallSnapshotAsync(snapshot, ct);
    }

    public async Task AppendHeartbeatAsync(string leaderId, IReadOnlyList<string> followerIds, int term, CancellationToken ct)
    {
        _ = leaderId;
        foreach (var followerId in followerIds)
        {
            if (_nodes.TryGetValue(followerId, out var node))
                node.ReceiveHeartbeat(term);
        }

        await Task.CompletedTask;
    }
}
