using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Vinno.IUS.Common.Log;
using Vinno.IUS.Common.Network;
using Vinno.IUS.Common.Network.Leaf;
using Vinno.IUS.Common.Network.Tcp;
using Vinno.IUS.Common.Network.Transfer;
using Vinno.vCloud.Client.Proxy.Interfaces;
using Vinno.vCloud.Common.Client.Managers.Interfaces;
using Vinno.vCloud.Common.Client.Managers.vCloudClient;
using Vinno.vCloud.Common.Storage;
using Vinno.vCloud.Common.Storage.ObjectStorageInfo;
using Vinno.vCloud.Protocol.Infrastructures;
using Vinno.vCloud.Protocol.Initializers;
using Vinno.vCloud.Protocol.Messages;
using Vinno.vCloud.Protocol.Messages.Client.Account;
using Vinno.vCloud.Protocol.Messages.Client.LiveTalking;
using Vinno.vCloud.Protocol.Messages.Client.Login;
using Vinno.vCloud.Protocol.Messages.Client.Storage;
using Vinno.vCloud.Protocol.Messages.Live;
using Vinno.vCloud.Protocol.Messages.Login;

namespace Vinno.vCloud.Client.Proxy
{
    public partial class ClientManager 
    {
        private const int HeartRateTime = 1; //minute
        private ConnectionKeeper _connectionKeeper;
        private HeartRateKeeper _heartRateKeeper;
        private ITcpCreator _tcpCreator;
        private LoginSource _loginSource;
        private string _sessionId;
        private object _clientLoker = new object();
        private ClientLeaf _leaf;
        private string _password;
        private string _accountName;
        private string _accountId;
        private string _liveServiceUrl;
        private ClientAccountMessage _accountDataMessage;
        private string _currentUniqueCode = string.Empty;
        private string _serverAddress;
        private int _port;
        private string _clinetId;
        private string _openId;


        /// <summary>
        /// login account Id
        /// </summary>
        public string AccountId => _accountId;

        public string AccountName=> _accountName;
        /// <summary>
        /// Gets or set the leaf of this client manager.
        /// </summary>
        public ClientLeaf Leaf
        {
            get { return _leaf; }
            set
            {
                if (_leaf != value)
                {
                    if (_leaf != null)
                    {
                        _heartRateKeeper?.Stop();
                        if (_connectionKeeper != null)
                        {
                            _connectionKeeper.Offlined -= OnOfflined;
                            _connectionKeeper.Stop();
                        }
                        _leaf.MessageArrived -= OnMessageArrived;
                        _leaf.Close();
                       
                    }
                    _leaf = value;

                    if (_leaf != null)
                    {
                        _leaf.MessageArrived += OnMessageArrived;
                        _connectionKeeper = new ConnectionKeeper(Leaf);
                        _connectionKeeper.Offlined += OnOfflined;
                        _connectionKeeper.Start();
                    }
                }
            }
        }

        /// <summary>
        /// Network connection is offlined then relogining, trigger by connection keeper
        /// </summary>
        public event EventHandler AccountRelogining;

        /// <summary>
        /// The event that account relogin
        /// </summary>
        public event EventHandler AccountRelogin;

        /// <summary>
        /// The event befor connection replaced
        /// </summary>
        public event EventHandler ConnectionReplacing;

        /// <summary>
        /// The event connection replaced, it may account login in another client
        /// </summary>
        public event EventHandler ConnectionReplaced;

        /// <summary>
        /// The event account password changed in another client
        /// </summary>
        public event EventHandler<string> AccountPasswordChanged;

        /// <summary>
        /// execute when visitor relogin fail
        /// </summary>
        public event EventHandler<LoginStatus> VisitorReloginFail;

        /// <summary>
        /// execute when scan login success
        /// </summary>
        public event EventHandler<bool> ScanLoginState;

        /// <summary>
        /// Flag indicates client is connected to vCloud server
        /// </summary>
        public bool IsConnected => Leaf != null && Leaf.Online;

        public event EventHandler<bool> ConnectedChanged;

        /// <summary>
        /// Get the upgrader which providers the upgrade functions.
        /// </summary>
        public IUpgrader Upgrader { get; set; }

        public IRemedical Remedical { get; }

        public IRemoteDiagnosis RemoteDiagnosis { get; }

        /// <summary>
        /// This property provide all community api communicate to server.
        /// </summary>
        public ICommunity Community { get; }

        /// <summary>
        /// Provide all chat message api communicate to server.
        /// </summary>
        public IMessageCenter MessageCenter { get; }

        /// <summary>
        /// Provide all account api communicate to server.
        /// </summary>
        public IAccount Account { get; }

        /// <summary>
        /// provide all report api
        /// </summary>
        public IReport Report { get; }
        
        /// <summary>
        /// provide all Share api
        /// </summary>
        public IShare Share { get; }

        /// <summary>
        /// provide all live api
        /// </summary>
        public ILive Live { get; }

        /// <summary>
        /// WorkOrder System
        /// </summary>
        public IWorkOrderSystem WorkOrderSystem { get; }

        /// <summary>
        /// provide all training api
        /// </summary>
        public ITraining Training { get; }

        public IDataStatistics DataStatistics { get; }
        /// <summary>
        /// Assign terminal
        /// </summary>
        public IAssignTerminal AssignTerminal { get; }

        public ILiveTalking LiveTalking { get; }

        public IAfterSales AfterSales { get; }

        public ICarotid Carotid { get; }

        /// <summary>
        /// provide all teaching api
        /// </summary>
        public ITeaching Teaching { get; }

        /// <summary>
        /// Provide all AI diagnosis requests.
        /// </summary>
        public IAIDiagnosis AIDiagnosis { get; }

        public IFrontPage FrontPage { get; }
        public ILiveWatcher LiveWatcher { get; private set; }

        public event EventHandler<Message> MessageArrived;

        public ClientManager(string hostUrl)
        {
            var address = hostUrl.Split(':');
            _serverAddress = address[0];
            _port = int.Parse(address[1]);
            _tcpCreator = new TcpCreator(_serverAddress, _port);
                         
            Leaf = new ClientLeaf(new LeafIdContext(), LeafMode.Dual, _tcpCreator);
          
            Leaf.RegisterSetAccountDataMessageFunc(SetAccountDataToMessage);
            UploadHelper.GetAuthorization = GetAuthentication;
        }             

        private void OnOfflined(object sender, EventArgs eventArgs)
        {
            lock (_clientLoker)
            {
                try
                {
                    if (Leaf == null) //Account had log off, do not need to reconnect
                    {
                        return;
                    }
                    Logger.WriteLineInfo("OnOfflined Enter");

                    _heartRateKeeper?.Stop();
                    _heartRateKeeper = null;
                    OnAccountRelogining();

                    TryResetClientLeaf();
                    if (Leaf.Online)
                    {

                        if (LoginByOpenId(_openId, _accountName,_password) == LoginStatus.Success)
                        {
                            OnAccountRelogin();
                        }
                    }
                }
                catch (Exception e)
                {
                    Logger.WriteLineError($"OnOfflined{e}");
                }
            }
        }


        public event EventHandler LoginChanged;

        private void OnMessageArrived(object sender, Message e)
        {
            MessageArrived?.Invoke(this, e);
        }

        public void ResetClientLeafWithLock()
        {
            lock (_clientLoker)
            {
                TryResetClientLeaf();
            }
        }

        /// <summary>
        /// Try set ClientLeaf if is online
        /// </summary>
        private void TryResetClientLeaf()
        {           
            _tcpCreator = new TcpCreator(_serverAddress, _port);           
            if (!_tcpCreator.HasValidIpAddress)
            {
                return;
            }
            var clientLeaf = new ClientLeaf(new LeafIdContext(), LeafMode.Dual, _tcpCreator);
            if (clientLeaf.Online)
            {
                Leaf = clientLeaf;
                Leaf.RegisterSetAccountDataMessageFunc(SetAccountDataToMessage);
            }
            else
            {
                clientLeaf.Close();
            }
            Logger.WriteLineInfo($"TryResetClientLeaf- new ClientLeaf end");
        }

        /// <summary>
        /// Login to vCloud server
        /// </summary>
        /// <param name="accountName">Account name (Unique)</param>
        /// <param name="password">Password(encrypted) </param>
        /// <param name="loginSource">Login Source</param>                
        /// <returns></returns>
        public LoginStatus Login(string accountName, string password, LoginSource loginSource, string clientId, bool isOffine = false)
        {
            lock (_clientLoker)
            {
                _loginSource = loginSource;
                _accountName = accountName;
                _password = password;
                _clinetId = clientId;
                if (IsConnected)
                {                                                                                              
                    var loginRequest = new ClientLoginRequest2
                    {
                        Name = accountName,
                        Password = password,
                        Source = loginSource,
                        ClientId = clientId,
                        ClientVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString(),
                        Force = true
                    };
                    try
                    {
                        var result = Leaf.Send(loginRequest);
                        var loginResult = LoginResult2.Convert(result);
                        if (loginResult != null)
                        {
                            _sessionId = loginResult.AccountSessionId;
                            _accountId = loginResult.AccountId;
                            var status = loginResult.Status;
                            if (status == LoginStatus.Success)
                            {
                                Logger.WriteLineInfo($"LoginRequest Success - account:{accountName}");
                                if (Leaf.Mode == LeafMode.Dual)
                                {
                                    _heartRateKeeper = new HeartRateKeeper(_sessionId, Leaf, HeartRateTime);
                                    _heartRateKeeper.Start();
                                }                                
                            }
                             
                            return loginResult.Status;
                        }
                    }
                    catch (ConnectionTimeoutException)
                    {
                        return LoginStatus.ConnectionTmeout;
                    }
                    catch (NotConnectedException)
                    {
                        return LoginStatus.ConnectServerFailed;
                    }
                    catch (Exception e)
                    {
                        Logger.WriteLineError($"Login ex:{e}");
                    }

                }
                ConnectedChanged?.Invoke(this, false);
                return LoginStatus.Unknown;
            }
        }

        public LoginStatus LoginByOpenId(string openId, string username="", string password = ""  )
        {
            lock (_clientLoker)
            {
                try
                {
                    _openId = openId;
                    _loginSource = LoginSource.ClientProxy;
                    if (!string.IsNullOrEmpty(username))
                    {
                        _accountName = username;
                    }
                    if (!string.IsNullOrEmpty(password))
                    {
                        _password = password;
                    }

                    if (string.IsNullOrEmpty(_clinetId))
                    {
                        _clinetId = Guid.NewGuid().ToString();
                    }

                    if (IsConnected)
                    {
                        using (var request = MessagePool.GetMessage<LoginByOpenIdRequest>())
                        {
                            request.OpenId = openId;
                            request.Source = _loginSource;
                            request.ClientId = _clinetId;
                            request.ClientVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString();
                            request.Name = username;
                            request.Password = password;
                            request.Type = LoginType.User;
                            var result = Leaf.Send(request);
                            var loginResult = LoginResult2.Convert(result);
                            if (loginResult != null)
                            {
                                _sessionId = loginResult.AccountSessionId;
                                _accountId = loginResult.AccountId;
                                var status = loginResult.Status;
                                if (status == LoginStatus.Success)
                                {
                                    Logger.WriteLineInfo($"LoginRequest Success -openId:{openId} , account:{_accountId}");
                                    if (Leaf.Mode == LeafMode.Dual)
                                    {
                                        _heartRateKeeper = new HeartRateKeeper(_sessionId, Leaf, HeartRateTime);
                                        _heartRateKeeper.Start();
                                    }
                                }

                                return loginResult.Status;
                            }
                        }
                    }

                }
                catch (ConnectionTimeoutException)
                {
                    return LoginStatus.ConnectionTmeout;
                }
                catch (NotConnectedException)
                {
                    return LoginStatus.ConnectServerFailed;
                }
                catch (Exception e)
                {
                    Logger.WriteLineError($"Login ex:{e}");
                }
                ConnectedChanged?.Invoke(this, false);
                return LoginStatus.Unknown;
            }
        }


        internal Task<Message> SendAsync(Message request)
        {
            return Leaf.SendAsync(request);
        }

        /// <summary>
        /// Logout from vCloud server async method
        /// </summary>       
        /// <returns>Logoff status</returns>
        public async Task<LogoffStatus> LogoutAsync()
        {
            LogoffStatus logoffStatus = await Task.Run(() =>
            {
                lock (_clientLoker)
                {
                    try
                    {
                       
                        var logoffRequest = new ClientLogoffRequest
                        {
                            Name = _accountName,
                            Password = _password,
                            Source = _loginSource
                        };
                        var result = Leaf.Send(logoffRequest);
                        LogoffResult logoffResult = LogoffResult.Convert(result);
                        if (logoffResult != null)
                        {
                            return logoffResult.Status;
                        }
                    }
                    catch (Exception exception)
                    {
                        Logger.WriteLineError($"LogoutAsync ex {exception}");
                    }
                    finally
                    {
                        try
                        {
                            Leaf = null;
                        }
                        catch (Exception ex)
                        {
                            Logger.WriteLineError($"KeepConnection failed, ex: {ex}");
                        }
                    }
                    return LogoffStatus.Unknown;
                }
            });
            return logoffStatus;

        }

        public void Dispose()
        {
            lock (_clientLoker)
            {
                Leaf = null;
            }
        }

        protected virtual void OnAccountRelogining()
        {
            AccountRelogining?.Invoke(this, EventArgs.Empty);
        }

        protected virtual void OnAccountRelogin()
        {          
            AccountRelogin?.Invoke(this, EventArgs.Empty);
        }

        public void StartBackgroundKeeper()
        {
            _connectionKeeper?.Stop();
            _heartRateKeeper?.Stop();

            _connectionKeeper?.Start();
            _heartRateKeeper?.Start();
        }

        private Message SetAccountDataToMessage(Message message)
        {
            if (message is ClientRequestMessage clientRequestMessage)
            {
                if (clientRequestMessage != null)
                {
                    clientRequestMessage.AccountData = GetAccountDataMessage() as ClientAccountMessage;
                }
            }
            else if (message is DictionaryMessage dictionaryMessage)
            {
                var accountMessage = GetAccountDataMessage() as ClientAccountMessage;
                if (accountMessage != null && dictionaryMessage != null && string.IsNullOrWhiteSpace(dictionaryMessage.GetAccountId()))
                {
                    dictionaryMessage.SetAccountBaseId(accountMessage.AccountId);
                    var accountIdValue = dictionaryMessage.GetAccountId();
                   
                }
            }
            return message;
        }

        private Message GetAccountDataMessage()
        {
            var accountData = new ClientAccountMessage
            {
                AccountId = _accountId ?? string.Empty,
                AccountName = _accountName ?? string.Empty,
                SessionId = _sessionId ?? string.Empty,
                Source = _loginSource
            };
            return accountData;
        }

        private string GetAuthentication(string fileName)
        {
            try
            {
                var authorization = string.Empty;
                var getAuthorizationRequest = new GetAuthorizationRequest();
                getAuthorizationRequest.FileName = fileName;
                var storageResult = Leaf.Send(getAuthorizationRequest);
                var getAuthorizationResult = GetAuthorizationResult.Convert(storageResult);
                if (getAuthorizationResult != null)
                {
                    authorization = getAuthorizationResult.Authorization;
                }
                return authorization;
            }
            catch (Exception e)
            {
                throw new Exception("get authentication form server error");
            }
        }

        public string GetLiveServiceUrl()
        {
            if (string.IsNullOrEmpty(_liveServiceUrl))
            {
                using (var request = MessagePool.GetMessage<GetLiveServiceUrlRequest>())
                {
                    var result = Leaf.Send(request);
                    if (result != null)
                    {
                        var resultMessage = GetLiveServiceUrlResult.Convert(result);
                        if (resultMessage != null)
                        {
                            _liveServiceUrl = resultMessage.Url;
                        }
                    }
                }
            }
            return _liveServiceUrl;
        }



       
    }

}