using Capnp.Rpc;
using FabAccessAPI;
using FabAccessAPI.Exceptions;
using FabAccessAPI.Exceptions.SASL;
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace FabAccessAPI_Test
{
    public class API_Test
    {
        [Test]
        public async Task Connect_HostUnreachable()
        {
            API api = new API();

            ConnectionData connectionData = new ConnectionData()
            {
                Host = new UriBuilder(TestEnv.SCHEMA, "UnkownHost" + TestEnv.TESTSERVER, TestEnv.TESTSERVER_PORT).Uri,
                Mechanism = SASLMechanismEnum.PLAIN,
                Username = "UnkownUser",
                Properties = new Dictionary<string, object>()
                {
                    { "Username", "UnkownUser" },
                    { "Password", TestEnv.PASSWORD }
                }
            };

            try
            {
                await api.Connect(connectionData);
            }
            catch (ConnectionException)
            {
                Assert.Pass();
            }
            Assert.Fail();
        }

        [Test]
        public async Task Connect_InvalidCredentials()
        {
            API api = new API();

            ConnectionData connectionData = TestEnv.CreateConnetionData("UnkownUser");

            try
            {
                await api.Connect(connectionData);
            }
            catch(AuthenticationException exception) when (exception.InnerException is InvalidCredentialsException)
            {
                Assert.Pass();
            }
            Assert.Fail();
        }

        [TestCase("Admin1")]
        public async Task ConnectDisconnect(string username)
        {
            API api = new API();

            ConnectionData connectionData = TestEnv.CreateConnetionData(username);

            bool event_Connected = false;
            bool event_Disconnected = false;
            api.ConnectionStatusChanged += (sender, eventArgs) =>
            {
                if (eventArgs == ConnectionStatusChanged.Connected)
                {
                    event_Connected = true;
                }
                if(eventArgs == ConnectionStatusChanged.Disconnected)
                {
                    event_Disconnected = true;
                }
            };

            await api.Connect(connectionData);

            bool HasSesion = api.Session != null;
            bool HasConnectionData = api.ConnectionData != null;
            bool HasServerData = api.ServerData != null;
            bool IsConnected = api.IsConnected;

            await api.Disconnect();

            Thread.Sleep(3000);
            Assert.Multiple(() =>
            {
                Assert.IsTrue(event_Connected, "event_Connected");
                Assert.IsTrue(event_Disconnected, "event_Disconnected");

                Assert.IsTrue(HasSesion, "HasSesion");
                Assert.IsTrue(HasConnectionData, "HasConnectionData");
                Assert.IsTrue(HasServerData, "HasServerData");
                Assert.IsTrue(IsConnected, "IsConnected");

                Assert.IsFalse(api.IsConnected, "api.IsConnected");
            });
        }

        [TestCase("Admin1")]
        public async Task TestConnectíon(string username)
        {
            API api = new API();

            ConnectionData connectionData = TestEnv.CreateConnetionData(username);

            ServerData serverData = await api.TryToConnect(connectionData);

            Assert.IsNotNull(serverData);
        }

        [TestCase("Admin1"), Explicit]
        public async Task Reconnect(string username)
        {
            API api = new API();

            ConnectionData connectionData = TestEnv.CreateConnetionData(username);

            int event_Connected = 0;
            int event_ConnectionLoss = 0;
            int event_Disconnected = 0;

            api.ConnectionStatusChanged += (sender, eventArgs) =>
            {
                if (eventArgs == ConnectionStatusChanged.Connected)
                {
                    event_Connected++;
                }
                if (eventArgs == ConnectionStatusChanged.ConnectionLoss)
                {
                    event_ConnectionLoss++;
                }
                if (eventArgs == ConnectionStatusChanged.Disconnected)
                {
                    event_Disconnected++;
                }
            };

            TcpRpcClient tcpRpcClient = new TcpRpcClient();
            await api.Connect(connectionData, tcpRpcClient);

            try
            {
                // Stop here and cut internet connection
                await api.Session.MachineSystem.Info.GetMachineList().ConfigureAwait(false);
            }
            catch
            {

            }
            Thread.Sleep(3000);

            // Stop here and connect with internet again
            await api.Disconnect();

            Thread.Sleep(1000);
            Assert.Multiple(() =>
            {
                Assert.AreEqual(2, event_Connected, "event_Connected");
                Assert.AreEqual(1, event_ConnectionLoss, "event_ConnectionLoss");
                Assert.AreEqual(1, event_Disconnected, "event_Disconnected");
            });
        }


    }
}