using System.Net;
using System.Text.Json;

namespace DHT.Utils.Http {
	public static class JsonExtensions {
		public static bool HasKey(this JsonElement json, string key) {
			return json.TryGetProperty(key, out _);
		}

		public static JsonElement RequireObject(this JsonElement json, string key, string? path = null) {
			if (json.TryGetProperty(key, out var result)) {
				return result;
			}
			else {
				throw new HttpException(HttpStatusCode.BadRequest, "Expected key '" + (path == null ? key : path + '.' + key) + "' to be an object.");
			}
		}

		public static JsonElement.ArrayEnumerator RequireArray(this JsonElement json, string key, string? path = null) {
			if (json.TryGetProperty(key, out var result) && result.ValueKind == JsonValueKind.Array) {
				return result.EnumerateArray();
			}
			else {
				throw new HttpException(HttpStatusCode.BadRequest, "Expected key '" + (path == null ? key : path + '.' + key) + "' to be an array.");
			}
		}

		public static string RequireString(this JsonElement json, string key, string path) {
			if (json.TryGetProperty(key, out var result) && result.ValueKind == JsonValueKind.String) {
				return result.ToString()!;
			}
			else {
				throw new HttpException(HttpStatusCode.BadRequest, "Expected key '" + path + '.' + key + "' to be a string.");
			}
		}

		public static bool RequireBool(this JsonElement json, string key, string path) {
			if (json.TryGetProperty(key, out var result) && result.ValueKind is JsonValueKind.True or JsonValueKind.False) {
				return result.GetBoolean();
			}
			else {
				throw new HttpException(HttpStatusCode.BadRequest, "Expected key '" + path + '.' + key + "' to be a boolean.");
			}
		}

		public static int RequireInt(this JsonElement json, string key, string path, int min = int.MinValue, int max = int.MaxValue) {
			if (json.TryGetProperty(key, out var result) && result.ValueKind == JsonValueKind.Number && result.TryGetInt32(out var i) && i >= min && i <= max) {
				return i;
			}
			else if (min == int.MinValue && max == int.MaxValue) {
				throw new HttpException(HttpStatusCode.BadRequest, "Expected key '" + path + '.' + key + "' to be a 32-bit integer.");
			}
			else if (max == int.MaxValue) {
				throw new HttpException(HttpStatusCode.BadRequest, "Expected key '" + path + '.' + key + "' to be a 32-bit integer (> " + min + ").");
			}
			else if (min == int.MinValue) {
				throw new HttpException(HttpStatusCode.BadRequest, "Expected key '" + path + '.' + key + "' to be a 32-bit integer (< " + max + ").");
			}
			else {
				throw new HttpException(HttpStatusCode.BadRequest, "Expected key '" + path + '.' + key + "' to be an integer (between " + min + " and " + max + ").");
			}
		}

		public static long RequireLong(this JsonElement json, string key, string path, long min = long.MinValue, long max = long.MaxValue) {
			if (json.TryGetProperty(key, out var result) && result.ValueKind == JsonValueKind.Number && result.TryGetInt64(out var l) && l >= min && l <= max) {
				return l;
			}
			else if (min == long.MinValue && max == long.MaxValue) {
				throw new HttpException(HttpStatusCode.BadRequest, "Expected key '" + path + '.' + key + "' to be a 64-bit integer.");
			}
			else if (max == long.MaxValue) {
				throw new HttpException(HttpStatusCode.BadRequest, "Expected key '" + path + '.' + key + "' to be a 64-bit integer (> " + min + ").");
			}
			else if (min == long.MinValue) {
				throw new HttpException(HttpStatusCode.BadRequest, "Expected key '" + path + '.' + key + "' to be a 64-bit integer (< " + max + ").");
			}
			else {
				throw new HttpException(HttpStatusCode.BadRequest, "Expected key '" + path + '.' + key + "' to be an integer (between " + min + " and " + max + ").");
			}
		}

		public static ulong RequireSnowflake(this JsonElement json, string key, string path) {
			if (ulong.TryParse(json.RequireString(key, path), out var snowflake)) {
				return snowflake;
			}
			else {
				throw new HttpException(HttpStatusCode.BadRequest, "Expected key '" + path + '.' + key + "' to be a Snowflake ID (64-bit unsigned integer in a string).");
			}
		}
	}
}
