using MediatR;
using Microsoft.IdentityModel.Tokens;
using Netina.Common.Models.Exception;
using Netina.Core.Abstracts;
using Netina.Core.CoreServices.Abstracts;
using Netina.Domain.CommandQueries.Commands;
using Netina.Domain.CommandQueries.Queries;
using Netina.Domain.Enums;
using Netina.Domain.MartenEntities.Settings;
using Netina.Domain.Models.Settings;
using Netina.Infrastructure.Models.RestApi.Zarinpal;
using Netina.Infrastructure.RestServices;
using Newtonsoft.Json;

namespace Netina.Infrastructure.Services;

public class ZarinpalService : IPaymentService
{
    private readonly IRestApiWrapper _restApiWrapper;
    private readonly IMediator _mediator;
    private readonly ISettingService _settingService;
    private readonly SiteSettings _siteSettings;
    public ZarinpalService(IRestApiWrapper restApiWrapper, IOptionsSnapshot<SiteSettings> snapshot, IMediator mediator , ISettingService settingService)
    {
        _restApiWrapper = restApiWrapper;
        _mediator = mediator;
        _settingService = settingService;
        _siteSettings = snapshot.Value;
    }
    public async Task<string> GetPaymentLinkAsync(double amount, string factorNumber, Guid orderId, Guid userId, string phoneNumber, string fullName, CancellationToken cancellationToken = default)
    {
        var paymentSetting = (await _settingService.GetSettingAsync("PaymentSetting", cancellationToken)) as PaymentSetting;
        if (paymentSetting == null)
            throw new AppException("تنظیمات پرداخت انجام نشده است");
        if (paymentSetting.ZarinPalApiKey.IsNullOrEmpty())
            throw new AppException("کد مرچنت زرین پال وارد نشده است");

        var request = new ZarinaplPaymentLinkRequest
        {
            description = $"پرداخت {amount.ToString("N0")} ریال توسط {fullName} با شماره تماس {phoneNumber} برای سفارش با شماره {factorNumber}",
            amount = (int)amount,
            callback_url = Path.Combine(_siteSettings.BaseUrl, "api", "accounting", "pay", "verify"),
            merchant_id = paymentSetting.ZarinPalApiKey,
            metadata = new ZarinaplPaymentLinkRequestMetadata { mobile = phoneNumber }
        };
        var responseJson = await _restApiWrapper.ZarinpalRestApi.GetPaymentLinkAsync(request);
        var response = JsonConvert.DeserializeObject<ZarinaplPaymentLinkResponse>(responseJson);
        if (response.data.code != 100)
            throw new AppException($"Exception in get link from zarinpal | {response.data.message}");


        var createPaymentResult = await _mediator.Send(new CreateOrUpdatePaymentCommand(null, factorNumber, amount,
            request.description, string.Empty, string.Empty,
                response.data.authority, PaymentType.Online, PaymentStatus.InPaymentGateway, orderId, userId), cancellationToken);

        string link = $"https://www.zarinpal.com/pg/StartPay/{response.data.authority}";
        return link;
    }

    public async Task<Tuple<string, string>> VerifyPaymentAsync(string authority, CancellationToken cancellationToken = default)
    {
        var paymentSetting = (await _settingService.GetSettingAsync("PaymentSetting", cancellationToken)) as PaymentSetting;
        if (paymentSetting == null)
            throw new AppException("تنظیمات پرداخت انجام نشده است");
        if (paymentSetting.ZarinPalApiKey.IsNullOrEmpty())
            throw new AppException("کد مرچنت زرین پال وارد نشده است");

        var payment = await _mediator.Send(new GetPaymentQuery(Authority: authority), cancellationToken);
        var request = new ZarinaplVerifyPaymentRequest
        {
            amount = (int)payment.Amount,
            authority = payment.Authority,
            merchant_id = paymentSetting.ZarinPalApiKey
        };
        var response = await _restApiWrapper.ZarinpalRestApi.VerifyPaymentAsync(request);
        if (response.data.code != 100)
            throw new AppException($"Exception in get link from zarinpal | {response.data.message}");

        payment.Status = PaymentStatus.Paid;
        payment.CardPan = response.data.card_pan;
        payment.TransactionCode = response.data.ref_id.ToString();

        await _mediator.Send(new CreateOrUpdatePaymentCommand(payment.Id, payment.FactorNumber, payment.Amount, payment.Description,
                payment.TransactionCode, payment.CardPan, payment.Authority, payment.Type, payment.Status,
                payment.OrderId, payment.CustomerId), cancellationToken);

        await _mediator.Send(new SubmitOrderPaymentCommand(payment.OrderId, OrderPaymentMethod.OnlinePayment, true),
            cancellationToken);

        return new Tuple<string, string>(payment.TransactionCode, payment.FactorNumber);
    }
}