using decePubClient.Helpers;
using decePubClient.Models;
using decePubClient.Models.Types;
using Markdig;

namespace decePubClient.Services
{
	public interface IStorage
	{
		ValueTask<List<Message>> GetMessages();
		ValueTask<Message> GetMessage(string messageId);
		ValueTask<string> AddMessages(List<Message> messages);
		ValueTask<string> UpdateMessages(List<Message> messages);
		ValueTask<string> RemoveMessage(string messageId);

		ValueTask<ICollection<ClientLogs>> GetClientLogs();
		ValueTask AddLog(Exception exception, string where);
		ValueTask AddLog(string message, string where);

		ValueTask<bool> RemoveAll(bool includeClientLogs = false);
	}

	public class Storage : IStorage
	{
		readonly IndexedDb _db;
		readonly ILogger<Storage> _logger;

		public Storage(IndexedDb indexedDb, ILogger<Storage> logger)
		{
			_db = indexedDb;
			_logger = logger;
		}

		#region Messagges

		public async ValueTask<Message> GetMessage(string messageId)
		{
			await _db.OpenIndexedDb();
			var message = await _db.GetByKey<string, Message>(nameof(Message), messageId);

			if (message is null)
			{
				var messages = await GetMessages();
				return messages.FirstOrDefault(m => m.MessageId == messageId);
			}

			return message;
		}

		public async ValueTask<List<Message>> GetMessages()
		{
			await _db.OpenIndexedDb();
			var messages = await _db.GetAll<Message>(nameof(Message)) ?? new();


			if (messages.Count == 0)
			{
				messages.Add(new()
				{
					RootMessageId = default,
					MessageId = "992167EE-8947-4823-B1E2-B4E1019B6974",
					User = Faker.GetRandomUser(),
					MessageType = (MessageType)Random.Shared.Next(0, 4),
					Title = default,
					Content = "test di messaggio normale senza titolo",
					Medias = new(),
					CreatedAt = DateTime.UtcNow.AddMinutes(Random.Shared.Next(-5000, 0)),
					IsFavourite = Random.Shared.Next() % 2 == 0,
					BoostsCounter = Random.Shared.Next(0, 100),
					IsBoostedByCurrentUser = Random.Shared.Next() % 2 == 0,
				});
				messages.Add(new()
				{
					RootMessageId = default,
					MessageId = "C5307644-4EA8-4B5F-A240-7CFE4DE34741",
					User = Faker.GetRandomUser(),
					MessageType = (MessageType)Random.Shared.Next(0, 4),
					Title = "sopra la panca la capra canta",
					Content = Markdown.ToHtml("test di messaggio con titolo"),
					Medias = new(),
					CreatedAt = DateTime.UtcNow.AddMinutes(Random.Shared.Next(-5000, 0)),
					IsFavourite = Random.Shared.Next() % 2 == 0,
					BoostsCounter = Random.Shared.Next(0, 100),
					IsBoostedByCurrentUser = Random.Shared.Next() % 2 == 0,
				});
				messages.Add(new()
				{
					RootMessageId = default,
					MessageId = "E473D562-D210-4453-93DA-157D4B90B0D8",
					User = Faker.GetRandomUser(),
					MessageType = (MessageType)Random.Shared.Next(0, 4),
					Title = default,
					Content = "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd test di messaggio con overflow del testo",
					Medias = new(),
					CreatedAt = DateTime.UtcNow.AddMinutes(Random.Shared.Next(-5000, 0)),
					IsFavourite = Random.Shared.Next() % 2 == 0,
					BoostsCounter = Random.Shared.Next(0, 100),
					IsBoostedByCurrentUser = Random.Shared.Next() % 2 == 0,
				});
				messages.Add(new()
				{
					RootMessageId = default,
					MessageId = "adf89dc6-0484-430e-a7c9-fcb1179b0c28",
					User = Faker.GetRandomUser(),
					MessageType = (MessageType)Random.Shared.Next(0, 4),
					Title = default,
					Content = "test video",
					Medias = new()
					{
						new()
						{
							FileName = "8252d175e714db88beb8ee0349dbac90405d3d5148e96e0cfd7de3b0876dbb1b.mp4",
							Url = "https://ihatebeinga.live/media/8252d175e714db88beb8ee0349dbac90405d3d5148e96e0cfd7de3b0876dbb1b.mp4",
							AltText = "https://ihatebeinga.live/media/8252d175e714db88beb8ee0349dbac90405d3d5148e96e0cfd7de3b0876dbb1b.mp4",
							ContentType = "video/mp4"
						}
					},
					CreatedAt = DateTime.UtcNow.AddMinutes(Random.Shared.Next(-5000, 0)),
					IsFavourite = Random.Shared.Next() % 2 == 0,
					BoostsCounter = Random.Shared.Next(0, 100),
					IsBoostedByCurrentUser = Random.Shared.Next() % 2 == 0,
				});
				messages.Add(new()
				{
					RootMessageId = default,
					MessageId = "96025b43-5235-44e9-a3fa-98df31edfbfb",
					User = Faker.GetRandomUser(),
					MessageType = (MessageType)Random.Shared.Next(0, 4),
					Title = default,
					Content = "test immagine",
					Medias = new()
					{
						new()
						{
							FileName = "51a7cf620f1dd096ac5867d5b78f8c71d57a23972b31c64ec124a8c41e77a618.jpg",
							Url = "https://ihatebeinga.live/media/51a7cf620f1dd096ac5867d5b78f8c71d57a23972b31c64ec124a8c41e77a618.jpg",
							AltText = "51a7cf620f1dd096ac5867d5b78f8c71d57a23972b31c64ec124a8c41e77a618.jpg",
							ContentType = "image/jpg"
						}
					},
					CreatedAt = DateTime.UtcNow.AddMinutes(Random.Shared.Next(-5000, 0)),
					IsFavourite = Random.Shared.Next() % 2 == 0,
					BoostsCounter = Random.Shared.Next(0, 100),
					IsBoostedByCurrentUser = Random.Shared.Next() % 2 == 0,
				});
				messages.Add(new()
				{
					RootMessageId = default,
					MessageId = "277ca76b-aaad-4aa6-95fb-25f59e2fddf9",
					User = Faker.GetRandomUser(),
					MessageType = (MessageType)Random.Shared.Next(0, 4),
					Title = default,
					Content = "test audio",
					Medias = new()
					{
						new()
						{
							FileName = "09bfb06d0096975f.mp3",
							Url = "https://cdn.masto.host/mastodonuno/media_attachments/files/107/798/288/501/917/857/original/09bfb06d0096975f.mp3",
							AltText = "09bfb06d0096975f.mp3",
							ContentType = "audio/mp3"
						}
					},
					CreatedAt = DateTime.UtcNow.AddMinutes(Random.Shared.Next(-5000, 0)),
					IsFavourite = Random.Shared.Next() % 2 == 0,
					BoostsCounter = Random.Shared.Next(0, 100),
					IsBoostedByCurrentUser = Random.Shared.Next() % 2 == 0,
				});
				messages.Add(new()
				{
					BoostingUser = Faker.GetRandomBoostingUser(),
					RootMessageId = default,
					MessageId = "de64e520-a7d3-4f31-b5a9-ba236f3b146c",
					User = Faker.GetRandomUser(),
					MessageType = (MessageType)Random.Shared.Next(0, 4),
					Title = default,
					Content = "test immagini",
					Medias = new()
					{
						new()
						{
							FileName = "51a7cf620f1dd096ac5867d5b78f8c71d57a23972b31c64ec124a8c41e77a618.jpg",
							Url = "https://ihatebeinga.live/media/51a7cf620f1dd096ac5867d5b78f8c71d57a23972b31c64ec124a8c41e77a618.jpg",
							AltText = "51a7cf620f1dd096ac5867d5b78f8c71d57a23972b31c64ec124a8c41e77a618.jpg",
							ContentType = "image/jpg"
						},
						new()
						{
							FileName = "51a7cf620f1dd096ac5867d5b78f8c71d57a23972b31c64ec124a8c41e77a618.jpg",
							Url = "https://ihatebeinga.live/media/51a7cf620f1dd096ac5867d5b78f8c71d57a23972b31c64ec124a8c41e77a618.jpg",
							AltText = "51a7cf620f1dd096ac5867d5b78f8c71d57a23972b31c64ec124a8c41e77a618.jpg",
							ContentType = "image/jpg"
						},
						new()
						{
							FileName = "51a7cf620f1dd096ac5867d5b78f8c71d57a23972b31c64ec124a8c41e77a618.jpg",
							Url = "https://ihatebeinga.live/media/51a7cf620f1dd096ac5867d5b78f8c71d57a23972b31c64ec124a8c41e77a618.jpg",
							AltText = "51a7cf620f1dd096ac5867d5b78f8c71d57a23972b31c64ec124a8c41e77a618.jpg",
							ContentType = "image/jpg"
						}
					},
					CreatedAt = DateTime.UtcNow.AddMinutes(Random.Shared.Next(-5000, 0)),
					IsFavourite = Random.Shared.Next() % 2 == 0,
					BoostsCounter = Random.Shared.Next(0,100),
					IsBoostedByCurrentUser = Random.Shared.Next() % 2 == 0,
				});
				messages.Add(new()
				{
					RootMessageId = default,
					MessageId = "de64e520-a7d3-4f31-b5a9-ba236f3b146c",
					User = Faker.GetRandomUser(),
					MessageType = (MessageType)Random.Shared.Next(0, 4),
					Title = default,
					Content = "test immagini",
					Medias = new()
					{
						new()
						{
							FileName = "51a7cf620f1dd096ac5867d5b78f8c71d57a23972b31c64ec124a8c41e77a618.jpg",
							Url = "https://ihatebeinga.live/media/51a7cf620f1dd096ac5867d5b78f8c71d57a23972b31c64ec124a8c41e77a618.jpg",
							AltText = "51a7cf620f1dd096ac5867d5b78f8c71d57a23972b31c64ec124a8c41e77a618.jpg",
							ContentType = "image/jpg"
						},
						new()
						{
							FileName = "51a7cf620f1dd096ac5867d5b78f8c71d57a23972b31c64ec124a8c41e77a618.jpg",
							Url = "https://ihatebeinga.live/media/51a7cf620f1dd096ac5867d5b78f8c71d57a23972b31c64ec124a8c41e77a618.jpg",
							AltText = "51a7cf620f1dd096ac5867d5b78f8c71d57a23972b31c64ec124a8c41e77a618.jpg",
							ContentType = "image/jpg"
						},
						new()
						{
							FileName = "51a7cf620f1dd096ac5867d5b78f8c71d57a23972b31c64ec124a8c41e77a618.jpg",
							Url = "https://ihatebeinga.live/media/51a7cf620f1dd096ac5867d5b78f8c71d57a23972b31c64ec124a8c41e77a618.jpg",
							AltText = "51a7cf620f1dd096ac5867d5b78f8c71d57a23972b31c64ec124a8c41e77a618.jpg",
							ContentType = "image/jpg"
						}
					},
					CreatedAt = DateTime.UtcNow.AddMinutes(Random.Shared.Next(-5000, 0)),
					IsFavourite = Random.Shared.Next() % 2 == 0,
					BoostsCounter = Random.Shared.Next(0,100),
					IsBoostedByCurrentUser = Random.Shared.Next() % 2 == 0,
				});
			}

			messages = messages.OrderByDescending(m => m.CreatedAt).ToList();

			return messages;
		}

		public async ValueTask<string> AddMessages(List<Message> messages)
		{
			await _db.OpenIndexedDb();
			var result = await _db.AddItems(nameof(Message), messages);
			_logger.LogInformation(result);
			return result;
		}

		public async ValueTask<string> UpdateMessages(List<Message> messages)
		{
			await _db.OpenIndexedDb();
			var result = await _db.UpdateItems(nameof(Message), messages);
			_logger.LogInformation(result);
			return result;

		}

		public async ValueTask<string> RemoveMessage(string messageId)
		{
			await _db.OpenIndexedDb();
			var result = await _db.DeleteByKey(nameof(Message), messageId);
			_logger.LogInformation(result);
			return result;
		}

		#endregion

		#region Logs

		public async ValueTask<ICollection<ClientLogs>> GetClientLogs()
		{
			try
			{
				_logger.LogInformation($"opening on {nameof(GetClientLogs)}");
				await _db.OpenIndexedDb();
				return (await _db.GetAll<ClientLogs>(nameof(ClientLogs))).OrderByDescending(cl => cl.TimeStamp).ToArray();
			}
			catch (Exception ex)
			{
				_logger.LogError(ex, ex.Message);
				return new List<ClientLogs>();
			}
		}

		public async ValueTask AddLog(Exception exception, string where)
		{
			try
			{
				await _db.OpenIndexedDb();

				var maxKey = await _db.GetMaxKey<long>(nameof(ClientLogs));
				var minKey = await _db.GetMinKey<long>(nameof(ClientLogs));

				if (minKey - maxKey <= -100)
					for (var i = minKey; i < minKey + 50; i++)
						_ = await _db.DeleteByKey(nameof(ClientLogs), i);

				maxKey += 1;
				var result = await _db.AddItems<ClientLogs>(nameof(ClientLogs), new()
				{
					new()
					{
						Id = maxKey,
						Where = where,
						Exception = new()
						{
							Type = exception.GetType().ToString(),
							HelpLink = exception.HelpLink,
							HResult = exception.HResult.ToString(),
							InnerExceptionMessage = exception.InnerException?.Message,
							Message = exception.Message,
							Source = exception.Source,
							StackTrace = exception.StackTrace,
							TargetSiteName = exception.TargetSite?.Name
						}
					}
				});
				_logger.LogDebug($"{nameof(AddLog)}() add logs result = {result}");
			}
			catch (Exception ex)
			{
				_logger.LogError(ex, ex.Message);
			}
		}

		public async ValueTask AddLog(string message, string where)
		{
			try
			{
				await _db.OpenIndexedDb();

				var maxKey = await _db.GetMaxKey<long>(nameof(ClientLogs));
				var minKey = await _db.GetMinKey<long>(nameof(ClientLogs));

				if (minKey - maxKey <= -100)
					for (var i = minKey; i < minKey + 50; i++)
						_ = await _db.DeleteByKey(nameof(ClientLogs), i);

				maxKey += 1;
				var result = await _db.AddItems<ClientLogs>(nameof(ClientLogs), new()
				{
					new()
					{
						Id = maxKey,
						Where = where,
						Exception = null,
						WarningMessage = message
					}
				});
			}
			catch (Exception ex)
			{
				_logger.LogError(ex, ex.Message);
			}
		}

		#endregion

		public async ValueTask<bool> RemoveAll(bool includeClientLogs = false)
		{
			try
			{
				_logger.LogInformation($"opening on {nameof(RemoveAll)}");
				await _db.OpenIndexedDb();
				_ = await _db.DeleteAll(nameof(Message));
				if (includeClientLogs)
					_ = await _db.DeleteAll(nameof(ClientLogs));

				return true;
			}
			catch (Exception ex)
			{
				_logger.LogError(ex, ex.Message);
				return default;
			}
		}

	}
}
