using System;
using System.Collections.Generic;
using System.Net;
using JetBrains.Annotations;
using Utf8Json;
using Webserver.Permissions;

namespace Webserver.WebAPI.APIs.Permissions {
	[UsedImplicitly]
	public class WebModules : AbsRestApi {
		private const string propertyModule = "module";
		private const string propertyPermissionLevelGlobal = "permissionLevelGlobal";
		private const string propertyPermissionLevelPerMethod = "permissionLevelPerMethod";
		private const string propertyIsDefault = "isDefault";
		

		private static readonly byte[] jsonKeyModule = JsonWriter.GetEncodedPropertyNameWithBeginObject (propertyModule);
		private static readonly byte[] jsonKeyPermissionLevelGlobal = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator (propertyPermissionLevelGlobal);
		private static readonly byte[] jsonKeyPermissionLevelPerMethod = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator (propertyPermissionLevelPerMethod);
		private static readonly byte[] jsonKeyIsDefault = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator (propertyIsDefault);

		private static readonly byte[][] jsonMethodNameKeys;

		static WebModules () {
			jsonMethodNameKeys = new byte[(int)ERequestMethod.Count][];
			for (int i = 0; i < jsonMethodNameKeys.Length; i++) {
				ERequestMethod method = (ERequestMethod)i;
				jsonMethodNameKeys [i] = JsonWriter.GetEncodedPropertyName (method.ToStringCached ());
			}
		}

		private static AdminWebModules ModulesInstance => AdminWebModules.Instance;

		protected override void HandleRestGet (RequestContext _context) {
			string id = _context.RequestPath;

			PrepareEnvelopedResult (out JsonWriter writer);
			
			if (string.IsNullOrEmpty (id)) {
				
				writer.WriteBeginArray ();
			
				bool first = true;
				foreach (AdminWebModules.WebModule module in ModulesInstance.GetModules ()) {
					if (!first) {
						writer.WriteValueSeparator ();
					}

					first = false;

					writeModuleJson (ref writer, module);
				}

				writer.WriteEndArray ();
				
				SendEnvelopedResult (_context, ref writer);
				return;
			}

			writer.WriteRaw (WebUtils.JsonEmptyData);
			SendEnvelopedResult (_context, ref writer, HttpStatusCode.BadRequest);
		}

		private void writeModuleJson (ref JsonWriter _writer, AdminWebModules.WebModule _module) {
			_writer.WriteRaw (jsonKeyModule);
			_writer.WriteString (_module.Name);
			_writer.WriteRaw (jsonKeyPermissionLevelGlobal);
			_writer.WriteInt32 (_module.LevelGlobal);
			_writer.WriteRaw (jsonKeyPermissionLevelPerMethod);

			_writer.WriteBeginObject ();

			if (_module.LevelPerMethod != null) {
				bool first = true;
				for (int iMethod = 0; iMethod < _module.LevelPerMethod.Length; iMethod++) {
					int methodLevel = _module.LevelPerMethod [iMethod];

					if (methodLevel == AdminWebModules.MethodLevelNotSupported) {
						continue;
					}

					if (!first) {
						_writer.WriteValueSeparator ();
					}

					first = false;

					_writer.WriteRaw (jsonMethodNameKeys [iMethod]);
					if (methodLevel == AdminWebModules.MethodLevelInheritGlobal) {
						_writer.WriteString (AdminWebModules.MethodLevelInheritKeyword);
					} else {
						_writer.WriteInt32 (methodLevel);
					}
				}
			}

			_writer.WriteEndObject ();

			_writer.WriteRaw (jsonKeyIsDefault);
			_writer.WriteBoolean (_module.IsDefault);

			_writer.WriteEndObject ();
		}

		protected override void HandleRestPost (RequestContext _context, IDictionary<string, object> _jsonInput, byte[] _jsonInputData) {
			string id = _context.RequestPath;

			if (string.IsNullOrEmpty (id)) {
				SendEmptyResponse (_context, HttpStatusCode.BadRequest, _jsonInputData, EApiErrorCode.NO_MODULE);
				return;
			}

			if (!AdminWebModules.Instance.IsKnownModule (id)) {
				SendEmptyResponse (_context, HttpStatusCode.BadRequest, _jsonInputData, EApiErrorCode.INVALID_MODULE);
				return;
			}

			AdminWebModules.WebModule module = AdminWebModules.Instance.GetModule (id);

			if (_jsonInput.ContainsKey (propertyPermissionLevelGlobal)) {
				if (!JsonCommons.TryGetJsonField (_jsonInput, propertyPermissionLevelGlobal, out int permissionLevelGlobal)) {
					SendEmptyResponse (_context, HttpStatusCode.BadRequest, _jsonInputData, EApiErrorCode.INVALID_PERMISSION_LEVEL_GLOBAL);
					return;
				}

				module = module.SetLevelGlobal (permissionLevelGlobal);
			}

			if (_jsonInput.TryGetValue (propertyPermissionLevelPerMethod, out object perLevelField)) {
				if (perLevelField is not IDictionary<string, object> perLevelObj) {
					SendEmptyResponse (_context, HttpStatusCode.BadRequest, _jsonInputData, EApiErrorCode.INVALID_PERMISSION_LEVEL_PER_METHOD_PROPERTY);
					return;
				}

				foreach ((string property, object valueObj) in perLevelObj) {
					if (!EnumUtils.TryParse (property, out ERequestMethod method, true)) {
						SendEmptyResponse (_context, HttpStatusCode.BadRequest, _jsonInputData, EApiErrorCode.INVALID_METHOD_NAME);
						return;
					}

					if (module.LevelPerMethod == null || module.LevelPerMethod [(int)method] == AdminWebModules.MethodLevelNotSupported) {
						SendEmptyResponse (_context, HttpStatusCode.BadRequest, _jsonInputData, EApiErrorCode.UNSUPPORTED_METHOD);
						return;
					}

					int permissionLevel;

					if (valueObj is string valueString) {
						if (!valueString.EqualsCaseInsensitive (AdminWebModules.MethodLevelInheritKeyword)) {
							SendEmptyResponse (_context, HttpStatusCode.BadRequest, _jsonInputData, EApiErrorCode.INVALID_PERMISSION_STRING);
							return;
						}						

						permissionLevel = AdminWebModules.MethodLevelInheritGlobal;
					} else if (valueObj is double valueDbl) {
						try {
							permissionLevel = (int)valueDbl;
						} catch (Exception) {
							SendEmptyResponse (_context, HttpStatusCode.BadRequest, _jsonInputData, EApiErrorCode.INVALID_PERMISSION_VALUE);
							return;
						}
					} else {
						SendEmptyResponse (_context, HttpStatusCode.BadRequest, _jsonInputData, EApiErrorCode.INVALID_PERMISSION_VALUE_TYPE);
						return;
					}

					module = module.SetLevelForMethod (method, permissionLevel);
				}
			}

			ModulesInstance.AddModule (module);

			SendEmptyResponse (_context, HttpStatusCode.Created);
		}

		protected override void HandleRestDelete (RequestContext _context) {
			string id = _context.RequestPath;

			bool removed = ModulesInstance.RemoveModule (id);

			SendEmptyResponse (_context, removed ? HttpStatusCode.NoContent : HttpStatusCode.NotFound);
		}

		protected override bool AllowPostWithId => true;

		public override int[] DefaultMethodPermissionLevels () => new[] {
			AdminWebModules.MethodLevelNotSupported,
			AdminWebModules.MethodLevelInheritGlobal,
			AdminWebModules.MethodLevelInheritGlobal,
			AdminWebModules.MethodLevelNotSupported,
			AdminWebModules.MethodLevelInheritGlobal
		};
	}
}