using System;
using System.Collections.Specialized;
using System.Diagnostics.CodeAnalysis;
using System.Net;
using System.Text;
using Utf8Json;
using HttpListenerRequest = SpaceWizards.HttpListener.HttpListenerRequest;
using HttpListenerResponse = SpaceWizards.HttpListener.HttpListenerResponse;

namespace Webserver {
	public static class WebUtils {
		// ReSharper disable once MemberCanBePrivate.Global
		public const string MimePlain = "text/plain";
		public const string MimeHtml = "text/html";
		// ReSharper disable once MemberCanBePrivate.Global
		public const string MimeJson = "application/json";
		
		private static readonly UnityEngine.Profiling.CustomSampler envelopeBuildSampler = UnityEngine.Profiling.CustomSampler.Create ("JSON_EnvelopeBuilding");
		private static readonly UnityEngine.Profiling.CustomSampler netWriteSampler = UnityEngine.Profiling.CustomSampler.Create ("JSON_Write");

		public static void WriteText (HttpListenerResponse _resp, string _text, HttpStatusCode _statusCode = HttpStatusCode.OK, string _mimeType = null) {
			_resp.StatusCode = (int)_statusCode;
			_resp.ContentType = _mimeType ?? MimePlain;
			_resp.ContentEncoding = Encoding.UTF8;

			if (string.IsNullOrEmpty (_text)) {
				_resp.ContentLength64 = 0;
			} else {
				byte[] buf = Encoding.UTF8.GetBytes (_text);
				_resp.ContentLength64 = buf.Length;
				_resp.OutputStream.Write (buf, 0, buf.Length);
			}
		}

		public static bool IsSslRedirected (HttpListenerRequest _req) {
			string proto = _req.Headers ["X-Forwarded-Proto"];
			return !string.IsNullOrEmpty (proto) && proto.Equals ("https", StringComparison.OrdinalIgnoreCase);
		}
		
		public static string GenerateGuid () {
			return Guid.NewGuid ().ToString ();
		}

		[SuppressMessage ("ReSharper", "MemberCanBePrivate.Global")]
		public static void WriteJsonData (HttpListenerResponse _resp, ref JsonWriter _jsonWriter, HttpStatusCode _statusCode = HttpStatusCode.OK) {
			ArraySegment<byte> jsonData = _jsonWriter.GetBuffer ();

			netWriteSampler.Begin ();
			_resp.StatusCode = (int)_statusCode;
			_resp.ContentType = MimeJson;
			_resp.ContentEncoding = Encoding.UTF8;
			_resp.ContentLength64 = jsonData.Count;
			_resp.OutputStream.Write (jsonData.Array!, 0, jsonData.Count);
			netWriteSampler.End ();
		}
		
		
		private static readonly byte[] jsonRawDataKey = JsonWriter.GetEncodedPropertyNameWithBeginObject ("data"); // {"data":

		private static readonly byte[] jsonRawMetaKey = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("meta"); // ,"meta":
		private static readonly byte[] jsonRawMetaServertimeKey = JsonWriter.GetEncodedPropertyNameWithBeginObject ("serverTime"); // {"serverTime":
		private static readonly byte[] jsonRawMetaRequestMethodKey = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("requestMethod"); // ,"requestMethod":
		private static readonly byte[] jsonRawMetaRequestSubpathKey = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("requestSubpath"); // ,"requestSubpath":
		private static readonly byte[] jsonRawMetaRequestBodyKey = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("requestBody"); // ,"requestBody":
		private static readonly byte[] jsonRawMetaErrorCodeKey = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("errorCode"); // ,"errorCode":
		private static readonly byte[] jsonRawMetaExceptionMessageKey = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("exceptionMessage"); // ,"exceptionMessage":
		private static readonly byte[] jsonRawMetaExceptionTraceKey = JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator ("exceptionTrace"); // ,"exceptionTrace":
		
		public static void PrepareEnvelopedResult (out JsonWriter _writer) {
			_writer = new JsonWriter ();
			_writer.WriteRaw (jsonRawDataKey);
		}

		public static void SendEnvelopedResult (RequestContext _context, ref JsonWriter _writer, HttpStatusCode _statusCode = HttpStatusCode.OK,
			byte[] _jsonInputData = null, string _errorCode = null, Exception _exception = null) {
			
			envelopeBuildSampler.Begin ();
			
			_writer.WriteRaw (jsonRawMetaKey);

			_writer.WriteRaw (jsonRawMetaServertimeKey);
			_writer.WriteString (DateTime.Now.ToString ("o"));

			if (!string.IsNullOrEmpty (_errorCode)) {
				_writer.WriteRaw (jsonRawMetaRequestMethodKey);
				_writer.WriteString (_context.Request.HttpMethod);

				_writer.WriteRaw (jsonRawMetaRequestSubpathKey);
				_writer.WriteString (_context.RequestPath);

				_writer.WriteRaw (jsonRawMetaRequestBodyKey);
				if (_jsonInputData != null) {
					_writer.WriteRaw (_jsonInputData);
				} else {
					_writer.WriteNull ();
				}

				_writer.WriteRaw (jsonRawMetaErrorCodeKey);
				_writer.WriteString (_errorCode);

				if (_exception != null) {
					_writer.WriteRaw (jsonRawMetaExceptionMessageKey);
					_writer.WriteString (_exception.Message);

					_writer.WriteRaw (jsonRawMetaExceptionTraceKey);
					_writer.WriteString (_exception.StackTrace);
				}
			}
			
			_writer.WriteEndObject (); // End of "meta" object
			_writer.WriteEndObject (); // End of overall result object
			
			envelopeBuildSampler.End ();

			WriteJsonData (_context.Response, ref _writer, _statusCode);
		}

		public static bool TryGetValue (this NameValueCollection _nameValueCollection, string _name, out string _result) {
			_result = _nameValueCollection [_name];
			return _result != null;
		}
	}
}