using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace OnlineSalesAutoCrop.CoreAPI.Configurations
{
	/// <summary>
	/// 
	/// </summary>
	/// <param name="Acceptable"></param>
	/// <param name="Status"></param>
	/// <param name="Message"></param>
	public record Result(bool Acceptable, Status Status, string Message);

	/// <summary>
	/// 
	/// </summary>
	public enum Status
	{
		/// <summary>
		/// 
		/// </summary>
		GENUINE,

		/// <summary>
		/// 
		/// </summary>
		FAKE,

		/// <summary>
		/// 
		/// </summary>
		NOTSUPPORTED
	}

	/// <summary>
	/// 
	/// </summary>
	public abstract class FileFormatDescriptor
	{
		/// <summary>
		/// 
		/// </summary>
		protected FileFormatDescriptor()
		{
			Initialize();
			MaxMagicNumberLength = MagicNumbers.Max(m => m.Length);
		}

		/// <summary>
		/// 
		/// </summary>
		protected abstract void Initialize();

		/// <summary>
		///     
		/// </summary>
		protected HashSet<string> Extensions { get; } = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		/// <summary>
		///     
		/// </summary>
		protected List<byte[]> MagicNumbers { get; } = [];

		/// <summary>
		///     
		/// </summary>
		protected int MaxMagicNumberLength { get; }

		/// <summary>
		///     
		/// </summary>
		protected string TypeName { get; set; }

		/// <summary>
		/// 
		/// </summary>
		/// <param name="extension"></param>
		/// <returns></returns>
		public bool IsIncludedExtension(string extension) => Extensions.Contains(extension);

		/// <summary>
		/// 
		/// </summary>
		/// <param name="file"></param>
		/// <returns></returns>
		public Result Validate(IFormFile file)
		{
			using var stream = file.OpenReadStream();
			Span<byte> initialBytes = stackalloc byte[MaxMagicNumberLength];
			_ = stream.Read(initialBytes);
			foreach (var magicNumber in MagicNumbers)
			{
				if (initialBytes[..magicNumber.Length].SequenceCompareTo(magicNumber) == 0)
				{
					return new Result(true, Status.GENUINE, $"{Status.GENUINE} {TypeName}");
				}
			}

			return new Result(false, Status.FAKE, $"{Status.FAKE} {TypeName}!");
		}
	}

	/// <summary>
	/// 
	/// </summary>
	public class ImageFile : FileFormatDescriptor
	{
		/// <summary>
		/// 
		/// </summary>
		protected override void Initialize()
		{
			TypeName = "IMAGE FILE";
			Extensions.UnionWith([".jpeg", ".jpg", ".png"]);
			MagicNumbers.AddRange(
			[
			 [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A],
			 [0xFF, 0xD8, 0xFF, 0xE0],
			 [0xFF, 0xD8, 0xFF, 0xE1],
			 [0xFF, 0xD8, 0xFF, 0xE2],
			 [0xFF, 0xD8, 0xFF, 0xE3]
			]);
		}
	}

	/// <summary>
	/// 
	/// </summary>
	public class PdfFile : FileFormatDescriptor
	{
		/// <summary>
		/// 
		/// </summary>
		protected override void Initialize()
		{
			TypeName = "PDF FILE";
			Extensions.Add(".pdf");
			MagicNumbers.Add([0x25, 0x50, 0x44, 0x46]);
		}
	}

	/// <summary>
	/// 
	/// </summary>
	public class ExcelFile : FileFormatDescriptor
	{
		/// <summary>
		/// 
		/// </summary>
		protected override void Initialize()
		{
			TypeName = "EXCEL FILE";
			Extensions.UnionWith([".xls", ".doc", ".ppt"]);
			MagicNumbers.Add([0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1]);
		}
	}

	/// <summary>
	/// 
	/// </summary>
	public class ExcelxFile : FileFormatDescriptor
	{
		/// <summary>
		/// 
		/// </summary>
		protected override void Initialize()
		{
			TypeName = "EXCEL FILE";
			Extensions.UnionWith([".xlsx"]);
			MagicNumbers.Add([0x50, 0x4B, 0x03, 0x04]);
		}
	}

	/// <summary>
	/// 
	/// </summary>
	public static class ImageFileValidator
	{
		private static readonly List<FileFormatDescriptor> AllowedFormats = [new ImageFile()];

		/// <summary>
		/// 
		/// </summary>
		/// <param name="file"></param>
		/// <returns></returns>
		public static Result Validate(IFormFile file)
		{
			var fileExtension = Path.GetExtension(file.FileName);
			var targetType = AllowedFormats.FirstOrDefault(x => x.IsIncludedExtension(fileExtension));

			if (targetType is null)
			{
				return new Result(false, Status.NOTSUPPORTED, $"{Status.NOTSUPPORTED}");
			}

			return targetType.Validate(file);
		}
	}

	/// <summary>
	/// 
	/// </summary>
	public static class PdfFileValidator
	{
		private static readonly List<FileFormatDescriptor> AllowedFormats = [new PdfFile()];

		/// <summary>
		/// 
		/// </summary>
		/// <param name="file"></param>
		/// <returns></returns>
		public static Result Validate(IFormFile file)
		{
			var fileExtension = Path.GetExtension(file.FileName);
			var targetType = AllowedFormats.FirstOrDefault(x => x.IsIncludedExtension(fileExtension));

			if (targetType is null)
			{
				return new Result(false, Status.NOTSUPPORTED, $"{Status.NOTSUPPORTED}");
			}

			return targetType.Validate(file);
		}
	}

	/// <summary>
	/// 
	/// </summary>
	public static class ExcelFileValidator
	{
		private static readonly List<FileFormatDescriptor> AllowedFormats = [new ExcelFile()];

		/// <summary>
		/// 
		/// </summary>
		/// <param name="file"></param>
		/// <returns></returns>
		public static Result Validate(IFormFile file)
		{
			var fileExtension = Path.GetExtension(file.FileName);
			var targetType = AllowedFormats.FirstOrDefault(x => x.IsIncludedExtension(fileExtension));

			if (targetType is null)
			{
				return new Result(false, Status.NOTSUPPORTED, $"{Status.NOTSUPPORTED}");
			}

			return targetType.Validate(file);
		}
	}

	/// <summary>
	/// 
	/// </summary>
	public static class ExcelxFileValidator
	{
		private static readonly List<FileFormatDescriptor> AllowedFormats = [new ExcelxFile()];

		/// <summary>
		/// 
		/// </summary>
		/// <param name="file"></param>
		/// <returns></returns>
		public static Result Validate(IFormFile file)
		{
			var fileExtension = Path.GetExtension(file.FileName);
			var targetType = AllowedFormats.FirstOrDefault(x => x.IsIncludedExtension(fileExtension));

			if (targetType is null)
			{
				return new Result(false, Status.NOTSUPPORTED, $"{Status.NOTSUPPORTED}");
			}

			return targetType.Validate(file);
		}
	}
}
