using System;

namespace Tests
{
	using CustomComponentExample;
	using Moq;
	using Softadmin.Api.Database;
	using System.Data;
	using Xunit;

	public class ExampleCustomComponentTests
	{
		private ExampleCustomComponent Tested { get; set; } = new ExampleCustomComponent();

		private Mock<IDbRequest> GetDataRequest { get; set; }

		private Mock<IDbRequest> SuccessRequest { get; set; }

		private Mock<IDbRequestFactory> RequestFactory { get; set; }

		private DataSet GetDataDataSet { get; set; } = new DataSet();

		public ExampleCustomComponentTests()
		{
			// Create a fake request that will behave as if the database was called, but instead returns a DataSet defined in this test class.
			this.GetDataRequest = new Mock<IDbRequest>();
			this.GetDataRequest.Setup(x => x.GetDataSet()).Returns(this.GetDataDataSet); // Setup the fake request to always return this.ObjectIdDataSet when GetDataSet() is called.

			// Do the same thing for the finished call, but no return DataSet is expected or needed.
			this.SuccessRequest = new Mock<IDbRequest>();

			// Create a fake request factory who's only job is to create IDbRequest-objects.
			this.RequestFactory = new Mock<IDbRequestFactory>();
			// Setup the request factory to always return the this.GetDataRequest object
			// created above when the CreateRequest-method is called with the string "Get data".
			// And return this.FinishedRequest when called with the string "Report success".
			this.RequestFactory.Setup(x => x.CreateRequest("Get data")).Returns(this.GetDataRequest.Object);
			this.RequestFactory.Setup(x => x.CreateRequest("Report success")).Returns(this.SuccessRequest.Object);
			this.Tested.DbRequestFactory = this.RequestFactory.Object; // Tell the custom component to use this fake object.
		}

		// Actual tests.

		[Fact] // Fact-attribute to indicate it's a test and not a helper method.
		public void DoesNothingOnEmptyResultSet()
		{
			// Make sure the component does not crash when there is not data returned.
			this.Tested.Execute();

			// Verify success SQL call was executed (but do not care about what it was called with).
			this.SuccessRequest.Verify(r => r.ExecuteNonQuery());
		}

		[Fact]
		public void SumsSingleValue()
		{
			// Make the data set contain a table with one column "Value", with the value 1.
			var table = new DataTable();
			table.Columns.Add("Value", typeof(int));
			var row = table.NewRow();
			row["Value"] = 1;
			table.Rows.Add(row);
			this.GetDataDataSet.Tables.Add(table);

			// Execute component.
			this.Tested.Execute();

			// Verify the SuccessRequest fake-database-call had the sum-parameter set to 1.
			this.SuccessRequest.Verify(r => r.SetParameter("Sum", 1));
			// And the SQL was executed.
			this.SuccessRequest.Verify(r => r.ExecuteNonQuery());
		}

		[Fact]
		public void ThrowsOnWrongMissingValueColumn()
		{
			// Make the data set contain a table with one column "ErrorColumnName", with the value 1.
			var table = new DataTable();
			table.Columns.Add("ErrorColumnName", typeof(int));
			var row = table.NewRow();
			row["ErrorColumnName"] = 1;
			table.Rows.Add(row);
			this.GetDataDataSet.Tables.Add(table);

			// Make sure any exception is thrown.
			Assert.ThrowsAny<Exception>(() => this.Tested.Execute());
		}

		[Fact]
		public void AddsTwoRowsToSum()
		{
			// Make the data set contain a table with one column "Value", with 2 rows with the values 1 and 5.
			var table = new DataTable();
			table.Columns.Add("Value", typeof(int));
			var row1 = table.NewRow();
			row1["Value"] = 1;
			table.Rows.Add(row1);
			var row2 = table.NewRow();
			row2["Value"] = 5;
			table.Rows.Add(row2);
			this.GetDataDataSet.Tables.Add(table);

			// Execute component.
			this.Tested.Execute();

			// Verify the SuccessRequest fake-database-call had the sum-parameter set to 6 (5+1).
			this.SuccessRequest.Verify(r => r.SetParameter("Sum", 6));
			// And the SQL was executed.
			this.SuccessRequest.Verify(r => r.ExecuteNonQuery());
		}
	}
}