using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Primitives;
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net.Http.Headers;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;

namespace OnlineSalesAutoCrop.CoreAPI
{
	/// <summary>
	/// 
	/// </summary>
	public static class ExtensionMethods
	{
		/// <summary>
		/// 
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="principal"></param>
		/// <param name="type"></param>
		/// <returns></returns>
		public static T GetClaimValue<T>(this ClaimsPrincipal principal, string type)
		{
			if (principal?.Identity == null || !principal.Identity.IsAuthenticated)
				return typeof(T) == typeof(string) ? ((T)Convert.ChangeType(string.Empty, typeof(T))) : default;

			Claim claim = principal.Claims.SingleOrDefault(p => p.Type == type);
			if (string.IsNullOrEmpty(claim?.Value))
				return typeof(T) == typeof(string) ? ((T)Convert.ChangeType(string.Empty, typeof(T))) : default;

			return (T)Convert.ChangeType(claim.Value, typeof(T));
		}

		/// <summary>
		/// 
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="session"></param>
		/// <param name="key"></param>
		/// <param name="value"></param>
		/// <returns></returns>
		public static async Task<bool> SetModulesToSession<T>(this ISession session, string key, T value)
		{
			ArgumentNullException.ThrowIfNull(session);

			try
			{
				string jsonString = Newtonsoft.Json.JsonConvert.SerializeObject(value);
				session.SetString(key: key, value: jsonString);
				await session.CommitAsync();

				return true;
			}
			catch
			{
				return false;
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="context"></param>
		/// <param name="moduleId"></param>
		/// <returns></returns>
		public static async Task<bool> IsPermitted(this HttpContext context, string moduleId)
		{
			if (context?.Session is null)
				return false;

			string key = context.GetAuthenticationToken();
			if (string.IsNullOrEmpty(key))
				return false;

			await context.Session.LoadAsync();
			string moduleIds = context.Session.GetString(key: key);
			if (string.IsNullOrEmpty(moduleIds))
			{
				return false;
			}

			try
			{
				List<string> value = Newtonsoft.Json.JsonConvert.DeserializeObject<List<string>>(moduleIds);
				return value.Contains(moduleId);
			}
			catch
			{
				return false;
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="value"></param>
		/// <param name="moduleId"></param>
		/// <returns></returns>
		public static async Task<bool> IsPermitted(this List<string> value, string moduleId)
		{
			if (value?.Count <= 0)
				return false;

			try
			{
				return value.Contains(moduleId);
			}
			catch
			{
				return false;
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="context"></param>
		/// <returns></returns>
		public static bool IsSessionExpired(this HttpContext context)
		{
			if (context?.Session is null)
				return true;

			string key = context.GetAuthenticationToken();
			if (string.IsNullOrEmpty(key))
				return true;

			string moduleIds = context.Session.GetString(key: key);
			if (string.IsNullOrEmpty(moduleIds))
				return true;

			return false;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="context"></param>
		/// <returns></returns>
		public static async Task<bool> ClearSessionAsync(this HttpContext context)
		{
			if (context?.Session is null)
				return false;

			context.Session.Clear();
			await context.Session.CommitAsync();
			return true;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="context"></param>
		/// <returns></returns>
		public static string GetAuthenticationToken(this HttpContext context)
		{
			if (context == null)
			{
				return null;
			}

			// Attempt to retrieve the "Authorization" header
			if (context.Request.Headers.TryGetValue("Authorization", out StringValues authHeaderValues))
			{
				var authHeader = authHeaderValues.FirstOrDefault();
				// Parse the header using AuthenticationHeaderValue
				if (authHeader != null && AuthenticationHeaderValue.TryParse(authHeader, out AuthenticationHeaderValue authHeaderValue))
				{
					// For Bearer tokens, the scheme is "Bearer" and the parameter is the token
					if (authHeaderValue.Scheme.Equals("Bearer", StringComparison.OrdinalIgnoreCase))
					{
						return authHeaderValue.Parameter;
					}
					// For Basic authentication, the scheme is "Basic" and the parameter is base64 encoded credentials
					else if (authHeaderValue.Scheme.Equals("Basic", StringComparison.OrdinalIgnoreCase))
					{
						// You would then typically decode this parameter to get username and password
						return authHeaderValue.Parameter;
					}
				}
			}
			return null;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="context"></param>
		/// <returns></returns>
		public static string GetIpAddress(this HttpContext context)
		{
			string ip = GetHeaderValueAs<string>(context: context, headerName: "X-Forwarded-For").SplitCsv().FirstOrDefault();
			if (string.IsNullOrEmpty(ip) && context?.Connection?.RemoteIpAddress != null)
			{
				ip = context?.Connection?.RemoteIpAddress.ToString();
				if (string.IsNullOrEmpty(ip))
				{
					ip = GetHeaderValueAs<string>(context: context, headerName: "REMOTE_ADDR");
					if (string.IsNullOrEmpty(ip))
						ip = context.Request?.Host.Value;
				}
			}

			return ip ?? string.Empty;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="context"></param>
		/// <param name="headerName"></param>
		/// <returns></returns>
		private static T GetHeaderValueAs<T>(HttpContext context, string headerName)
		{
			if (context.Request?.Headers?.TryGetValue(headerName, out StringValues values) ?? false)
			{
				string rawValues = values.ToString();

				if (!string.IsNullOrEmpty(rawValues))
					return (T)Convert.ChangeType(values.ToString(), typeof(T));
			}
			return default;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="csvList"></param>
		/// <returns></returns>
		private static List<string> SplitCsv(this string csvList)
		{
			if (string.IsNullOrWhiteSpace(csvList))
				return [];

			return [.. csvList.TrimEnd(',').Split(',').AsEnumerable().Select(s => s.Trim())];
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="textToCompress"></param>
		/// <returns></returns>
		public static string CompressString(this string textToCompress)
		{
			byte[] bytes = Encoding.UTF8.GetBytes(textToCompress);
			using var ms = new MemoryStream();
			using (BrotliStream brotli = new(ms, CompressionLevel.Optimal))
			{
				brotli.Write(bytes, 0, bytes.Length);
			}
			return Convert.ToBase64String(ms.ToArray());
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="textToDecompress"></param>
		/// <returns></returns>
		public static string DecompressString(this string textToDecompress)
		{
			byte[] bytes = Convert.FromBase64String(textToDecompress);
			using var ms = new MemoryStream(bytes);
			using var Brotli = new BrotliStream(ms, CompressionMode.Decompress);
			using var sr = new StreamReader(Brotli);
			return sr.ReadToEnd();
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="textToDecompress"></param>
		/// <returns></returns>
		public static List<string> DecompressModuleIds(this string textToDecompress)
		{
			return textToDecompress.DecompressString().SplitCsv();
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="logger"></param>
		/// <param name="exception"></param>
		public static void LogError(this ILogger logger, Exception exception)
		{
			logger.LogError(exception: exception, message: string.Empty);
		}
	}
}