using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Aitex.Common.Util;
using Aitex.Core.RT.Log;
using MECF.Framework.Common.Account.Permissions;

namespace MECF.Framework.Common.Account.Extends
{
	/// <summary>
	/// 角色账号加载器，从_AccountEx.xml文件加载角色。
	/// </summary>
	public class RoleAccountLoader : XmlLoader
	{
		#region Variables
		
		private const string NODE_NAME_PERMS_GROUP = "Perms";
		private const string NODE_NAME_PERMS_ITEM = "Perm";

		private readonly MenuLoader _menuLoader;
		private List<Role> _roleList;
		private List<AccountEx> _accountList;

		#endregion
		
		#region Constructors

		/// <summary>
		/// 创建角色加载器对象实例。
		/// </summary>
		/// <param name="fileName">角色配置文件名称，通常为_Account.xml</param>
		public RoleAccountLoader(string fileName)
			: base(fileName)
		{
			
			_menuLoader = new MenuLoader(PathManager.GetCfgDir() + "menu.xml");
			_menuLoader.Load();
		}

		#endregion

		#region Methods

		/// <summary>
		/// 获取配置文件中定义的所有菜单项。
		/// </summary>
		/// <returns></returns>
		public List<AppMenu> GetAllMenus()
		{
			return _menuLoader.MenuList;
		}
		
		/// <summary>
		/// 获取所有非超级用户角色列表。
		/// </summary>
		/// <returns></returns>
		public List<Role> GetRoles(bool includeSuper = false)
        {
            if (includeSuper)
                return _roleList;

			return _roleList.Where(x => !x.IsSuper).ToList();
		}

        /// <summary>
        /// 获取所有非超级用户账号列表。
        /// </summary>
        /// <returns></returns>
        public List<AccountEx> GetAccounts(bool includeSuper = false)
		{
            if (includeSuper)
                return _accountList;

            return _accountList.Where(x => !x.IsSuper).ToList();
		}

		/// <summary>
		/// 解析Account.xml文件。
		/// </summary>
		protected override void AnalyzeXml()
		{
			if (XmlDoc == null)
			{
				return;
			}
			var roleItems = from r in XmlDoc.Descendants("roleItem")
				select (r);
			var list = new List<Role>();
			foreach (var roleNode in roleItems)
			{
				var strRoleId = roleNode.Attribute("id").Value;
				var strRoleName = roleNode.Attribute("name").Value;
				var strIsAutoLogout = roleNode.Attribute("autologout").Value;
				var strAutoLogoutTime = roleNode.Attribute("logouttime").Value;
				int.TryParse(strAutoLogoutTime, out var logoutTime);
				var isAutoLogout = ((strIsAutoLogout == "1"));

				var strBuildIn = roleNode.Attribute("buildin")?.Value ?? "false";
				if (!bool.TryParse(strBuildIn, out var isBuildIn))
					isBuildIn = false;
				// ID为0 ~ 3的角色强制为系统内置角色，禁止删除
				if (strRoleId is "0" or "1" or "2" or "3")
					isBuildIn = true;

				var strDescription = roleNode.Attribute("description")?.Value ?? "";

				// 旧版的权限保存方式为字符串形式，新版更改为<Perm />节点列表。
				// 为兼容旧格式，此处应该判断roleNode节点中的内容，将旧版自动转换为PermissionControlItem列表
				if (roleNode.FirstNode is XText)
				{
					// 旧版
					var permContent = roleNode.Value;
					var helper = new PermissionHelper();
					helper.ParsePermission(permContent);
					var role = new Role(strRoleId, strRoleName, isAutoLogout, logoutTime,
						helper.MenuPermissionDictionary, isBuildIn, strDescription);
					list.Add(role);
				}
				else
				{
					// 新版
					var permList = new Dictionary<string, MenuPermissionEnum>();
					foreach (var grpPerms in roleNode.Elements(NODE_NAME_PERMS_GROUP))
					{
                        foreach (var xe in grpPerms.Elements(NODE_NAME_PERMS_ITEM))
                        {
                            var permName = xe.Attribute("name")?.Value;
                            var strPermValue = xe.Attribute("value")?.Value ?? "1";
                            if (!int.TryParse(strPermValue, out var permValue))
                                permValue = (int)MenuPermissionEnum.MP_NONE;

                            if (!string.IsNullOrEmpty(permName))
                                permList[permName] = (MenuPermissionEnum)permValue;
                        }
					}
					
					var role = new Role(strRoleId, strRoleName, isAutoLogout, logoutTime,
						permList, isBuildIn, strDescription);
					list.Add(role);
				}
			}

			var roleAdmin = new Role("-1", "Administrators", true, 20, null, true, null)
			{
				IsSuper = true
			};
			list.Add(roleAdmin);

			_roleList = list;
			var users = from r in XmlDoc.Descendants("userItem")
				select (r);
			var lstUsers = new List<AccountEx>();
			
			foreach (var userNode in users)
			{
				var lstRolesBelongTo = new List<string>();
				var strUserId = userNode.Attribute("id").Value;
				var strLoginName = userNode.Attribute("loginname").Value;
				var strPassword = Decrypt(userNode.Attribute("password").Value);
				var strFirstName = userNode.Attribute("firstname").Value;
				var strLastName = userNode.Attribute("lastname").Value;
				var strEmail = userNode.Attribute("email").Value;
				var strDescription = "";
				if (userNode.Attribute("description") != null)
				{
					strDescription = userNode.Attribute("description").Value;
				}
				var userRoles = from ro in userNode.Descendants("role")
					select (ro);
				foreach (var userRoleNode in userRoles)
				{
					var strRoleId = userRoleNode.Attribute("id").Value;
					lstRolesBelongTo.Add(strRoleId);
				}
				
				var item3 = new AccountEx(strUserId, strLoginName, strPassword, strFirstName, strLastName, strEmail, lstRolesBelongTo, strDescription);
				lstUsers.Add(item3);
			}
			var admin = new AccountEx("-1", "sic_admin", "sic_admin", "", "", "", new List<string> { "-1" })
			{
				IsSuper = true
			};
			lstUsers.Add(admin);

            var readOnly = new AccountEx("-2", "Read-Only", "read-only", "", "", "", new List<string> { "-1" })
            {
                IsSuper = true
            };
			lstUsers.Add(readOnly);
			
			_accountList = lstUsers;
		}

		/// <summary>
		/// 获取一个可用的角色ID。
		/// </summary>
		/// <returns></returns>
        public string GetAvailableRoleID()
        {
			// 4以下的ID为系统保留ID。
            var id = 4;
            while (true)
            {
                var ro = _roleList.FirstOrDefault(x => x.RoleId == id.ToString());
                if (ro == null)
                    break;

                if (id >= 10000)
                    throw new IndexOutOfRangeException("no available role ID.");

                id++;
            }

            return id.ToString();
        }

        /// <summary>
        /// 更新指定的角色。
        /// </summary>
        /// <param name="newRole"></param>
        /// <returns></returns>
        public bool UpdateRole(Role newRole)
		{
			var oldRole = _roleList.Find((Role item) => item.RoleId == newRole.RoleId);
			if (oldRole == null)
				_roleList.Add(newRole);
			else
				_roleList[_roleList.IndexOf(oldRole)] = newRole;

			var doc = XmlDoc;
			var roleToUpdate = doc.Descendants("roleItem").FirstOrDefault(x => x.Attribute("id").Value == newRole.RoleId);
            
			if (roleToUpdate != null)
			{
				// 修改现有的Role
				roleToUpdate.RemoveNodes();
			}
			else
			{
				// 新增Role
				roleToUpdate = new XElement("roleItem");
				doc.Root.Element("roles").Add(roleToUpdate);
			}
			
			// 设置属性
			roleToUpdate.SetAttributeValue("id", newRole.RoleId);
			roleToUpdate.SetAttributeValue("name", newRole.RoleName);
			roleToUpdate.SetAttributeValue("autologout", (newRole.IsAutoLogout ? "1" : "0"));
			roleToUpdate.SetAttributeValue("logouttime", newRole.LogoutTime.ToString());
			roleToUpdate.SetAttributeValue("buildin", newRole.IsBuildIn.ToString());
			roleToUpdate.SetAttributeValue("description", newRole.Description);

			// 创建权限清单
			var permList = new XElement(NODE_NAME_PERMS_GROUP);
			foreach (var perm in newRole.PermissionControlItems)
				permList.Add(new XElement(NODE_NAME_PERMS_ITEM,
                    new XAttribute("name", perm.Key),
					new XAttribute("value", (int)perm.Value)));
			roleToUpdate.Add(permList);

			doc.Save(FileName);
			return true;
		}

		/// <summary>
		/// 删除指定ID的角色。
		/// </summary>
		/// <param name="roleId"></param>
		/// <returns></returns>
		public bool DeleteRole(string roleId)
		{
			Load();
			var roleToDelete = _roleList.Find((Role item) => item.RoleId == roleId);
			if (roleToDelete != null)
			{
				_roleList.Remove(roleToDelete);
				var xmlDoc = XmlDoc;
				var list = (from m_xRole in xmlDoc.Descendants("roleItem")
					where m_xRole.Attribute("id").Value == roleId
					select m_xRole).ToList();
				if (list.Count > 0)
				{
					list[0].Remove();
					foreach (var item in _accountList)
					{
						if (item.RoleIDs.Contains(roleToDelete.RoleId))
						{
							item.RoleIDs.Remove(roleToDelete.RoleId);
						}
					}
					list = (from m_xRole in xmlDoc.Descendants("role")
						where m_xRole.Attribute("id").Value == roleToDelete.RoleId
						select m_xRole).ToList();
					if (list.Count > 0)
					{
						list.Remove();
					}
					xmlDoc.Save(FileName);
					return true;
				}
				return false;
			}
			return false;
		}

		private Dictionary<string, MenuPermissionEnum> GetRolePermission(string roleId)
		{
			var perms = _roleList.FirstOrDefault(x => x.RoleId == roleId)?.PermissionControlItems;
			if(perms == null)
				LOG.Error($"Unable to find the Role by ID {roleId}, or the permission list is empty.");
			return perms;
		}
        
		/// <summary>
		/// 
		/// </summary>
		/// <param name="roleId"></param>
		/// <returns></returns>
		public List<AppMenu> GetAccessibleMenusByRole(string roleId)
		{
			var list = new List<AppMenu>();
			var rolePermission = GetRolePermission(roleId);
			if (rolePermission == null)
				return null;
			
			foreach (var menu in GetAllMenus())
			{
				var appMenusHasPerm = new List<AppMenu>();
				foreach (var menuItem in menu.MenuItems)
				{
					if (!rolePermission.TryGetValue(menuItem.MenuID, out var perm))
					{
						// 当前菜单的权限未定义
						LOG.Error($"The permission of {menuItem.MenuID} is not defined.");
						continue; 
					}

					var appMenu = new AppMenu(menuItem.MenuID, menuItem.ViewModel, menuItem.ResKey, null,
						menuItem.Description)
					{
						System = menuItem.System,
						AlarmModule = menuItem.AlarmModule,
						Permission = perm
					};
					if (appMenu.Permission > MenuPermissionEnum.MP_NONE)
					{
						appMenusHasPerm.Add(appMenu);
					}
				}

				if (appMenusHasPerm.Count > 0)
				{
					var appMenu2 = new AppMenu(menu.MenuID, menu.ViewModel, menu.ResKey, appMenusHasPerm, menu.Description);
					appMenu2.System = menu.System;
					appMenu2.AlarmModule = menu.AlarmModule;
					list.Add(appMenu2);
				}
			}

			return list;
		}

		/// <summary>
		/// 获取指定角色指定菜单的访问权限。
		/// </summary>
		/// <param name="roleId"></param>
		/// <param name="menuName"></param>
		/// <returns></returns>
		public MenuPermissionEnum GetMenuPermission(string roleId, string menuName)
		{
			var rolePermission = GetRolePermission(roleId);
			if (rolePermission.TryGetValue(menuName, out var perm))
				return perm;

			return MenuPermissionEnum.MP_NONE;
		}

		/// <summary>
		/// 保存账号。如果当前账号ID已存在，则更新账号；否则新建账号并保存。
		/// </summary>
		/// <param name="newAccount"></param>
		/// <returns></returns>
		public bool UpdateAccount(AccountEx newAccount)
		{
			var accountEx = _accountList.Find((AccountEx item) => item.UserId == newAccount.UserId);
			if (accountEx == null)
			{
				_accountList.Add(newAccount);
			}
			else
			{
				_accountList[_accountList.IndexOf(accountEx)] = newAccount;
			}
			var xmlDoc = XmlDoc;
			var list = (from xAccount in xmlDoc.Descendants("userItem")
				where xAccount.Attribute("id")?.Value == newAccount.UserId
				select xAccount).ToList();

			if (list.Count > 0)
			{
				list[0].SetAttributeValue("loginname", newAccount.LoginName);
				list[0].SetAttributeValue("password", Encrypt(newAccount.Password));
				list[0].SetAttributeValue("firstname", newAccount.FirstName);
				list[0].SetAttributeValue("lastname", newAccount.LastName);
				list[0].SetAttributeValue("email", newAccount.Email);
				list[0].SetAttributeValue("description", newAccount.Description);

                list[0].Element("rolegroup").RemoveAll();
				foreach (var roleID in newAccount.RoleIDs)
				{
					list[0].Element("rolegroup").Add(new XElement("role", new XAttribute("id", roleID)));
				}
			}
			else
			{
				var xElement = new XElement("userItem", new XAttribute("id", newAccount.UserId), new XAttribute("loginname", newAccount.LoginName), new XAttribute("password", Encrypt(newAccount.Password)), new XAttribute("firstname", newAccount.FirstName), new XAttribute("lastname", newAccount.LastName), new XAttribute("email", newAccount.Email), new XAttribute("description", newAccount.Description), new XElement("rolegroup"));
				foreach (var roleID2 in newAccount.RoleIDs)
				{
					xElement.Element("rolegroup").Add(new XElement("role", new XAttribute("id", roleID2)));
				}
				xmlDoc.Root.Element("users").Add(xElement);
			}
			xmlDoc.Save(FileName);
			return true;
		}

		/// <summary>
		/// 删除账号。
		/// </summary>
		/// <param name="id"></param>
		/// <returns></returns>
		public bool DeleteAccount(string id)
		{
			var accountEx = _accountList.Find((AccountEx item) => item.UserId == id);
			if (accountEx != null)
			{
				_accountList.Remove(accountEx);
				var xdoc = XmlDoc;
				var list = (from xAccount in xdoc.Descendants("userItem")
					where xAccount.Attribute("id")?.Value == id
					select xAccount).ToList();
				if (list.Count > 0)
				{
					list[0].Remove();
					xdoc.Save(FileName);
					return true;
				}
				return false;
			}
			return false;
		}

		/// <summary>
		/// 加密字符串。
		/// </summary>
		/// <param name="encrytStr"></param>
		/// <returns></returns>
		public string Encrypt(string encrytStr)
		{
			if (string.IsNullOrWhiteSpace(encrytStr))
			{
				return string.Empty;
			}
			try
			{
				var bytes = Encoding.UTF8.GetBytes(encrytStr);
				return Convert.ToBase64String(bytes);
			}
			catch
			{
				return encrytStr;
			}
		}

		/// <summary>
		/// 解密字符串。
		/// </summary>
		/// <param name="decryptStr"></param>
		/// <returns></returns>
		public string Decrypt(string decryptStr)
		{
			if (string.IsNullOrWhiteSpace(decryptStr))
			{
				return string.Empty;
			}
			try
			{
				var bytes = Convert.FromBase64String(decryptStr);
				return Encoding.UTF8.GetString(bytes);
			}
			catch
			{
				return decryptStr;
			}
		}
		
		#endregion
	}
}
