using Grand.Business.Core.Commands.Checkout.Orders;
using Grand.Business.Core.Interfaces.Checkout.Orders;
using Grand.Business.Core.Interfaces.Checkout.Payments;
using Grand.Business.Core.Interfaces.Checkout.Shipping;
using Grand.Business.Core.Interfaces.Common.Directory;
using Grand.Business.Core.Interfaces.Common.Localization;
using Grand.Business.Core.Interfaces.Common.Pdf;
using Grand.Domain.Customers;
using Grand.Domain.Orders;
using Grand.Domain.Payments;
using Grand.Domain.Shipping;
using Grand.Infrastructure;
using Grand.Web.Commands.Models.Orders;
using Grand.Web.Common.Controllers;
using Grand.Web.Common.Filters;
using Grand.Web.Common.Page;
using Grand.Web.Common.Security.Authorization;
using Grand.Web.Events;
using Grand.Web.Extensions;
using Grand.Web.Features.Models.Orders;
using Grand.Web.Models.Orders;
using MediatR;
using Microsoft.AspNetCore.Mvc;

namespace Grand.Web.Controllers;

[DenySystemAccount]
public class OrderController : BasePublicController
{
    #region Constructors

    public OrderController(IOrderService orderService,
        IWorkContext workContext,
        IPaymentService paymentService,
        IPaymentTransactionService paymentTransactionService,
        ITranslationService translationService,
        IGroupService groupService,
        IMediator mediator,
        OrderSettings orderSettings)
    {
        _orderService = orderService;
        _workContext = workContext;
        _paymentService = paymentService;
        _paymentTransactionService = paymentTransactionService;
        _translationService = translationService;
        _groupService = groupService;
        _mediator = mediator;
        _orderSettings = orderSettings;
    }

    #endregion

    #region Fields

    private readonly IOrderService _orderService;
    private readonly IWorkContext _workContext;
    private readonly IPaymentService _paymentService;
    private readonly IPaymentTransactionService _paymentTransactionService;
    private readonly ITranslationService _translationService;
    private readonly IGroupService _groupService;
    private readonly IMediator _mediator;
    private readonly OrderSettings _orderSettings;

    #endregion

    #region Utilities

    protected virtual bool IsRequestBeingRedirected {
        get {
            var response = HttpContext.Response;
            return new List<int> { 301, 302 }.Contains(response.StatusCode);
        }
    }

    protected virtual bool IsPostBeingDone {
        get => HttpContext.Items["grand.IsPOSTBeingDone"] != null &&
               Convert.ToBoolean(HttpContext.Items["grand.IsPOSTBeingDone"]);
        set => HttpContext.Items["grand.IsPOSTBeingDone"] = value;
    }

    #endregion

    #region Methods

    //My account / Orders
    [HttpGet]
    [CustomerGroupAuthorize(SystemCustomerGroupNames.Registered)]
    public virtual async Task<IActionResult> CustomerOrders(OrderPagingModel command)
    {
        var model = await _mediator.Send(new GetCustomerOrderList {
            Customer = _workContext.CurrentCustomer,
            Language = _workContext.WorkingLanguage,
            Store = _workContext.CurrentStore,
            Command = command
        });
        return View(model);
    }

    //My account / Order details page
    [HttpGet]
    public virtual async Task<IActionResult> Details(string orderId)
    {
        var order = await _orderService.GetOrderById(orderId);
        if (!await order.Access(_workContext.CurrentCustomer, _groupService))
            return Challenge();

        var model = await _mediator.Send(new GetOrderDetails
            { Order = order, Language = _workContext.WorkingLanguage });

        return View(model);
    }

    //My account / Order details page / Cancel Unpaid Order
    [HttpGet]
    public virtual async Task<IActionResult> CancelOrder(string orderId)
    {
        var order = await _orderService.GetOrderById(orderId);
        if (!await order.Access(_workContext.CurrentCustomer, _groupService)
            || order.PaymentStatusId != PaymentStatus.Pending
            || (order.ShippingStatusId != ShippingStatus.ShippingNotRequired &&
                order.ShippingStatusId != ShippingStatus.Pending)
            || order.OrderStatusId != (int)OrderStatusSystem.Pending
            || !_orderSettings.UserCanCancelUnpaidOrder)

            return Challenge();

        await _mediator.Send(new CancelOrderCommand { Order = order, NotifyCustomer = true, NotifyStoreOwner = true });

        return RedirectToRoute("OrderDetails", new { orderId });
    }

    //My account / Order details page / PDF invoice
    [HttpGet]
    public virtual async Task<IActionResult> GetPdfInvoice(string orderId, [FromServices] IPdfService pdfService)
    {
        var order = await _orderService.GetOrderById(orderId);
        if (!await order.Access(_workContext.CurrentCustomer, _groupService))
            return Challenge();

        var orders = new List<Order> { order };
        byte[] bytes;
        using (var stream = new MemoryStream())
        {
            await pdfService.PrintOrdersToPdf(stream, orders, _workContext.WorkingLanguage.Id);
            bytes = stream.ToArray();
        }

        return File(bytes, "application/pdf", $"order_{order.Id}.pdf");
    }

    //My account / Order details page / Add order note        
    [HttpPost]
    [AutoValidateAntiforgeryToken]
    public virtual async Task<IActionResult> AddOrderNote(AddOrderNoteModel model)
    {
        if (!_orderSettings.AllowCustomerToAddOrderNote)
            return RedirectToRoute("HomePage");

        if (!ModelState.IsValid) return View("AddOrderNote", model);

        var order = await _orderService.GetOrderById(model.OrderId);
        if (!await order.Access(_workContext.CurrentCustomer, _groupService))
            return Challenge();

        await _mediator.Send(new InsertOrderNoteCommand
            { Order = order, OrderNote = model, Language = _workContext.WorkingLanguage });

        //notification
        await _mediator.Publish(new OrderNoteEvent(order, model));

        Notification(NotifyType.Success, _translationService.GetResource("OrderNote.Added"), true);
        return RedirectToRoute("OrderDetails", new { orderId = model.OrderId });
    }

    //My account / Order details page / re-order
    [HttpGet]
    public virtual async Task<IActionResult> ReOrder(string orderId)
    {
        var order = await _orderService.GetOrderById(orderId);
        if (!await order.Access(_workContext.CurrentCustomer, _groupService))
            return Challenge();

        var warnings = await _mediator.Send(new ReOrderCommand { Order = order });
        if (warnings.Any())
            Notification(NotifyType.Error, string.Join(",", warnings), true);

        return RedirectToRoute("ShoppingCart");
    }

    //My account / Order details page / Complete payment
    [HttpPost]
    [AutoValidateAntiforgeryToken]
    public virtual async Task<IActionResult> RePostPayment(string orderId)
    {
        var order = await _orderService.GetOrderById(orderId);
        if (!await order.Access(_workContext.CurrentCustomer, _groupService))
            return Challenge();

        var paymentTransaction = await _paymentTransactionService.GetOrderByGuid(order.OrderGuid);

        if (paymentTransaction == null || !await _paymentService.CanRePostRedirectPayment(paymentTransaction))
            return RedirectToRoute("OrderDetails", new { orderId });

        await _paymentService.PostRedirectPayment(paymentTransaction);

        if (IsRequestBeingRedirected || IsPostBeingDone)
            //redirection or POST has been done in PostProcessPayment
            return Content("Redirected");

        //if no redirection has been done (to a third-party payment page)
        //theoretically it's not possible
        return RedirectToRoute("OrderDetails", new { orderId });
    }

    //My account / Order details page / Shipment details page
    [HttpGet]
    public virtual async Task<IActionResult> ShipmentDetails(string shipmentId,
        [FromServices] IShipmentService shipmentService)
    {
        var shipment = await shipmentService.GetShipmentById(shipmentId);
        if (shipment == null)
            return Challenge();

        var order = await _orderService.GetOrderById(shipment.OrderId);
        if (!await order.Access(_workContext.CurrentCustomer, _groupService))
            return Challenge();

        var model = await _mediator.Send(new GetShipmentDetails {
            Customer = _workContext.CurrentCustomer,
            Language = _workContext.WorkingLanguage,
            Order = order,
            Shipment = shipment
        });

        return View(model);
    }

    //My account / Loyalty points
    [HttpGet]
    [CustomerGroupAuthorize(SystemCustomerGroupNames.Registered)]
    public virtual async Task<IActionResult> CustomerLoyaltyPoints(
        [FromServices] LoyaltyPointsSettings loyaltyPointsSettings)
    {
        if (!loyaltyPointsSettings.Enabled)
            return RedirectToRoute("CustomerInfo");

        var model = await _mediator.Send(new GetCustomerLoyaltyPoints {
            Customer = _workContext.CurrentCustomer,
            Store = _workContext.CurrentStore,
            Currency = _workContext.WorkingCurrency
        });
        return View(model);
    }

    #endregion
}