using System;
using System.Threading.Tasks;
using System.Collections.Generic;

using apiwrappercli.poco.order;
using SPTraderWebApi.Model;
using SPTraderWebApi.Controllers;

using Microsoft.AspNetCore.Mvc;

using NUnit.Framework;
using Autofac.Extras.Moq;
using SPTraderWebApi.Services;
using FakeItEasy;
using Microsoft.Extensions.Logging;
using Moq;

// NUnit3 Test setup/cases
// https://nunit.org/
// https://docs.nunit.org/articles/nunit/writing-tests/attributes/testcase.html
// https://www.youtube.com/watch?v=_hqRpzZ7HGs
// TEST -> Right click, debug test [Test Explorer]
// Following sample test WebApi2 MVC endpoints

namespace SPTraderWebApiUnitTest
{
    public class Tests
    {
        private HomeController ctr = null;
        private OrderCacheController occ = null;

        [OneTimeSetUp]
        public void OneTimeSetUp()
        {
            ctr = new HomeController(null);
            occ = new OrderCacheController();
        }

        [SetUp]
        public void SetUp()
        { }

        [TearDown]
        public void TearDown()
        { }

        [OneTimeTearDown]
        public void OneTimeTearDown()
        { }

        [Test]
        public void TestGet()
        {
            // Arrange

            // Act
            var res = ctr.Get();
            var okResult = res as OkObjectResult;

            // Assert
            Assert.IsNotNull(okResult);
            Assert.AreEqual(200, okResult.StatusCode);

            var val = okResult.Value as HomePage;
            Assert.IsTrue(val.Title.Length > 0);
        }

        [Test]
        public async Task TestAsync()
        {
            // Arrange

            // Act
            var res = await ctr.AsynchronousCall();
            var okResult = res as OkObjectResult;

            // Assert
            Assert.IsNotNull(okResult);
            Assert.AreEqual(200, okResult.StatusCode);
            Assert.IsTrue(okResult.Value is string);
            //Assert.IsType<string>(okResult.Value);

            var val = okResult.Value as string;
            Assert.AreEqual(val, "this is async call");
        }

        [TestCase("Tim", 40)]
        [TestCase("Elsa", 23)]
        [TestCase("Luna", 19)]
        public void TestPost(string name, int age)
        {
            // Arrange
            var rm = new ReqMessage()
            {
                Name = name,
                Age = age
            };

            // Act
            var res = ctr.GetJsonString(rm);
            var okResult = res as OkObjectResult;

            // Assert
            Assert.IsNotNull(okResult);
            Assert.AreEqual(200, okResult.StatusCode);
            Assert.That(okResult.Value, Is.TypeOf<ReqMessage>());

            var val = okResult.Value as ReqMessage;
            Assert.AreEqual(val.Name, name);
            Assert.AreEqual(val.Age, age);
        }

        [Ignore("Ignore this test")]
        [TestCase("TIMHSU01")]
        public void TestGetOrders(string accNo)
        {
            // Arrange
            // DOESN'T WORK, WAIT ONE ON ANOTHER THREAD CAUSES ISSUES

            // Act
            var res = occ.GetOrders(accNo);
            var okResult = res as OkObjectResult;

            // Assert
            Assert.IsNotNull(okResult);
            Assert.AreEqual(200, okResult.StatusCode);
            Assert.That(okResult.Value, Is.TypeOf<List<SPOrder>>());

            var ords = okResult.Value as List<SPOrder>;

            foreach (var ord in ords)
            {
                Assert.AreEqual(ord.AccNo, accNo);
            }
        }

        #region Mock Demo
        // https://www.youtube.com/watch?v=DwbYxP-etMY

        [Ignore("Ignore this test")]
        [TestCase("Tim", 43)]
        [TestCase("Edea", 45)]
        public void TestMock_Post(string name, int age)
        {
            // Arrange
            var req = new ReqMessage { Name = name, Age = age };
            // DOESN'T WORK, UNKNOWN HOW TO USE MOCK

            var cls = Mock.Of<HomeController>();
            var res = cls.GetJsonString(req);

            Assert.That(res, Is.TypeOf<OkObjectResult>());

            var actual = res as OkObjectResult;
            var actual_value = actual.Value as ReqMessage;

            // Assert
            Assert.AreEqual(actual_value.Name, name);
            Assert.AreEqual(actual_value.Age, age);
        }

        [Test]
        public void TestMock_Login()
        {
            using (var mock = AutoMock.GetLoose())
            {
                // Arrange
                var cls = mock.Mock<ILoginService>()
                    .Setup(x => x.LoginTServer())
                    .Returns(GetLoginResult());

                // Act
                Assert.IsNotNull(cls);
            }
        }

        private bool GetLoginResult() { return true; }

        [TestCase("Tim", 43)]
        [TestCase("Edea", 45)]
        [TestCase("Lightning", 23)]
        public void Test_FakeItEasy_Logger(string name, int age)
        {
            // Arrange
            var fakeLog = A.Fake<ILoginService>();
            A.CallTo(() => fakeLog.LoginTServer()).Returns(true);
                
            // Act
            var ctrl = new HomeController(null);    // should replace null with fakeLog
            var res = ctrl.GetJsonString(new ReqMessage { Name = name, Age = age });

            // Assert
            Assert.That(res, Is.TypeOf<OkObjectResult>());

            var actual = res as OkObjectResult;
            var actual_value = actual.Value as ReqMessage;

            // Assert
            Assert.AreEqual(actual_value.Name, name);
            Assert.AreEqual(actual_value.Age, age);
        }

        #endregion
    }
}