using System.Collections.Generic;
using System.Xml;

namespace Webserver.Permissions {
	public class AdminApiTokens : AdminSectionAbs {
		public static AdminApiTokens Instance { get; private set; }

		public AdminApiTokens (AdminTools _parent) : base (_parent, "apitokens") {
			Instance = this;
		}

		private readonly Dictionary<string, ApiToken> tokens = new CaseInsensitiveStringDictionary<ApiToken> ();


#region IO

		public override void Clear () {
			tokens.Clear ();
		}

		public override void ParseElement (XmlElement _childElement) {
			if (ApiToken.TryParse (_childElement, out ApiToken apiToken)) {
				tokens [apiToken.Name] = apiToken;
			}
		}

		public override void Save (XmlElement _root) {
			XmlElement adminTokensElem = _root.AddXmlElement (SectionTypeName);
			adminTokensElem.AddXmlComment (" <token name=\"adminuser1\" secret=\"supersecrettoken\" permission_level=\"0\" /> ");

			foreach ((string _, ApiToken apiToken) in tokens) {
				apiToken.ToXml (adminTokensElem);
			}
		}

#endregion


#region Runtime interaction

		public void AddToken (string _name, string _secret, int _permissionLevel) {
			lock (Parent) {
				ApiToken apiToken = new ApiToken (_name, _secret, _permissionLevel);
				tokens[_name] = apiToken;
				Parent.Save ();
			}
		}

		public bool RemoveToken (string _name) {
			lock (Parent) {
				bool removed = tokens.Remove (_name);
				if (removed) {
					Parent.Save ();
				}

				return removed;
			}
		}

		public Dictionary<string, ApiToken> GetTokens () {
			lock (Parent) {
				return tokens;
			}
		}

#endregion

		public readonly struct ApiToken {
			public readonly string Name;
			public readonly string Secret;
			public readonly int PermissionLevel;

			public ApiToken (string _name, string _secret, int _permissionLevel) {
				Name = _name;
				Secret = _secret;
				PermissionLevel = _permissionLevel;
			}
			
			public void ToXml (XmlElement _parent) {
				_parent.AddXmlElement ("token")
					.SetAttrib ("name", Name)
					.SetAttrib ("secret", Secret)
					.SetAttrib ("permission_level", PermissionLevel.ToString ());
			}

			public static bool TryParse (XmlElement _element, out ApiToken _result) {
				_result = default;


				if (!_element.TryGetAttribute ("name", out string name)) {
					Log.Warning ($"[Web] [Perms] Ignoring apitoken-entry because of missing 'name' attribute: {_element.OuterXml}");
					return false;
				}

				if (!_element.TryGetAttribute ("secret", out string secret)) {
					Log.Warning ($"[Web] [Perms] Ignoring apitoken-entry because of missing 'secret' attribute: {_element.OuterXml}");
					return false;
				}

				if (!_element.TryGetAttribute ("permission_level", out string permissionLevelString)) {
					Log.Warning ($"[Web] [Perms] Ignoring apitoken-entry because of missing 'permission_level' attribute: {_element.OuterXml}");
					return false;
				}

				if (!int.TryParse (permissionLevelString, out int permissionLevel)) {
					Log.Warning (
						$"[Web] [Perms] Ignoring apitoken-entry because of invalid (non-numeric) value for 'permission_level' attribute: {_element.OuterXml}");
					return false;
				}

				
				_result = new ApiToken (name, secret, permissionLevel);
				return true;
			}
		}


#region Specials

		public int GetPermissionLevel (string _name, string _secret) {
			lock (Parent) {
				if (tokens.TryGetValue (_name, out ApiToken apiToken) && apiToken.Secret == _secret) {
					return apiToken.PermissionLevel;
				}

				if (IsCommandlineToken (_name, _secret)) {
					return 0;
				}

				return int.MaxValue;
			}
		}

		private bool commandlineChecked;
		private string commandlineTokenName;
		private string commandlineTokenSecret;

		private bool IsCommandlineToken (string _name, string _secret) {
			if (!commandlineChecked) {
				commandlineTokenName = GameUtils.GetLaunchArgument ("webapitokenname");
				commandlineTokenSecret = GameUtils.GetLaunchArgument ("webapitokensecret");

				commandlineChecked = true;
			}

			if (string.IsNullOrEmpty (commandlineTokenName) || string.IsNullOrEmpty (commandlineTokenSecret)) {
				return false;
			}

			return _name == commandlineTokenName && _secret == commandlineTokenSecret;
		}

#endregion
	}
}