using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Telepathy;
using PhilExample.Interfaces;
using PhilExampleCrawler.Common.Models;
using PhilExampleCrawler.Common.TCP.Enums;
using PhilExampleCrawler.Common.TCP.Packets;
using PhilExampleCrawler.Common.TCP.Utils;
using Xamarin.Forms;

namespace PhilExample.Services
{
    internal class TCPClient : ITCPClient, IForegroundTCPDelegate
    {
        private readonly Client _c = new Client();
        private readonly IForegroundTCPService _fgService = DependencyService.Get<IForegroundTCPService>();

        public bool IsConnected => _c != null
                                && _c.Connected;

        private bool _isUserRegistered = false;
        private DateTime? _registeredUntil = null;
        public bool IsUserRegistered => _isUserRegistered;
        public DateTime? RegisteredUntil => _registeredUntil;

        public event EventHandler<int> OnUserConnected;
        public event EventHandler<(int connectionID, BasePacket bp)> OnDataReceived;
        public event EventHandler<int> OnUserDisconnected;

        CancellationTokenSource NewCTS() => new CancellationTokenSource(TimeSpan.FromSeconds(10));

        public void ReceiveLoop()
        {
            while (_c.Connecting) ;

            while (IsConnected)
                while(_c.GetNextMessage(out Message msg))
                switch (msg.eventType)
                {
                    case EventType.Connected:
                        OnUserConnected?.Invoke(this, msg.connectionId);
                        break;
                    case EventType.Data:
                        OnDataReceived?.Invoke(this, (msg.connectionId, msg.data.Deserialize()));
                        break;
                    case EventType.Disconnected:
                        OnUserDisconnected?.Invoke(this, msg.connectionId);
                        break;
                }
        }

        public bool ConnectToServer(string ip, int port)
        {
            if (IsConnected && _fgService.IsRunning)
                return true;

            try
            {
                var cts = NewCTS();
                _c.Connect(ip, port);
                _fgService.Start(this);
                while (!IsConnected || cts.IsCancellationRequested) ;
                return IsConnected;
            }
            catch (Exception ex)
            {
                //TODO: LOG
                return false;
            }
        }

        /// <summary> Closes Connection to TCPAPI and stops Foreground Service </summary>
        public void Disconnect()
        {
            _c.Disconnect();
            _fgService.Stop();
        }

        public void OnForegroundStop()
        {
            //TODO: Restart logic if possible
            Console.WriteLine("TCPClient.OnForegroundStop() was called. TODO: implement Restart logic");
        }

        public bool RegisterForUpdates(User user)
        {
            if (!IsConnected)
                return false;

            var bp = new BasePacket(new RegisterUserSessionLoad() { AuthCode = user.AuthCode });
            var bpSerialized = bp.Serialize();

            try
            {
                OnDataReceived += TCPClient_OnDataReceived;

                var cts = NewCTS();
                if (!_c.Send(bpSerialized))
                    return false;

                while (!_isUserRegistered && !cts.IsCancellationRequested) ;
                return _isUserRegistered;
            }
            finally
            {
                OnDataReceived -= TCPClient_OnDataReceived;
            }
        }

        //TODO: Implement
        //Could be used if user wants to pause crawl sessions.
        //Could be used explicitly when disconnecting per buttonclick from user,
        //so server knows exactly that user doesnt need to be sent insertions anymore
        //Otherwise user might linger in TCPAPI until "ValidTo" from UserSession is gone.
        //and some logic might still crawl for suspendedUsers so they can get updated for the time they were suspended
        //if they just lost TCP Connection for a min (train ride etc)
        public void Unregister(User user)
        {
            throw new NotImplementedException();
        }





        private void TCPClient_OnDataReceived(object sender, (int connectionID, BasePacket bp) e)
        {
            //Essential for .RegisterForUpdates(..) to work
            if (e.bp.LoadData.Type == LoadType.RegisterUserSessionOKLoad && e.bp.LoadData is RegisterUserSessionOKLoad regOKLoad)
            {
                _registeredUntil = regOKLoad.ValidUntil;
                _isUserRegistered = true;
            }
        }


        //TODO: Remove this from ForegroundDelegate, put this into seperate Interface?

        #region delegate
        public string GetForegroundNotificationTitle
            => "TODO 1 aktive Suche";//_activeCrawlers.Count == 1 ? "1 aktive Suche"
                                     //                          : _activeCrawlers.Count + " aktive Suchen";
        public string GetForegroundNotificationDescription
            => "TODO suchparams"; //string.Join(Environment.NewLine, _activeCrawlers.Select(x => ToNotificationString(x.CrawlSearchParams)));

        #endregion
    }
}
