using Grand.Business.Core.Interfaces.Checkout.Payments;
using Grand.Business.Core.Queries.Checkout.Orders;
using Grand.Data;
using Grand.Domain;
using Grand.Domain.Payments;
using Grand.Infrastructure.Extensions;
using MediatR;

namespace Grand.Business.Checkout.Services.Payments;

public class PaymentTransactionService : IPaymentTransactionService
{
    private readonly IMediator _mediator;
    private readonly IRepository<PaymentTransaction> _repositoryPaymentTransaction;

    public PaymentTransactionService(
        IRepository<PaymentTransaction> repositoryPaymentTransaction,
        IMediator mediator)
    {
        _repositoryPaymentTransaction = repositoryPaymentTransaction;
        _mediator = mediator;
    }

    /// <summary>
    ///     Inserts an payment transaction
    /// </summary>
    /// <param name="paymentTransaction">payment transaction</param>
    public virtual async Task InsertPaymentTransaction(PaymentTransaction paymentTransaction)
    {
        ArgumentNullException.ThrowIfNull(paymentTransaction);

        await _repositoryPaymentTransaction.InsertAsync(paymentTransaction);

        //event notification
        await _mediator.EntityInserted(paymentTransaction);
    }

    /// <summary>
    ///     Update an payment transaction
    /// </summary>
    /// <param name="paymentTransaction">payment transaction</param>
    public virtual async Task UpdatePaymentTransaction(PaymentTransaction paymentTransaction)
    {
        ArgumentNullException.ThrowIfNull(paymentTransaction);

        await _repositoryPaymentTransaction.UpdateAsync(paymentTransaction);

        //event notification
        await _mediator.EntityUpdated(paymentTransaction);
    }

    /// <summary>
    ///     Delete an payment transaction
    /// </summary>
    /// <param name="paymentTransaction">payment transaction</param>
    public virtual async Task DeletePaymentTransaction(PaymentTransaction paymentTransaction)
    {
        ArgumentNullException.ThrowIfNull(paymentTransaction);

        await _repositoryPaymentTransaction.DeleteAsync(paymentTransaction);

        //event notification
        await _mediator.EntityDeleted(paymentTransaction);
    }

    /// <summary>
    ///     Gets an payment transaction
    /// </summary>
    /// <param name="id">The identifier</param>
    /// <returns>PaymentTransaction</returns>
    public virtual Task<PaymentTransaction> GetById(string id)
    {
        return _repositoryPaymentTransaction.GetByIdAsync(id);
    }

    /// <summary>
    ///     Gets an payment transactions for order Ident
    /// </summary>
    /// <param name="orderCode">The order code</param>
    /// <returns>PaymentTransaction</returns>
    public virtual async Task<IList<PaymentTransaction>> GetByOrderCode(string orderCode)
    {
        return await Task.FromResult(
            _repositoryPaymentTransaction.Table.Where(x => x.OrderCode == orderCode)
                .ToList());
    }

    /// <summary>
    ///     Gets an payment transactions for order guid
    /// </summary>
    /// <param name="orderGuid">The order guid</param>
    /// <returns>PaymentTransaction</returns>
    public virtual async Task<PaymentTransaction> GetOrderByGuid(Guid orderGuid)
    {
        return await Task.FromResult(_repositoryPaymentTransaction.Table.FirstOrDefault(x => x.OrderGuid == orderGuid));
    }

    /// <summary>
    ///     Get an payment transactions by authorization transaction ID and payment method system name
    /// </summary>
    /// <param name="authorizationTransactionId">Authorization transaction ID</param>
    /// <param name="paymentMethodSystemName">Payment method system name</param>
    /// <returns>Order</returns>
    public virtual async Task<IList<PaymentTransaction>> GetByAuthorizationTransactionIdAndPaymentMethod(
        string authorizationTransactionId, string paymentMethodSystemName)
    {
        var query = from p in _repositoryPaymentTransaction.Table
            select p;

        if (!string.IsNullOrEmpty(authorizationTransactionId))
            query = query.Where(c => c.AuthorizationTransactionId == authorizationTransactionId);

        if (!string.IsNullOrEmpty(paymentMethodSystemName))
            query = query.Where(c => c.PaymentMethodSystemName == paymentMethodSystemName);

        return await Task.FromResult(query.ToList());
    }

    /// <summary>
    ///     Search payment transactions
    /// </summary>
    /// <param name="orderGuid">Order ident by guid</param>
    /// <param name="storeId">Store ident</param>
    /// <param name="customerEmail">Customer email</param>
    /// <param name="ts">Transaction status</param>
    /// <param name="pageIndex">Page index</param>
    /// <param name="pageSize">Page size</param>
    /// <param name="createdFromUtc">Created date from (UTC); null to load all records</param>
    /// <param name="createdToUtc">Created date to (UTC); null to load all records</param>
    /// <returns>Page list payment transaction</returns>
    public virtual async Task<IPagedList<PaymentTransaction>> SearchPaymentTransactions(
        Guid? orderGuid = null,
        string storeId = "",
        string customerEmail = "",
        TransactionStatus? ts = null,
        int pageIndex = 0, int pageSize = int.MaxValue,
        DateTime? createdFromUtc = null,
        DateTime? createdToUtc = null)
    {
        var model = new GetPaymentTransactionQuery {
            CreatedFromUtc = createdFromUtc,
            CreatedToUtc = createdToUtc,
            PageIndex = pageIndex,
            PageSize = pageSize,
            OrderGuid = orderGuid,
            CustomerEmail = customerEmail,
            StoreId = storeId,
            Ts = ts
        };

        var query = await _mediator.Send(model);
        return await PagedList<PaymentTransaction>.Create(query, pageIndex, pageSize);
    }

    /// <summary>
    ///     Set payment error for transaction
    /// </summary>
    public virtual async Task SetError(string paymentTransactionId, List<string> errors)
    {
        await _repositoryPaymentTransaction.UpdateField(paymentTransactionId, x => x.Errors, errors);
    }
}