using ConfectioneryContracts.BindingModels;
using ConfectioneryContracts.BusinessLogicsContracts;
using ConfectioneryContracts.SearchModels;
using ConfectioneryContracts.ViewModels;
using ConfectioneryDatabaseImplement.Models;
using ConfectioneryDataModels.Models;
using Microsoft.AspNetCore.Mvc;
using System.Text.Json;

namespace ConfectioneryRestApi.Controllers
{
	[Route("api/[controller]/[action]")]
	[ApiController]
	public class ShopController : Controller
	{
		private readonly ILogger _logger;

		private readonly IShopLogic _logic;

		public ShopController(IShopLogic logic, ILogger<ShopController> logger)
		{
			_logger = logger;
			_logic = logic;
		}

		[HttpGet]
		public List<ShopViewModel>? GetShops() 
		{
			try
			{
				return _logic.ReadList(null);
			}
			catch (Exception ex)
			{
				_logger.LogError(ex, "Ошибка получения списка магазинов");
				throw;
			}
		}

		/// <summary>
		/// Получает на вход айди магазина и по нему возвращает магазин
		/// </summary>
		/// <param name="id">The identifier.</param>
		/// <returns>
		/// Кортеж из магазина, итератора с изделиями которые находятся в магазине и итератора с  количеством этих изделий.
		/// </returns>
		/// Почему изделия и их количество не находятся в одном кортеже? потому что тогда он их не сериализует
		/// и я не знаю почему.
		/// Также, к сожалению, приходится явно присваивать каждое поле из IPastyModel в PastyViewModel, поскольку
		/// нельзя сериализовать интерфейс, и при это нельзя его неявно кастануть к PastryViewModel, даже если в
		/// истинном типе ConfectioneryDatabaseImplement.Pastry такой каст есть.
		/// Сделать же каст в PastryViewModel из IPastryModel нельзя, потому что запрещен каст с интерфейсами.
		/// Единственный нормальный вариант создать отдельную сущность, где объединить изделия и их количество,
		/// и уже ее хранить в магазине и соответственно ее передавать. Но поскольку для этого нужно перелопатить пол-проекта
		/// и получить минус баллы на pr я откажусь от подобной идеи.
		[HttpGet]
		public Tuple<ShopViewModel, IEnumerable<PastryViewModel>, IEnumerable<int>>? GetShopWithPastries(int id)
		{
			try
			{
				
				var shop = _logic.ReadElement(new() { Id = id });
				if (shop == null)
				{
					return null;
				}
				return Tuple.Create(shop,
						shop.Pastries.Select(x => new PastryViewModel () 
						{ 
							Id = x.Value.Item1.Id,
							Price = x.Value.Item1.Price,
							PastryName = x.Value.Item1.PastryName,
						}),
						shop.Pastries.Select(x => x.Value.Item2));
			}
			catch (Exception ex)
			{
				_logger.LogError(ex, "Ошибка получения магазина");
				throw;
			}
		}

		[HttpPost]
		public void CRUDShop(Action action)
		{
			try
			{
				action.Invoke();
			}
			catch (Exception ex)
			{
				_logger.LogError(ex, "Ошибка операции CRUD - {operation} с магазином", action.Method.Name);
				throw;
			}
		}

		[HttpPost]
		public void UpdateShop(ShopBindingModel model) => CRUDShop(() => _logic.Update(model));
		[HttpPost]
		public void CreateShop(ShopBindingModel model) => CRUDShop(() => _logic.Create(model));
		[HttpPost]
		public void DeleteShop(ShopBindingModel model) => CRUDShop(() => _logic.Delete(model));

		[HttpPost]
		public void AddPastryInShop(Tuple<ShopSearchModel, PastryViewModel, int> countPastryForShop)
		{
			CRUDShop(() => _logic.AddPastry(countPastryForShop.Item1, countPastryForShop.Item2, countPastryForShop.Item3));
		}
	}
}