using BarBusinessLogic.MailWorker;
using BarContracts.BindingModels;
using BarContracts.BusinessLogicContracts;
using BarContracts.SearchModels;
using BarContracts.StoragesContracts;
using BarContracts.ViewModels;
using BarDataModels.Enums;
using Microsoft.Extensions.Logging;

namespace BarBusinessLogic.BusinessLogics
{
    public class OrderLogic : IOrderLogic
    {
        private readonly ILogger _logger;

        private readonly IOrderStorage _orderStorage;

        private readonly AbstractMailWorker _mailLogic;

        static readonly object _locker = new object();

        public OrderLogic(ILogger<CocktailLogic> Logger, IOrderStorage OrderStorage, AbstractMailWorker MailLogic)
        {
            _logger = Logger;
            _orderStorage = OrderStorage;
            _mailLogic = MailLogic;
        }

        public List<OrderViewModel>? ReadList(OrderSearchModel? Model)
        {
            _logger.LogInformation("ReadList. ClientId: {ClientId}. Status: {Status}. ImplementerId: {ImplementerId}. DateFrom: {DateFrom}. DateTo: {DateTo}. OrderId: {Id}",
                Model?.ClientId, Model?.Status, Model?.ImplementerId, Model?.DateFrom, Model?.DateTo, Model?.Id);

            var List = Model is null ? _orderStorage.GetFullList() : _orderStorage.GetFilteredList(Model);
            if (List is null)
            {
                _logger.LogWarning("ReadList return null list");
                return null;
            }
            _logger.LogInformation("ReadList. Count: {Count}", List.Count);
            return List;
        }

        public bool CreateOrder(OrderBindingModel Model)
        {
            CheckModel(Model);

            if (Model.Status != OrderStatus.Undefined)
            {
                _logger.LogWarning("Invalid order status");
                return false;
            }

            Model.Status = OrderStatus.Accepted;

            var CreatedOrder = _orderStorage.Insert(Model);

            if (CreatedOrder == null)
            {
                _logger.LogWarning("Insert operation failed");
                return false;
            }

            Task.Run(() => _mailLogic.SendMailAsync(new MailSendInfoBindingModel
            {
                MailAddress = CreatedOrder.ClientEmail,
                Subject = $"Изменение статуса заказа номер {CreatedOrder.Id}",
                Text = $"Ваш заказ номер {CreatedOrder.Id} на коктейль {CreatedOrder.CocktailName} от {CreatedOrder.DateCreate} на сумму {CreatedOrder.Sum} принят."
            }));

            return true;
        }

        private bool ChangeOrderStatus(OrderBindingModel Model, OrderStatus NewStatus)
        {
            CheckModel(Model, false);
            var Order = _orderStorage.GetElement(new OrderSearchModel { Id = Model.Id });
            if (Order == null)
            {
                _logger.LogWarning("Change status operation failed. Order not found");
                return false;
            }

            if (Order.Status + 1 != NewStatus)
            {
                _logger.LogWarning("Change status operation failed. Incorrect new status: {NewStatus}. Current status: {currStatus}",
                    NewStatus, Order.Status);
                return false;
            }

            Model.CocktailId = Order.CocktailId;
            Model.ClientId = Order.ClientId;
            if (!Model.ImplementerId.HasValue) Model.ImplementerId = Order.ImplementerId;
            Model.Count = Order.Count;
            Model.Sum = Order.Sum;
            Model.DateCreate = Order.DateCreate;
            Model.Status = NewStatus;

            if (Model.Status == OrderStatus.Ready)
                Model.DateImplement = DateTime.Now;
            else
                Model.DateImplement = Order.DateImplement;

            if (_orderStorage.Update(Model) == null)
            {
                _logger.LogWarning("Change status operation failed");
                return false;
            }

            string DateInfo = Model.DateImplement.HasValue ? $"Дата выполнения: {Model.DateImplement}" : "";
            Task.Run(() => _mailLogic.SendMailAsync(new MailSendInfoBindingModel
            {
                MailAddress = Order.ClientEmail,
                Subject = $"Изменение статуса заказа номер {Order.Id}",
                Text = $"Ваш заказ номер {Order.Id} на коктейль {Order.CocktailName} от {Order.DateCreate} на сумму {Order.Sum}. Статус изменен на {NewStatus}. {DateInfo}"
            }));

            return true;
        }

        public bool TakeOrderInWork(OrderBindingModel Model)
        {
            lock (_locker)
            {
                return ChangeOrderStatus(Model, OrderStatus.BeingProcessed);
            }
        }

        public bool FinishOrder(OrderBindingModel Model)
        {
            return ChangeOrderStatus(Model, OrderStatus.Ready);
        }

        public bool DeliveryOrder(OrderBindingModel Model)
        {
            return ChangeOrderStatus(Model, OrderStatus.Delivered);
        }

        private void CheckModel(OrderBindingModel Model, bool WithParams = true)
        {
            if (Model == null)
                throw new ArgumentNullException(nameof(Model));

            if (!WithParams)
                return;

            if (Model.Count <= 0)
                throw new ArgumentNullException("Количество коктейльов в заказе быть больше 0", nameof(Model.Count));

            if (Model.Sum <= 0)
                throw new ArgumentNullException("Стоимость заказа должна быть больше 0", nameof(Model.Sum));

            _logger.LogInformation("Order. CocktailId:{CocktailId}. Count:{Count}. Sum:{Sum}. " +
                "Status:{Status}. DateCreate:{DateCreate}. DateImplement:{DateImplement}. Id: {Id}",
                Model.CocktailId, Model.Count, Model.Sum, Model.Status, Model.DateCreate,
                Model.DateImplement, Model.Id);
        }

        public OrderViewModel? ReadElement(OrderSearchModel Model)
        {
            if (Model == null)
            {
                throw new ArgumentNullException(nameof(Model));
            }

            _logger.LogInformation("ReadElement. ClientId: {ClientId}. Status: {Status}. ImplementerId: {ImplementerId}. DateFrom: {DateFrom}. DateTo: {DateTo}. OrderId: {Id}",
                Model.ClientId, Model.Status, Model.ImplementerId, Model.DateFrom, Model.DateTo, Model.Id);

            var Order = _orderStorage.GetElement(Model);

            if (Order == null)
            {
                _logger.LogWarning("ReadElement element not found");
                return null;
            }

            _logger.LogInformation("ReadElement find. Id: {Id}", Order.Id);
            return Order;
        }
    }
}
