using System;
using System.Collections.Generic;
using System.IO;
using System.Xml;
using Aitex.Common.Util;
using Aitex.Core.RT.Event;
using Aitex.Core.RT.Log;
using Aitex.Core.Util;
using Aitex.Core.Utilities;

namespace Aitex.Core.Account
{
	public class AccountManager : Singleton<AccountManager>
	{
        #region Variables

        private readonly Dictionary<string, Tuple<Guid, DateTime, string>> _userList;
        private readonly string _accountPath;
        private readonly string _rolePath;
        private readonly string _viewPath;
        private readonly XmlDocument _accountXml;
        private readonly XmlDocument _roleXml;
        private readonly XmlDocument _viewsXml;

        #endregion

        #region Constructors

        public AccountManager()
        {
            SerialNumber = "001";
            Module = "System";
            try
            {
                _userList = new Dictionary<string, Tuple<Guid, DateTime, string>>();
                _accountPath = Path.Combine(PathManager.GetAccountFilePath(), "Account.xml");
                _rolePath = Path.Combine(PathManager.GetAccountFilePath(), "Roles.xml");
                _viewPath = Path.Combine(PathManager.GetAccountFilePath(), "Views.xml");
                _accountXml = new XmlDocument();
                _roleXml = new XmlDocument();
                var fileInfo = new FileInfo(_rolePath);
                if (!fileInfo.Directory.Exists)
                {
                    fileInfo.Directory.Create();
                }
                if (!fileInfo.Exists)
                {
                    _roleXml.LoadXml("<?xml version=\"1.0\" encoding=\"utf-8\"?><Aitex><Roles></Roles></Aitex>");
                    Save(_roleXml, _rolePath);
                }
                else
                {
                    _roleXml.Load(_rolePath);
                }
                var fileInfo2 = new FileInfo(_accountPath);
                if (!fileInfo2.Directory.Exists)
                {
                    fileInfo2.Directory.Create();
                }
                if (!fileInfo2.Exists)
                {
                    _accountXml.LoadXml("<?xml version='1.0' encoding='utf-8' ?><AccountManagement></AccountManagement>");
                    Save(_accountXml, _accountPath);
                }
                else
                {
                    _accountXml.Load(_accountPath);
                }
                _viewsXml = new XmlDocument();
                fileInfo2 = new FileInfo(_viewPath);
                if (!fileInfo2.Directory.Exists)
                {
                    fileInfo2.Directory.Create();
                }
                if (!fileInfo2.Exists)
                {
                    _viewsXml.LoadXml("<?xml version='1.0' encoding='utf-8' ?><root><Views></Views></root>");
                    Save(_viewsXml, _viewPath);
                }
                else
                {
                    _viewsXml.Load(_viewPath);
                }
                var text = Path.Combine(PathManager.GetCfgDir(), "RolePermission.xml");
                if (!File.Exists(text))
                {
                    var xmlDocument = new XmlDocument();
                    xmlDocument.LoadXml("<?xml version=\"1.0\" encoding=\"utf-8\" ?><Aitex></Aitex>");
                    xmlDocument.Save(text);
                }
            }
            catch (Exception ex)
            {
                LOG.Write(ex);
            }
        }


        #endregion

        #region Properties

        public string SerialNumber { get; }

        public string Module { get; }

        public GetAccountListResult Accounts { get; private set; }

        #endregion

        #region Methods

		public List<Account> GetLoginUserList()
		{
			var list = new List<Account>();
			foreach (var key in _userList.Keys)
			{
				var accountInfo = GetAccountInfo(key).AccountInfo;
				accountInfo.LoginIP = _userList[key].Item3;
				list.Add(accountInfo);
			}
			return list;
		}

		public void RegisterViews(List<string> views)
		{
			try
			{
				var xmlNode = _viewsXml.SelectSingleNode("/root/Views");
				foreach (var view in views)
				{
					if (xmlNode.SelectSingleNode($"View[@Name='{view}']") == null)
					{
						var xmlElement = _viewsXml.CreateElement("View");
						xmlElement.SetAttribute("Name", view);
						xmlElement.SetAttribute("Description", view);
						xmlNode.AppendChild(xmlElement);
					}
				}
				Save(_viewsXml, _viewPath);
			}
			catch (Exception ex)
			{
				LOG.Write(ex);
			}
		}

		private void Save(XmlDocument doc, string path)
		{
			doc.Save(path);
			FileSigner.Sign(path);
			GetAccountList();
		}

		public  void Logout(string accountId)
		{
			try
			{
				LOG.Write($"用户{accountId}注销登录");
				accountId = accountId.ToLower();
				if (_userList.ContainsKey(accountId))
				{
					_userList.Remove(accountId);
				}
				EV.PostMessage("System", EventEnum.UserLoggedOff, accountId);
			}
			catch (Exception ex)
			{
				LOG.Write(ex, $"注销用户{accountId}发生异常");
			}
		}

		public void Kickout(string accountId, string kickOutReason)
		{
			try
			{
				LOG.Write($"用户{accountId}强制注销登录");
				accountId = accountId.ToLower();
				if (_userList.ContainsKey(accountId))
				{
					EV.PostKickoutMessage($"用户{accountId}强制注销登录,{kickOutReason}");
					_userList.Remove(accountId);
				}
				EV.PostMessage(Module, EventEnum.UserLoggedOff, accountId);
			}
			catch (Exception ex)
			{
				LOG.Write(ex, $"强制注销用户{accountId}发生异常");
			}
		}

		public GetAccountInfoResult GetAccountInfo(string accountId)
		{
			try
			{
				accountId = accountId.ToLower();
				var getAccountInfoResult = new GetAccountInfoResult();
				if (!FileSigner.IsValid(_accountPath))
				{
					getAccountInfoResult.Description = "账号文件数字签名校验失败";
					getAccountInfoResult.ActSuccess = false;
				}
				else
				{
					var accountNode = GetAccountNode(accountId);
					if (accountNode == null)
					{
						if (accountId == "admin")
						{
							var account = new Account
							{
								Role = "Admin",
								Permission = GetSingleRolePermission("Admin"),
								AccountId = "admin",
								RealName = "admin",
								Email = "admin@admin.com",
								Telephone = "86-21-88886666",
								Touxian = "Admin",
								Company = "MY Tech",
								Department = "IT",
								Description = "Administrator，拥有用户权限修改、菜单修改，定序器修改等权限.",
								AccountStatus = true,
								Md5Pwd = Md5Helper.GetMd5Hash("admin")
							};
							CreateAccount(account);
							getAccountInfoResult.ActSuccess = true;
							getAccountInfoResult.AccountInfo = account;
							getAccountInfoResult.Description = $"成功获取账号信息{accountId}";
						}
						else
						{
							getAccountInfoResult.Description = $"账号{accountId}不存在";
							getAccountInfoResult.ActSuccess = false;
						}
					}
					else
					{
						getAccountInfoResult.AccountInfo = new Account
						{
							Role = accountNode.SelectSingleNode("Role").InnerText,
							Permission = GetSingleRolePermission((accountId == "admin")
								? "Admin"
								: accountNode.SelectSingleNode("Role").InnerText),
							AccountId = accountId,
							RealName = accountNode.SelectSingleNode("RealName").InnerText,
							Email = accountNode.SelectSingleNode("Email").InnerText,
							Telephone = accountNode.SelectSingleNode("Telephone").InnerText,
							Touxian = accountNode.SelectSingleNode("Touxian").InnerText,
							Company = accountNode.SelectSingleNode("Company").InnerText,
							Department = accountNode.SelectSingleNode("Department").InnerText,
							Description = accountNode.SelectSingleNode("Description").InnerText,
							AccountStatus = (string.Compare(accountNode.SelectSingleNode("AccountState").InnerText,
								"Enable", ignoreCase: true) == 0),
							AccountCreationTime = accountNode.SelectSingleNode("CreationTime").InnerText,
							LastAccountUpdateTime = accountNode.SelectSingleNode("LastUpdateTime").InnerText,
							LastLoginTime = accountNode.SelectSingleNode("LastLoginTime").InnerText,
							Md5Pwd = accountNode.SelectSingleNode("Password").InnerText
						};
						getAccountInfoResult.Description = $"获取账号{accountId}成功";
						getAccountInfoResult.ActSuccess = true;
					}
				}
				return getAccountInfoResult;
			}
			catch (Exception ex)
			{
				var text = $"获取账号{accountId}发生异常";
				LOG.Write(ex, text);
				return new GetAccountInfoResult
				{
					ActSuccess = false,
					Description = text
				};
			}
		}
		
		public ChangePwdResult ChangePassword(string accountId, string newPassword)
		{
			try
			{
				LOG.Write($"修改账号{accountId}的密码");
				accountId = accountId.ToLower();
				var changePwdResult = new ChangePwdResult();
				if (!FileSigner.IsValid(_accountPath))
				{
					changePwdResult.Description = "修改密码失败，账号文件数字签名损坏！";
					changePwdResult.ActSucc = false;
				}
				else
				{
					var accountNode = GetAccountNode(accountId);
					if (accountNode == null)
					{
						changePwdResult.Description = $"账号{accountId}不存在";
						changePwdResult.ActSucc = false;
					}
					else
					{
						accountNode.SelectSingleNode("Password").InnerText = Md5Helper.GetMd5Hash(newPassword);
						Save(_accountXml, _accountPath);
						changePwdResult.Description = "修改密码成功！";
						changePwdResult.ActSucc = true;
						EV.PostMessage(Module, EventEnum.PasswordChanged, accountId);
					}
				}
				return changePwdResult;
			}
			catch (Exception ex)
			{
				var text = $"修改账号{accountId}的密码失败";
				LOG.Write(ex, text);
				return new ChangePwdResult
				{
					ActSucc = false,
					Description = text
				};
			}
		}

		public CreateAccountResult CreateAccount(Account newAccount)
		{
			try
			{
				LOG.Write($"创建账号{newAccount.AccountId}");
				var createAccountResult = new CreateAccountResult();
				if (newAccount == null)
				{
					createAccountResult.Description = "账号有误";
					createAccountResult.ActSucc = false;
				}
				else if (!FileSigner.IsValid(_accountPath))
				{
					createAccountResult.Description = $"创建账号失败，数字签名损坏！";
					createAccountResult.ActSucc = false;
				}
				else if (GetAccountNode(newAccount.AccountId) != null)
				{
					createAccountResult.Description = $"创建账号失败，账号 {newAccount.AccountId} 已存在！";
					createAccountResult.ActSucc = false;
				}
				else
				{
					var xmlElement = _accountXml.CreateElement("Account");
					xmlElement.SetAttribute("AccountId", newAccount.AccountId.ToLower());
					_accountXml.DocumentElement.AppendChild(xmlElement);
					var xmlElement2 = _accountXml.CreateElement("RealName");
					xmlElement2.InnerText = newAccount.RealName;
					xmlElement.AppendChild(xmlElement2);
					xmlElement2 = _accountXml.CreateElement("Role");
					xmlElement2.InnerText = newAccount.Role.ToString();
					xmlElement.AppendChild(xmlElement2);
					xmlElement2 = _accountXml.CreateElement("Password");
					xmlElement2.InnerText = Md5Helper.GetMd5Hash(newAccount.AccountId);
					xmlElement.AppendChild(xmlElement2);
					xmlElement2 = _accountXml.CreateElement("AccountState");
					xmlElement2.InnerText = (newAccount.AccountStatus ? "Enable" : "Disable");
					xmlElement.AppendChild(xmlElement2);
					xmlElement2 = _accountXml.CreateElement("Email");
					xmlElement2.InnerText = newAccount.Email;
					xmlElement.AppendChild(xmlElement2);
					xmlElement2 = _accountXml.CreateElement("Telephone");
					xmlElement2.InnerText = newAccount.Telephone;
					xmlElement.AppendChild(xmlElement2);
					xmlElement2 = _accountXml.CreateElement("Touxian");
					xmlElement2.InnerText = newAccount.Touxian;
					xmlElement.AppendChild(xmlElement2);
					xmlElement2 = _accountXml.CreateElement("Company");
					xmlElement2.InnerText = newAccount.Company;
					xmlElement.AppendChild(xmlElement2);
					xmlElement2 = _accountXml.CreateElement("Department");
					xmlElement2.InnerText = newAccount.Department;
					xmlElement.AppendChild(xmlElement2);
					xmlElement2 = _accountXml.CreateElement("Description");
					xmlElement2.InnerText = newAccount.Description;
					xmlElement.AppendChild(xmlElement2);
					xmlElement2 = _accountXml.CreateElement("CreationTime");
					xmlElement2.InnerText = DateTime.Now.ToString();
					xmlElement.AppendChild(xmlElement2);
					xmlElement2 = _accountXml.CreateElement("LastLoginTime");
					xmlElement2.InnerText = string.Empty;
					xmlElement.AppendChild(xmlElement2);
					xmlElement2 = _accountXml.CreateElement("LastUpdateTime");
					xmlElement2.InnerText = string.Empty;
					xmlElement.AppendChild(xmlElement2);
					Save(_accountXml, _accountPath);
					createAccountResult.Description = $"创建新账号{newAccount.AccountId}成功";
					createAccountResult.ActSucc = true;
					EV.PostMessage(Module, EventEnum.AccountCreated, newAccount.AccountId);
				}
				return createAccountResult;
			}
			catch (Exception ex)
			{
				var text = $"创建账号{newAccount.AccountId}失败";
				LOG.Write(ex, text);
				return new CreateAccountResult
				{
					ActSucc = false,
					Description = text
				};
			}
		}

		public DeleteAccountResult DeleteAccount(string accountId)
		{
			try
			{
				LOG.Write($"删除账号{accountId}");
				accountId = accountId.ToLower();
				var deleteAccountResult = new DeleteAccountResult();
				if (accountId == "admin")
				{
					deleteAccountResult.Description = "Admin'admin'账号不能删除";
					deleteAccountResult.ActSucc = false;
				}
				else if (!FileSigner.IsValid(_accountPath))
				{
					deleteAccountResult.Description = "删除账号失败，账号文件数字签名损坏！";
					deleteAccountResult.ActSucc = false;
				}
				else
				{
					var accountNode = GetAccountNode(accountId);
					if (accountNode == null)
					{
						deleteAccountResult.Description = $"删除账号 {accountId} 失败，账号不存在！";
						deleteAccountResult.ActSucc = false;
					}
					else
					{
						_accountXml.DocumentElement.RemoveChild(accountNode);
						Save(_accountXml, _accountPath);
						deleteAccountResult.Description = $"删除账号 {accountId} 成功！";
						deleteAccountResult.ActSucc = true;
						EV.PostMessage(Module, EventEnum.AccountDeleted, accountId);
					}
				}
				return deleteAccountResult;
			}
			catch (Exception ex)
			{
				var text = $"删除账号{accountId}发生异常";
				LOG.Write(ex, text);
				return new DeleteAccountResult
				{
					ActSucc = false,
					Description = text
				};
			}
		}

		public UpdateAccountResult UpdateAccount(Account account)
		{
			try
			{
				var updateAccountResult = new UpdateAccountResult();
				if (account == null)
				{
					updateAccountResult.Description = "账号有误";
					updateAccountResult.ActSucc = false;
				}
				else if (!FileSigner.IsValid(_accountPath))
				{
					updateAccountResult.Description = $"更新账号资料失败，账号文件数字签名损坏！";
					updateAccountResult.ActSucc = false;
				}
				else
				{
					var accountNode = GetAccountNode(account.AccountId.ToLower());
					if (accountNode == null)
					{
						updateAccountResult.Description = $"更新账号 {account.AccountId} 失败，账号不存在！";
						updateAccountResult.ActSucc = false;
					}
					else
					{
						accountNode.SelectSingleNode("RealName").InnerText = account.RealName;
						accountNode.SelectSingleNode("Role").InnerText = ((account.AccountId.ToLower() == "admin") ? "Admin" : account.Role.ToString());
						accountNode.SelectSingleNode("AccountState").InnerText = (account.AccountStatus ? "Enable" : "Disable");
						accountNode.SelectSingleNode("Email").InnerText = account.Email;
						accountNode.SelectSingleNode("Telephone").InnerText = account.Telephone;
						accountNode.SelectSingleNode("Touxian").InnerText = account.Touxian;
						accountNode.SelectSingleNode("Company").InnerText = account.Company;
						accountNode.SelectSingleNode("Department").InnerText = account.Department;
						accountNode.SelectSingleNode("Description").InnerText = account.Description;
						accountNode.SelectSingleNode("CreationTime").InnerText = account.AccountCreationTime;
						accountNode.SelectSingleNode("LastLoginTime").InnerText = account.LastLoginTime;
						accountNode.SelectSingleNode("LastUpdateTime").InnerText = account.LastAccountUpdateTime;
						Save(_accountXml, _accountPath);
						updateAccountResult.Description = $"成功更新 {account.AccountId} 的账号资料！";
						updateAccountResult.ActSucc = true;
						EV.PostMessage(Module, EventEnum.AccountChanged, account.AccountId);
					}
				}
				return updateAccountResult;
			}
			catch (Exception ex)
			{
				var text = $"更新账号{account.AccountId}资料发生异常";
				LOG.Write(ex, text);
				return new UpdateAccountResult
				{
					ActSucc = false,
					Description = text
				};
			}
		}

		public GetAccountListResult GetAccountList()
		{
			try
			{
				LOG.Write("获取所有的账号信息列表");
				var getAccountListResult = new GetAccountListResult();
				if (!FileSigner.IsValid(_accountPath))
				{
					getAccountListResult.Description = "获取账号列表失败，账号文件数字签名文件损坏！";
					getAccountListResult.ActSuccess = false;
					getAccountListResult.AccountList = null;
				}
				else
				{
					var xmlNodeList = _accountXml.SelectNodes("AccountManagement/Account");
					var list = new List<Account>();
					foreach (XmlNode item in xmlNodeList)
					{
						list.Add(GetAccountInfo(item.Attributes["AccountId"].Value).AccountInfo);
					}
					getAccountListResult.AccountList = list;
					getAccountListResult.Description = "成功获取账号列表！";
					getAccountListResult.ActSuccess = true;
				}
				Accounts = getAccountListResult;
				return getAccountListResult;
			}
			catch (Exception ex)
			{
				var text = "获取账号列表发生异常";
				LOG.Write(ex, text);
				return new GetAccountListResult
				{
					AccountList = null,
					ActSuccess = false,
					Description = text
				};
			}
		}

		public void CheckAlive(string accountId)
		{
			try
			{
				if (_userList.ContainsKey(accountId))
				{
					_userList[accountId] = new Tuple<Guid, DateTime, string>(_userList[accountId].Item1, DateTime.Now, _userList[accountId].Item3);
				}
				else
				{
					EV.PostKickoutMessage($"账号{accountId}已在服务器上注销");
				}
			}
			catch (Exception ex)
			{
				LOG.Write(ex);
			}
		}

		private XmlElement GetAccountNode(string accountId)
		{
			var xmlNode = _accountXml.SelectSingleNode($"/AccountManagement/Account[@AccountId='{accountId.ToLower()}']");
			return (XmlElement)xmlNode;
		}

		public static SerializableDictionary<string, string> GetAllViewList()
		{
			var serializableDictionary = new SerializableDictionary<string, string>();
			try
			{
				var xmlDocument = new XmlDocument();
				var filename = Path.Combine(PathManager.GetAccountFilePath(), "Views.xml");
				xmlDocument.Load(filename);
				var xmlNodeList = xmlDocument.SelectNodes("/root/Views/View");
				if (xmlNodeList != null)
				{
					foreach (XmlElement item in xmlNodeList)
					{
						serializableDictionary.Add(item.Attributes["Name"].Value, item.Attributes["Description"].Value);
					}
				}
			}
			catch (Exception ex)
			{
				LOG.Write(ex);
				serializableDictionary = new SerializableDictionary<string, string>();
			}
			return serializableDictionary;
		}

		public bool SaveAllRolesPermission(Dictionary<string, Dictionary<string, ViewPermission>> data)
		{
			try
			{
				var xmlElement = _roleXml.SelectSingleNode("/Aitex/Roles") as XmlElement;
				xmlElement.RemoveAll();
				foreach (var datum in data)
				{
					if (datum.Key == "Admin")
					{
						continue;
					}
					var xmlElement2 = _roleXml.CreateElement("Role");
					xmlElement2.SetAttribute("Name", datum.Key);
					xmlElement.AppendChild(xmlElement2);
					foreach (var key in data[datum.Key].Keys)
					{
						var xmlElement3 = _roleXml.CreateElement("View");
						xmlElement2.AppendChild(xmlElement3);
						xmlElement3.SetAttribute("Name", key);
						xmlElement3.SetAttribute("Permission", data[datum.Key][key].ToString());
					}
				}
				_roleXml.Save(_rolePath);
			}
			catch (Exception ex)
			{
				LOG.Write(ex);
				return false;
			}
			return true;
		}


		public SerializableDictionary<string, ViewPermission> GetSingleRolePermission(string roleName)
		{
			var serializableDictionary = new SerializableDictionary<string, ViewPermission>();
			try
			{
				var allViewList = GetAllViewList();
				if (roleName == "Admin")
				{
					foreach (var item in allViewList)
					{
						serializableDictionary.Add(item.Key, ViewPermission.FullyControl);
					}
				}
				else
				{
					var xmlNode = _roleXml.SelectSingleNode($"/Aitex/Roles/Role[@Name='{roleName}']");
					if (xmlNode != null)
					{
						foreach (XmlElement item2 in xmlNode)
						{
							var value = item2.Attributes["Name"].Value;
							var value2 = item2.Attributes["Permission"].Value;
							if (allViewList.ContainsKey(value))
							{
								serializableDictionary.Add(value, (ViewPermission)Enum.Parse(typeof(ViewPermission), value2, ignoreCase: true));
							}
						}
					}
				}
			}
			catch (Exception ex)
			{
				LOG.Write(ex);
				serializableDictionary = new SerializableDictionary<string, ViewPermission>();
			}
			return serializableDictionary;
		}
		
		#endregion
	}
}
