﻿using System;
using System.Threading;
using System.Threading.Tasks;
using NUnit.Framework;
using Rebus.Activation;
using Rebus.Config;
using Rebus.Persistence.InMem;
using Rebus.Routing.TypeBased;
using Rebus.Tests.Contracts;
using Rebus.Tests.Contracts.Extensions;
using Rebus.Tests.Extensions;
using Rebus.Timeouts;
using Rebus.Transport.InMem;
#pragma warning disable 1998

namespace Rebus.Tests.Integration;

[TestFixture]
public class TestMessageDeferral : FixtureBase
{
    readonly InMemNetwork _network = new InMemNetwork(true);

    BuiltinHandlerActivator _server;
    BuiltinHandlerActivator _client;

    protected override void SetUp()
    {
        _network.Reset();

        _server = CreateBus("test.message.deferral", configurer =>
        {
            configurer.Timeouts(t => t.StoreInMemory());
        });

        _client = CreateBus("test.message.deferral.CLIENT", configurer =>
        {
            configurer
                .Routing(r => r.TypeBased().Map<string>("test.message.deferral"))
                .Timeouts(t => t.UseExternalTimeoutManager("test.message.deferral"));
        });
    }

    [Test]
    public async Task CanDeferMessage_ToSelf()
    {
        var messageReceived = new ManualResetEvent(false);
        var deliveryTime = DateTime.MaxValue;

        _server.AddHandlerWithBusTemporarilyStopped<string>(async s =>
        {
            deliveryTime = DateTime.UtcNow;
            messageReceived.Set();
        });

        var sendTime = DateTime.UtcNow;
        var delay = TimeSpan.FromSeconds(5);

        await _server.Bus.DeferLocal(delay, "hej med dig!");

        messageReceived.WaitOrDie(TimeSpan.FromSeconds(8));

        var timeToBeDelivered = deliveryTime - sendTime;

        Assert.That(timeToBeDelivered, Is.GreaterThanOrEqualTo(delay));
    }

    [Test]
    public async Task CanDeferMessage_ToAnotherDestination()
    {
        var messageReceived = new ManualResetEvent(false);
        var deliveryTime = DateTime.MaxValue;

        _server.AddHandlerWithBusTemporarilyStopped<string>(async s =>
        {
            deliveryTime = DateTime.UtcNow;
            messageReceived.Set();
        });

        var sendTime = DateTime.UtcNow;
        var delay = TimeSpan.FromSeconds(5);

        await _client.Bus.Defer(delay, "hej med dig!");

        messageReceived.WaitOrDie(TimeSpan.FromSeconds(8));

        var timeToBeDelivered = deliveryTime - sendTime;

        Assert.That(timeToBeDelivered, Is.GreaterThanOrEqualTo(delay));
    }

    BuiltinHandlerActivator CreateBus(string queueName, Action<RebusConfigurer> additionalConfiguration = null)
    {
        var activator = new BuiltinHandlerActivator();

        Using(activator);

        var configurer = Configure.With(activator)
            .Transport(t => t.UseInMemoryTransport(_network, queueName));

        additionalConfiguration?.Invoke(configurer);

        configurer.Start();

        return activator;
    }
}