using FabAccessAPI;
using FabAccessAPI.Schema;
using NUnit.Framework;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace FabAccessAPI_Test.API_TestEnv
{
    [TestFixture]
    public class Machine_Test
    {
        #region SetUp
        [SetUp]
        public async Task SetUp()
        {
            API api = new API();
            ConnectionData connectionData = TestEnv.CreateConnetionData("Admin1");
            await api.Connect(connectionData);

            IReadOnlyList<Machine> machine_list = await api.Session.MachineSystem.Info.GetMachineList().ConfigureAwait(false);

            List<Task> tasks = new List<Task>();
            foreach (Machine m in machine_list)
            {
                tasks.Add(m.Manage.ForceFree());
            }

            await Task.WhenAll(tasks);
        }
        #endregion

        [TestCase("Admin1", "MachineA1")]
        [TestCase("ManagerA1", "MachineA1")]
        [TestCase("MakerA1", "MachineA1")]
        [Order(1)]
        public async Task UseGiveBack(string username, string machineID)
        {
            API api = new API();
            ConnectionData connectionData = TestEnv.CreateConnetionData(username);
            await api.Connect(connectionData);

            Machine machine = (await api.Session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false)).Just;

            // Check State before run Test
            if (machine.State != Machine.MachineState.free)
            {
                await api.Disconnect();
                Assert.Inconclusive("State is not 'free'");
            }

            await machine.Use.Use().ConfigureAwait(false);

            machine = (await api.Session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false)).Just;
            await machine.Inuse.GiveBack().ConfigureAwait(false);

            machine = (await api.Session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false)).Just;

            await api.Disconnect();

            Assert.AreEqual(Machine.MachineState.free, machine.State);
        }

        [TestCase("ManagerA1", "MakerA1", "MachineA1")]
        [TestCase("MakerA1", "Admin1", "MachineA1")]
        [TestCase("ManagerA1", "GuestA1", "MachineA1")]
        [Order(2), Ignore("Not Implemented")]
        public async Task TransferMachine(string username1, string username2, string machineID)
        {
            API api1 = new API();
            ConnectionData connectionData1 = TestEnv.CreateConnetionData(username1);
            await api1.Connect(connectionData1);

            API api2 = new API();
            ConnectionData connectionData2 = TestEnv.CreateConnetionData(username2);
            await api2.Connect(connectionData2);

            Machine machine1 = (await api1.Session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false)).Just;

            // Check State before run Test
            if (machine1.State != Machine.MachineState.free)
            {
                await api1.Disconnect();
                await api2.Disconnect();
                Assert.Inconclusive("State is not 'free'");
            }

            await machine1.Use.Use().ConfigureAwait(false);
            machine1 = (await api1.Session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false)).Just;
            await machine1.Inuse.Releasefortakeover().ConfigureAwait(false);

            Machine machine2 = (await api2.Session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false)).Just;
            await machine2.Takeover.Accept().ConfigureAwait(false);

            machine2 = (await api2.Session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false)).Just;
            await machine2.Inuse.GiveBack().ConfigureAwait(false);

            machine1 = (await api1.Session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false)).Just;

            await api1.Disconnect();
            await api2.Disconnect();

            Assert.AreEqual(Machine.MachineState.free, machine1.State);
        }

        [TestCase("ManagerA1", "MakerA1", "MachineA1")]
        [TestCase("MakerA1", "Admin1", "MachineA1")]
        [TestCase("ManagerA1", "GuestA1", "MachineA1")]
        [Order(3), Ignore("Not Implemented")]
        public async Task TransferMachine_Reject(string username1, string username2, string machineID)
        {
            API api1 = new API();
            ConnectionData connectionData1 = TestEnv.CreateConnetionData(username1);
            await api1.Connect(connectionData1);

            API api2 = new API();
            ConnectionData connectionData2 = TestEnv.CreateConnetionData(username2);
            await api2.Connect(connectionData2);

            Machine machine1 = (await api1.Session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false)).Just;

            // Check State before run Test
            if (machine1.State != Machine.MachineState.free)
            {
                await api1.Disconnect();
                await api2.Disconnect();
                Assert.Inconclusive("State is not 'free'");
            }

            await machine1.Use.Use().ConfigureAwait(false);
            machine1 = (await api1.Session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false)).Just;
            await machine1.Inuse.Releasefortakeover().ConfigureAwait(false);

            Machine machine2 = (await api2.Session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false)).Just;
            await machine2.Takeover.Reject().ConfigureAwait(false);

            machine1 = (await api1.Session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false)).Just;
            await machine1.Inuse.GiveBack().ConfigureAwait(false);

            machine2 = (await api1.Session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false)).Just;

            await api1.Disconnect();
            await api2.Disconnect();

            Assert.AreEqual(Machine.MachineState.free, machine2.State);
        }

        [TestCase("ManagerA1", "ManagerA1", "MachineA1")]
        [TestCase("ManagerA1", "Admin1", "MachineA1")]
        [TestCase("MakerA1", "Admin1", "MachineA1")]
        [Order(4), Ignore("Not Implemented")]
        public async Task CheckMachine(string username1, string username2, string machineID)
        {
            API api1 = new API();
            ConnectionData connectionData1 = TestEnv.CreateConnetionData(username1);
            await api1.Connect(connectionData1);

            API api2 = new API();
            ConnectionData connectionData2 = TestEnv.CreateConnetionData(username2);
            await api2.Connect(connectionData2);

            Machine machine1 = (await api1.Session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false)).Just;

            // Check State before run Test
            if (machine1.State != Machine.MachineState.free)
            {
                await api1.Disconnect();
                await api2.Disconnect();
                Assert.Inconclusive("State is not 'free'");
            }

            await machine1.Use.Use().ConfigureAwait(false);
            machine1 = (await api1.Session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false)).Just;
            await machine1.Inuse.GiveBack().ConfigureAwait(false);

            Machine machine2 = (await api2.Session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false)).Just;
            await machine2.Check.Check().ConfigureAwait(false);

            machine1 = (await api1.Session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false)).Just;

            await api1.Disconnect();
            await api2.Disconnect();

            Assert.AreEqual(Machine.MachineState.free, machine1.State);
        }

        [TestCase("ManagerA1", "ManagerA1", "MachineA1")]
        [TestCase("ManagerA1", "Admin1", "MachineA1")]
        [TestCase("MakerA1", "Admin1", "MachineA1")]
        [Order(5), Ignore("Not Implemented")]
        public async Task CheckMachine_Reject(string username1, string username2, string machineID)
        {
            API api1 = new API();
            ConnectionData connectionData1 = TestEnv.CreateConnetionData(username1);
            await api1.Connect(connectionData1);

            API api2 = new API();
            ConnectionData connectionData2 = TestEnv.CreateConnetionData(username2);
            await api2.Connect(connectionData2);


            Machine machine1 = (await api1.Session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false)).Just;

            // Check State before run Test
            if (machine1.State != Machine.MachineState.free)
            {
                await api1.Disconnect();
                await api2.Disconnect();
                Assert.Inconclusive("State is not 'free'");
            }

            await machine1.Use.Use().ConfigureAwait(false);
            machine1 = (await api1.Session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false)).Just;
            await machine1.Inuse.GiveBack().ConfigureAwait(false);

            Machine machine2 = (await api2.Session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false)).Just;
            await machine2.Check.Reject().ConfigureAwait(false);

            machine1 = (await api1.Session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false)).Just;
            await machine1.Inuse.GiveBack().ConfigureAwait(false);

            machine2 = (await api2.Session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false)).Just;
            await machine2.Check.Check().ConfigureAwait(false);

            machine1 = (await api1.Session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false)).Just;

            await api1.Disconnect();
            await api2.Disconnect();

            Assert.AreEqual(Machine.MachineState.free, machine1.State);
        }

        [TestCase("MakerA1", "GuestA1", "ManagerA1", "MachineA1")]
        [Order(4), Ignore("Not Implemented")]
        public async Task CheckMachine_NoPermission(string username1, string username2, string username3, string machineID)
        {
            API api1 = new API();
            ConnectionData connectionData1 = TestEnv.CreateConnetionData(username1);
            await api1.Connect(connectionData1);

            API api2 = new API();
            ConnectionData connectionData2 = TestEnv.CreateConnetionData(username2);
            await api2.Connect(connectionData2);

            API api3 = new API();
            ConnectionData connectionData3 = TestEnv.CreateConnetionData(username3);
            await api3.Connect(connectionData3);

            Machine machine1 = (await api1.Session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false)).Just;

            // Check State before run Test
            if (machine1.State != Machine.MachineState.free)
            {
                await api1.Disconnect();
                await api2.Disconnect();
                await api3.Disconnect();
                Assert.Inconclusive("State is not 'free'");
            }

            await machine1.Use.Use().ConfigureAwait(false);
            machine1 = (await api1.Session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false)).Just;
            await machine1.Inuse.GiveBack().ConfigureAwait(false);

            Machine machine2 = (await api2.Session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false)).Just;
            bool result = ((Machine.CheckInterface_Proxy)machine2.Check).IsNull;

            Machine machine3 = (await api3.Session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false)).Just;
            await machine3.Check.Check().ConfigureAwait(false);

            await api1.Disconnect();
            await api2.Disconnect();
            await api3.Disconnect();

            Assert.IsTrue(result);
        }

        [TestCase("ManagerA1", "MachineA1")]
        [Order(5)]
        public async Task CurrentUser(string username, string machineID)
        {
            API api = new API();
            ConnectionData connectionData = TestEnv.CreateConnetionData(username);
            await api.Connect(connectionData);

            Machine machine = (await api.Session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false)).Just;

            // Check State before run Test
            if (machine.State != Machine.MachineState.free)
            {
                await api.Disconnect();
                Assert.Inconclusive("State is not 'free'");
            }

            Machine.MachineInfoExtended machineInfoExtended = await machine.Manage.GetMachineInfoExtended().ConfigureAwait(false);

            await api.Disconnect();

            Assert.IsNull(machineInfoExtended.CurrentUser.Just);
        }
    }
}