﻿using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using DotNetCqs.CommonServiceLocator;
using DotNetCqs.TestMessaging;
using Microsoft.Practices.ServiceLocation;
using Moq;
using NUnit.Framework;

namespace DotNetCqs.Tests.CommonServiceLocator
{
	[TestFixture]
	public class IocEventBusTests
	{
		private Mock<IServiceLocator> _serviceLocator;
		private Mock<IApplicationEventSubscriber<MultiApplicationEvent>> _eventSubscriber1;
		private Mock<IApplicationEventSubscriber<MultiApplicationEvent>> _eventSubscriber2;

		private MultiApplicationEvent _applicationEvent;
		private IocEventBus _eventBus;

		[SetUp]
		public void SetUp()
		{
			_eventSubscriber1 = new Mock<IApplicationEventSubscriber<MultiApplicationEvent>>();
			_eventSubscriber2 = new Mock<IApplicationEventSubscriber<MultiApplicationEvent>>();

			_serviceLocator = new Mock<IServiceLocator>();
			_serviceLocator.Setup(x => x.GetAllInstances<IApplicationEventSubscriber<MultiApplicationEvent>>())
				.Returns(new[] { _eventSubscriber1.Object });

			_applicationEvent = new MultiApplicationEvent("hello world");
			_eventBus = new IocEventBus(_serviceLocator.Object);
		}

		[Test]
		public void must_have_IServiceLocator_to_work_successfully()
		{
			// act
			Assert.Throws<ArgumentNullException>(() => new IocEventBus(null));
		}


		[Test]
		public async Task works_with_multiple_handlers()
		{
			// arrange
			_serviceLocator.Setup(x => x.GetAllInstances<IApplicationEventSubscriber<MultiApplicationEvent>>())
				.Returns(new[] { _eventSubscriber1.Object, _eventSubscriber2.Object });

			// act
			await _eventBus.PublishAsync(_applicationEvent);

			// assert
			_eventSubscriber1.Verify(x => x.HandleAsync(_applicationEvent), Times.Once);
			_eventSubscriber2.Verify(x => x.HandleAsync(_applicationEvent), Times.Once);
		}

		/*[Test]
		public async Task one_failing_handler_should_not_abort_others()
		{
			// arrange
			_eventSubscriber1.Setup(x => x.HandleAsync(It.IsAny<MultiApplicationEvent>())).Throws<AggregateException>();

			_serviceLocator.Setup(x => x.GetAllInstances<IApplicationEventSubscriber<MultiApplicationEvent>>())
				.Returns(new[] { _eventSubscriber1.Object, _eventSubscriber2.Object });

			Exception actualException = null;

			// act
			try
			{
				await _eventBus.PublishAsync(_applicationEvent);
			}
			catch (Exception e)
			{
				actualException = e;
			}

			// assert
			_eventSubscriber1.Verify(x => x.HandleAsync(_applicationEvent), Times.Once);
			_eventSubscriber2.Verify(x => x.HandleAsync(_applicationEvent), Times.Once);

			Assert.IsInstanceOf<AggregateException>(actualException);
		}*/

		[Test]
		public async Task works_without_subscribers_since_there_is_no_coupling_between_the_publisher_and_subscribers()
		{
			// arrange
			_serviceLocator.Setup(x => x.GetAllInstances<IApplicationEventSubscriber<UnusedEvent>>())
				.Returns(new List<IApplicationEventSubscriber<UnusedEvent>>());

			// act
			await _eventBus.PublishAsync(_applicationEvent);
		}

		[Test]
		public async Task works_with_null_subscribers_since_there_is_no_coupling_between_the_publisher_and_subscribers()
		{
			// arrange
			_serviceLocator.Setup(x => x.GetAllInstances<IApplicationEventSubscriber<UnusedEvent>>())
				.Returns((IEnumerable<IApplicationEventSubscriber<UnusedEvent>>)null);

			// act
			await _eventBus.PublishAsync(_applicationEvent);
		}
	}
}
