using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Threading;
using System.Threading.Tasks;
using Aitex.Common.Util;
using Aitex.Core.Account;
using Aitex.Core.RT.Event;
using Aitex.Core.RT.Log;
using Aitex.Core.RT.SCCore;
using Aitex.Core.Util;
using Aitex.Core.WCF;
using MECF.Framework.Common.Account.Extends;
using MECF.Framework.Common.Equipment;

namespace MECF.Framework.Common.Account
{
	/// <summary>
	/// 账号管理器。
	/// </summary>
	public class AccountExManager : Singleton<AccountExManager>
	{
		#region Variables

		private readonly string _scAccountTemplateFile = PathManager.GetCfgDir() + "Account//Account.xml";
		private readonly string _scAccountLocalFile = PathManager.GetCfgDir() + "Account//_AccountEx.xml";

		// 旧的_Account.xml文件路径，为兼容已有的账户配置。
		private readonly string _oldAccountXmlFile = PathManager.GetCfgDir() + "Account//_Account.xml";

		private readonly SemaphoreSlim _syncRoot = new SemaphoreSlim(1, 1);

		/// <summary>
		/// 是否启用单会话登录模式。
		/// </summary>
		private bool _enableSingleSessionMode;

		#endregion

		#region Constructors

		public AccountExManager()
		{

		}

		#endregion

		#region Properties

		/// <summary>
		/// 返回角色加载器。
		/// </summary>
		public RoleAccountLoader RoleAccountLoader { get; private set; }

		#endregion

		#region Methods

		/// <summary>
		/// 初始化当前对象。
		/// </summary>
		/// <param name="enableService"></param>
		/// <exception cref="ApplicationException"></exception>
		public void Initialize(bool enableService)
        {
            _enableSingleSessionMode = SC.SafeGetValue("System.SingleSessionLoginMode", false);


            if (!File.Exists(_scAccountLocalFile))
			{
				if (File.Exists(_oldAccountXmlFile))
				{
					// 如果已存在_Account.xml，则从_Account.xml创建_AccountEx.xml文件。
					File.Copy(_oldAccountXmlFile, _scAccountLocalFile);
					Thread.Sleep(10);
				}
				else if (File.Exists(_scAccountTemplateFile))
				{
					File.Copy(_scAccountTemplateFile, _scAccountLocalFile);
					Thread.Sleep(10);
				}
                else
                {
                    // 模板文件不存在
                    throw new ApplicationException("Can not initialize account configuration file, " +
                                                   _scAccountTemplateFile);
                }
            }

			// 默认不支持多用户登录。
            CredentialManager.Instance.Initialize(!_enableSingleSessionMode);

			RoleAccountLoader = new RoleAccountLoader(_scAccountLocalFile);
			RoleAccountLoader.Load();

			if (enableService)
			{
				Singleton<WcfServiceManager>.Instance.Initialize(new Type[1] { typeof(AccountService) });
			}
		}

		/// <summary>
		/// 获取WCF客户端IP信息。
		/// </summary>
		/// <returns></returns>
		private static RemoteEndpointMessageProperty GetCurrentWCFClientEndPoint()
		{
			var context = OperationContext.Current;
			var prop = context.IncomingMessageProperties;
			var endpoint =
				prop[RemoteEndpointMessageProperty.Name] as RemoteEndpointMessageProperty;
			return endpoint;
		}

		/// <summary>
		/// 等待已登录的用户确认是否允许新的登录请求。
		/// </summary>
		/// <param name="userLoggedIn">已登录用户的凭据。</param>
		/// <param name="userRequesting">新用户的登录凭据。</param>
		/// <returns></returns>
		private static async Task<LoginRequestResults> RequestAndWaitLoginConfirmation(Credential userLoggedIn,
			Credential userRequesting)
		{
			// 等待已登录用户响应，返回结果；
			// 1. Rejected，拒绝新的登录请求
			// 2. Logged Out，主动注销，允许新的登录请求
			// 3. Timeout，超时没有响应，保持原用户的登录凭据
			return await Task.Run(async () =>
			{
				// 通知已登录的用户，其它客户端使用该用户名登录，是否允许登录
				var ct = userRequesting.LoginRequestCancellationTokenSource.Token;

				// 等待已登录的凭据被移除（客户端注销）
				var sw = new Stopwatch();
				sw.Start();

				while (true)
				{
					switch (userRequesting.State)
                    {
                        case CredentialState.Confirmed:
                            // 客户端已注销，可以放行新的凭据
                            CredentialManager.Instance.Grant(userRequesting);
                            EV.PostMessage(ModuleName.System.ToString(), EventEnum.UserLoggedIn,
                                userLoggedIn.AccountInfo.LoginName);
                            return await Task.FromResult(LoginRequestResults.Confirmed);

                        case CredentialState.Reject or CredentialState.RequestCanceled:
                            if(userRequesting.State == CredentialState.Reject)
                                // 已登录用户拒绝了登录请求
                                EV.PostInfoLog(ModuleName.System.ToString(),
                                    $"User {userRequesting.AccountInfo.LoginName}'s login request from {userRequesting.LoginIP}:{userRequesting.LoginPort} is rejected");

                            return await Task.FromResult(LoginRequestResults.Rejected);

						case CredentialState.Requesting:
						case CredentialState.Alive:

                            break;
                    }


                    Thread.Sleep(200);
                    if (ct.IsCancellationRequested)
                        break;

					// 等待指定的时间，然后向申请端发送超时
					if (sw.Elapsed.TotalSeconds > CredentialManager.REQ_LOGIN_CRED_LIFT_TIME_SEC)
						break;

				}

				// 等待确认超时
				return LoginRequestResults.Timeout;
			});
		}

        /// <summary>
        /// 登录。
        /// </summary>
        /// <param name="userName">用户名</param>
        /// <param name="password">密码</param>
        /// <param name="roleID">角色ID</param>
        /// <param name="clientInfo">发起登录请求的客户端信息</param>
        /// <returns><see cref="LoginRequestResults"/></returns>
        /// <exception cref="TimeoutException"></exception>
        internal async Task<LoginResult> Login(string userName, string password, string roleID,
			LoginClientInfo clientInfo)
		{
			var endPoint = GetCurrentWCFClientEndPoint();

			try
			{
				var accountList = RoleAccountLoader.GetAccounts();

				var matchedAccount = accountList.FirstOrDefault(x => x.LoginName == userName);
				if (matchedAccount == null)
					// 用户不存在
					return await Task.FromResult(new LoginResult(LoginRequestResults.NoMatchUser));

				if (matchedAccount.Password != password)
					// 密码错误
					return await Task.FromResult(new LoginResult(LoginRequestResults.WrongPwd));

				// 查找当前用户的登录角色是否存在
				var matchedRole = matchedAccount.RoleIDs.FirstOrDefault(x => x == roleID);
				if (matchedRole == null)
					// 找不到角色
					return await Task.FromResult(new LoginResult(LoginRequestResults.NoMatchRole));

				// 创建新凭据
				var requestingCred = new Credential(CredentialManager.GenerateToken(), matchedAccount)
				{
					LoginIP = endPoint.Address,
					LoginPort = endPoint.Port,
					ClientInfo = clientInfo,
					RoleID = roleID
				};

				// 如果禁用了单会话登录模式，则直接放行凭据。
                if (!_enableSingleSessionMode)
                {
                    CredentialManager.Instance.ForceGrant(requestingCred);
                    EV.PostMessage(ModuleName.System.ToString(), EventEnum.UserLoggedIn, $"{userName}@{requestingCred.LoginIP}:{requestingCred.LoginPort}");
                    return await Task.FromResult(new LoginResult(LoginRequestResults.Confirmed, requestingCred));
                }

				// 如果还没有任何用户登录到RT，则直接放行
                if (CredentialManager.Instance.LoggedInCount == 0 && CredentialManager.Instance.LoginRequestingCount == 0)
				{
                    // 添加凭据到登录请求列表，Grant时需要请求列表中存在此凭据。
                    CredentialManager.Instance.AddRequestingList(requestingCred);

                    // 没有用户登录，直接发放凭据
                    CredentialManager.Instance.Grant(requestingCred);
					EV.PostMessage(ModuleName.System.ToString(), EventEnum.UserLoggedIn, $"{userName}@{requestingCred.LoginIP}:{requestingCred.LoginPort}");
					return await Task.FromResult(new LoginResult(LoginRequestResults.Confirmed, requestingCred));
				}

				if (CredentialManager.Instance.IsSupportMultiUserLogin)
				{
                    // 支持多用户同时登录

                    // 如果当前用户名正在请求登录，则拒绝发起新的请求。
                    if (CredentialManager.Instance.GetRequestingCredential(userName) != null)
                        return await Task.FromResult(new LoginResult(LoginRequestResults.RequstingLogin));

                    // 添加凭据到登录请求列表，Grant时需要请求列表中存在此凭据。
                    CredentialManager.Instance.AddRequestingList(requestingCred);

                    // 查找当前请求的用户是否已经登录				
                    var loggedCred = CredentialManager.Instance.GetCredential(userName);
					if (loggedCred == null)
					{
						// 用户未登录，直接放行凭据
						CredentialManager.Instance.Grant(requestingCred);
						EV.PostMessage(ModuleName.System.ToString(), EventEnum.UserLoggedIn, $"{userName}@{requestingCred.LoginIP}:{requestingCred.LoginPort}");
						return await Task.FromResult(new LoginResult(LoginRequestResults.Confirmed, requestingCred));
					}

					// 否则等待已登录的Session确认
					var ret = await RequestAndWaitLoginConfirmation(loggedCred, requestingCred);
					return ret == LoginRequestResults.Confirmed ? new LoginResult(ret, requestingCred) : new LoginResult(ret);

				}
				else
				{
					// 仅支持单一用户登录
					if (CredentialManager.Instance.LoggedInCount > 1)
					{
						EV.PostWarningLog(ModuleName.System.ToString(),
							$"{nameof(CredentialManager)} does not support multiple users login but more than one credentials found.");
					}

                    // 如果有正在请求登录，则拒绝发起新的请求。
                    if (CredentialManager.Instance.LoginRequestingCount > 0)
                        return await Task.FromResult(new LoginResult(LoginRequestResults.RequstingLogin));

                    // 添加凭据到登录请求列表，Grant时需要请求列表中存在此凭据。
                    CredentialManager.Instance.AddRequestingList(requestingCred);

                    // 已登录的用户凭据
                    var loggedCred = CredentialManager.Instance.GetCredential();

					var ret = await RequestAndWaitLoginConfirmation(loggedCred, requestingCred);
					return ret == LoginRequestResults.Confirmed ? new LoginResult(ret, requestingCred) : new LoginResult(ret);
				}
			}
			catch (Exception ex)
			{
				LOG.Error(ex.Message, ex);
				return await Task.FromResult(new LoginResult(LoginRequestResults.Error));
			}
			finally
			{

			}
		}

        /// <summary>
        /// 登录请求发起端取消登录请求。
        /// </summary>
        /// <param name="userName"></param>
        internal void CancelLoginRequest(string userName)
		{
            CredentialManager.Instance.Cancel(userName);
        }

        /// <summary>
        /// 确认登录请求。
        /// </summary>
        /// <param name="userName"></param>
        internal void ConfirmedLoginRequest(string userName)
		{
			CredentialManager.Instance.Accept(userName);
		}

        /// <summary>
        /// 拒绝登录请求。
        /// </summary>
        /// <param name="userName"></param>
        internal void RejectLoginRequest(string userName)
		{
			CredentialManager.Instance.Reject(userName);
		}

		internal void Logout(Guid myToken)
		{
			var loginCred = CredentialManager.Instance.GetCredential(myToken);
			if (loginCred != null)
            {
                EV.PostMessage(ModuleName.System.ToString(), EventEnum.UserLoggedOff,
                    $"{loginCred.AccountInfo.LoginName}@{loginCred.LoginIP}:{loginCred.LoginPort}");
			}

			CredentialManager.Instance.Remove(myToken);
		}

		/// <summary>
		/// 校验指定账户密码是否正确。
		/// </summary>
		/// <param name="accountId">账号ID</param>
		/// <param name="passwordMD5">MD5加密后的密码</param>
		/// <returns></returns>
		internal bool CheckPassword(string accountId, string passwordMD5)
		{
			var account = RoleAccountLoader.GetAccounts().FirstOrDefault(x => x.UserId == accountId);
			if (account == null)
			{
				LOG.Error($"CheckPassword Unable to find the account with ID {accountId}");
				return false;
			}

			return account.Password == passwordMD5;

		}
		
		#endregion
	}
}
