using NUnit.Framework;
using Rhino.Mocks;
using System;
using System.Collections.Generic;
using TINK.Model.State;


namespace TestTINKLib
{
    
    [TestFixture]
    public class TestStateInfo
    {
        [Test]
        public void TestConstruct()
        {
            var l_oInUseState = new StateInfoMutable(() => new DateTime(2017, 09, 20, 23, 20, 0));

            // Verify initial values of properties.
            Assert.AreEqual(InUseStateEnum.Disposable, l_oInUseState.Value, "Default state is available");
            Assert.AreEqual("Disposable", l_oInUseState.ToString());
            Assert.IsNull(l_oInUseState.RemainingTime);
            Assert.IsNull(l_oInUseState.From);
            Assert.IsNull(l_oInUseState.Code);
        }

        [Test]
        public void TestConstructCopy()
        {
            // State is available.
            var l_oSource = MockRepository.GenerateStub<IStateInfo>();
            l_oSource.Stub(x => x.Value).Return(InUseStateEnum.Disposable);

            var l_oState = new StateInfoMutable(p_oState: l_oSource);

            Assert.AreEqual(InUseStateEnum.Disposable, l_oState.Value);
            Assert.IsNull(l_oState.RemainingTime);
            Assert.IsNull(l_oState.From);
            Assert.IsNull(l_oState.Code);

            // State is requested.
            l_oSource = MockRepository.GenerateStub<IStateInfo>();
            l_oSource.Stub(x => x.Value).Return(InUseStateEnum.Reserved);
            l_oSource.Stub(x => x.From).Return(new DateTime(2018, 1, 4, 17, 26, 0));
            l_oSource.Stub(x => x.MailAddress).Return("who@the");
            l_oSource.Stub(x => x.Code).Return("323");

            l_oState = new StateInfoMutable(() => new DateTime(2018, 1, 4, 17, 30, 1), l_oSource);

            Assert.AreEqual(InUseStateEnum.Reserved, l_oState.Value);
            Assert.AreEqual(new TimeSpan(0, 10, 59), l_oState.RemainingTime);
            Assert.AreEqual(new DateTime(2018, 1, 4, 17, 26, 0), l_oState.From);
            Assert.AreEqual("who@the", l_oState.MailAddress);
            Assert.AreEqual("323", l_oState.Code);

            // State is booked.
            l_oSource = MockRepository.GenerateStub<IStateInfo>();
            l_oSource.Stub(x => x.Value).Return(InUseStateEnum.Booked);
            l_oSource.Stub(x => x.From).Return(new DateTime(2018, 1, 4, 17, 00, 0));
            l_oSource.Stub(x => x.MailAddress).Return("who@the");
            l_oSource.Stub(x => x.Code).Return("323");

            l_oState = new StateInfoMutable(() => new DateTime(2018, 1, 4, 17, 30, 1), l_oSource);

            Assert.AreEqual(InUseStateEnum.Booked, l_oState.Value);
            Assert.IsNull(l_oState.RemainingTime);
            Assert.AreEqual(new DateTime(2018, 1, 4, 17, 00, 0), l_oState.From);
            Assert.AreEqual("who@the", l_oState.MailAddress);
            Assert.AreEqual("323", l_oState.Code);
        }

        [Test]
        public void TestDoUpdate()
        {
            var l_oReservedAt = new DateTime(2017, 09, 20, 22, 01, 00);
            var l_oDateTimeMock = new DateTimeMocker(new List<DateTime> {
                l_oReservedAt,                              // Booking time 
                l_oReservedAt.Add(new TimeSpan(0, 4, 0)),    // Reserved for 4 mns
                l_oReservedAt.Add(new TimeSpan(0, 16, 0))   // Time elapsed since booking 16 mns
                });

            var l_oStateInfo = new StateInfoMutable(l_oDateTimeMock.GetDateTime);

            // Update state from Copri.
            l_oStateInfo.Load(
                InUseStateEnum.Reserved, // Copri acknowledges state reserved.
                l_oDateTimeMock.GetDateTime(), 
                "heiz@mustermann"); // Owner from Copri.
                
            // Invoke first update (after simulated 4mns)
            l_oStateInfo.UpdateOnTimeElapsed();
            Assert.AreEqual(InUseStateEnum.Reserved, l_oStateInfo.Value);
            Assert.AreEqual(11, l_oStateInfo.RemainingTime.Value.Minutes);
            Assert.AreEqual(new DateTime(2017, 09, 20, 22, 01, 00), l_oStateInfo.From.Value);
            Assert.AreEqual("heiz@mustermann", l_oStateInfo.MailAddress);
            Assert.IsNull(l_oStateInfo.Code);

            // Invoke second update (after simulated 16 mns)
            l_oStateInfo.UpdateOnTimeElapsed();
            Assert.AreEqual(InUseStateEnum.Disposable, l_oStateInfo.Value);
            Assert.IsNull(l_oStateInfo.RemainingTime);
            Assert.IsNull(l_oStateInfo.From);
            Assert.IsNull(l_oStateInfo.MailAddress);
            Assert.IsNull(l_oStateInfo.Code);
        }

        [Test]
        public void TestUpdateFromWebserver()
        {
            Func<DateTime> l_oNowMock = () => new DateTime(2017, 09, 21, 23, 40, 0);
            var l_oStateInfo = new StateInfoMutable(l_oNowMock);

            l_oStateInfo.Load(InUseStateEnum.Booked, new DateTime(2017, 09, 21, 23, 30, 0), "heiz@mustermann", "21");

            Assert.AreEqual(InUseStateEnum.Booked, l_oStateInfo.Value);
            Assert.AreEqual("Booked", l_oStateInfo.ToString());
            Assert.IsNull(l_oStateInfo.RemainingTime);
            Assert.AreEqual(new DateTime(2017, 09, 21, 23, 30, 0), l_oStateInfo.From.Value);
            Assert.AreEqual("heiz@mustermann", l_oStateInfo.MailAddress);
            Assert.AreEqual("21", l_oStateInfo.Code);

            DateTime FROM = new DateTime(2017, 09, 21, 23, 35, 0);
            l_oStateInfo.Load(InUseStateEnum.Reserved, FROM, "heiz@mustermann", "22");

            // Verify initial values of properties.
            Assert.AreEqual(InUseStateEnum.Reserved, l_oStateInfo.Value); 
            Assert.AreEqual("Reserved", l_oStateInfo.ToString());
            Assert.AreEqual(new TimeSpan(0, 15, 0).Subtract(l_oNowMock().Subtract(FROM)).Minutes, l_oStateInfo.RemainingTime.Value.Minutes);
            Assert.AreEqual(FROM, l_oStateInfo.From);
            Assert.AreEqual("heiz@mustermann", l_oStateInfo.MailAddress);
            Assert.AreEqual("22", l_oStateInfo.Code);

            l_oStateInfo.Load(InUseStateEnum.Disposable, new DateTime(1970, 1, 1, 0, 0, 0), "heiz@mustermann", "unused");

            // Verify initial values of properties.
            Assert.AreEqual(InUseStateEnum.Disposable, l_oStateInfo.Value);
            Assert.AreEqual("Disposable", l_oStateInfo.ToString());
            Assert.IsNull(l_oStateInfo.RemainingTime);
            Assert.IsNull(l_oStateInfo.From);
            Assert.IsNull( l_oStateInfo.MailAddress);
            Assert.IsNull(l_oStateInfo.Code);
        }

        [Test]
        public void TestLoad()
        {
            var l_oState = new StateInfoMutable(
                () => new DateTime(2018, 01, 03, 21, 14, 0)); // Now
            Assert.AreEqual(InUseStateEnum.Disposable, l_oState.Value);
            Assert.IsNull(l_oState.From);
            Assert.IsNull(l_oState.RemainingTime);
            Assert.IsNull(l_oState.MailAddress);
            Assert.IsNull(l_oState.Code);

            // Construct requested state object.
            var l_oSource = new StateInfo(
                () => new DateTime(2018, 01, 03, 21, 53, 0), 
                new DateTime(2018, 01, 03, 21, 13, 0), // Requested from
                "a@b", 
                "123");
            l_oState.Load(l_oSource);
            Assert.AreEqual(InUseStateEnum.Reserved, l_oState.Value);
            Assert.AreEqual(new DateTime(2018, 01, 03, 21, 13, 0), l_oState.From);
            Assert.AreEqual(14, l_oState.RemainingTime.Value.Minutes);
            Assert.AreEqual("a@b", l_oState.MailAddress);
            Assert.AreEqual("123", l_oState.Code);


            // Construct booked state object.
            l_oSource = new StateInfo(new DateTime(2018, 01, 03, 21, 37, 0), "a@b", "123");
            l_oState.Load(l_oSource);
            Assert.AreEqual(InUseStateEnum.Booked, l_oState.Value);
            Assert.AreEqual(new DateTime(2018, 01, 03, 21, 37, 0), l_oState.From);
            Assert.IsNull(l_oState.RemainingTime);
            Assert.AreEqual("a@b", l_oState.MailAddress);
            Assert.AreEqual("123", l_oState.Code);

            // Construct disposable object
            l_oSource = new StateInfo();
            l_oState.Load(l_oSource);
            Assert.AreEqual(InUseStateEnum.Disposable, l_oState.Value);
            Assert.IsNull(l_oState.From);
            Assert.IsNull(l_oState.RemainingTime);
            Assert.IsNull(l_oState.MailAddress);
            Assert.IsNull(l_oState.Code);
        }
    }
}