using MediatR;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;
using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure;
using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency;
using Ordering.API.Application.Commands;
using Ordering.API.Application.IntegrationCommands.Commands;
using Ordering.API.Application.IntegrationEvents;
using Ordering.Domain.Exceptions;
using System.Threading.Tasks;

namespace Ordering.API.Application.Sagas
{
    /// <summary>
    /// Saga for handling the place order process
    /// and which is started once the basket.api has 
    /// successfully processed the items ordered.
    /// Saga provides a period of grace to give the customer
    /// the opportunity to cancel the order before proceeding
    /// with the validations.
    /// </summary>
    public class OrderProcessSaga : OrderSaga,
        IIntegrationEventHandler<ConfirmGracePeriodCommandMsg>,
        IAsyncRequestHandler<CancelOrderCommand, bool>,
        IAsyncRequestHandler<ShipOrderCommand, bool>
    {

        public OrderProcessSaga(
            OrderingContext orderingContext)
            : base(orderingContext)
        {
        }        

        /// <summary>
        /// Command handler which confirms that the grace period
        /// has been completed and order has not been cancelled.
        /// If so, the process continues for validation. 
        /// </summary>
        /// <param name="event">
        /// Integration command message which is sent by a saga 
        /// scheduler which provides the sagas that its grace 
        /// period has completed.
        /// </param>
        /// <returns></returns>
        public async Task Handle(ConfirmGracePeriodCommandMsg @event)
        {
            var orderSaga = FindSagaById(@event.OrderId);
            CheckValidSagaId(orderSaga);

            if (orderSaga.OrderStatus != OrderStatus.Cancelled)
            {
                orderSaga.SetAwaitingValidationStatus();

                await SaveChangesAsync();
            }
        }
        
        /// <summary>
        /// Handler which processes the command when
        /// customer executes cancel order from app
        /// </summary>
        /// <param name="command"></param>
        /// <returns></returns>
        public async Task<bool> Handle(CancelOrderCommand command)
        {
            var result = false;
            var orderSaga = FindSagaById(command.OrderNumber);
            CheckValidSagaId(orderSaga);

            // Not possible to cancel order when 
            // it has already been shipped
            if (orderSaga.GetOrderStatusId() != OrderStatus.Cancelled.Id
                || orderSaga.GetOrderStatusId() != OrderStatus.Shipped.Id)
            {
                orderSaga.SetCancelStatus();
                result = await SaveChangesAsync();
            }
            return result;
        }

        /// <summary>
        /// Handler which processes the command when
        /// administrator executes ship order from app
        /// </summary>
        /// <param name="command"></param>
        /// <returns></returns>
        public async Task<bool> Handle(ShipOrderCommand command)
        {
            var result = false;
            var orderSaga = FindSagaById(command.OrderNumber);
            CheckValidSagaId(orderSaga);

            // Only ship order when 
            // its status is paid
            if (orderSaga.GetOrderStatusId() == OrderStatus.Paid.Id)
            {
                orderSaga.SetShippedStatus();
                result = await SaveChangesAsync();
            }
            return result;
        }

        private void CheckValidSagaId(Order orderSaga)
        {
            if (orderSaga is null)
            {
                throw new OrderingDomainException("Not able to process order saga event. Reason: no valid orderId");
            }
        }

        #region CommandHandlerIdentifiers

        public class CancelOrderCommandIdentifiedHandler : IdentifierCommandHandler<CancelOrderCommand, bool>
        {
            public CancelOrderCommandIdentifiedHandler(IMediator mediator, IRequestManager requestManager) : base(mediator, requestManager)
            {
            }

            protected override bool CreateResultForDuplicateRequest()
            {
                return true;                // Ignore duplicate requests for processing order.
            }
        }

        public class ShipOrderCommandIdentifiedHandler : IdentifierCommandHandler<ShipOrderCommand, bool>
        {
            public ShipOrderCommandIdentifiedHandler(IMediator mediator, IRequestManager requestManager) : base(mediator, requestManager)
            {
            }

            protected override bool CreateResultForDuplicateRequest()
            {
                return true;                // Ignore duplicate requests for processing order.
            }
        }

        #endregion
    }
}