using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Tiku.Application.Commerce;
using Tiku.Application.PlatformBilling;
using Tiku.Application.Security;
using Tiku.Infrastructure.Persistence;

namespace Tiku.Infrastructure.PlatformBilling;

internal sealed class PlatformBillingNotificationService(
    ITenantExecutionScope tenantExecutionScope,
    IPlatformBillingPaymentGateway paymentGateway) : IPlatformBillingNotificationService
{
    public async Task ProcessAsync(PlatformBillingNotification notification,
        CancellationToken cancellationToken = default)
    {
        var parsed = await paymentGateway.ParseNotificationAsync(
            notification.Provider,
            new PaymentNotificationRequest(Guid.Empty, notification.Provider, notification.Headers,
                notification.RawBody, notification.Body),
            cancellationToken);
        if (!parsed.SignatureValid || !parsed.Paid)
            throw Error("Payment notification was not a valid paid event.", "platform_billing_notification_invalid");

        await tenantExecutionScope.ExecuteAsync(
            new SystemScopeRequest(null, SystemScopeCallerType.Platform, nameof(PlatformBillingNotificationService),
                "Settle platform billing notification", parsed.EventId, true),
            async (services, token) =>
            {
                var db = services.GetRequiredService<TikuDbContext>();
                var order = await db.PlatformBillingOrders.AsNoTracking()
                                .SingleOrDefaultAsync(value => value.OrderNo == parsed.OrderNo, token)
                            ?? throw Error("Platform billing order was not found.", "platform_billing_order_not_found");
                if (order.TotalAmountCents != parsed.AmountCents)
                    throw Error("Payment notification amount does not match the order.",
                        "platform_billing_payment_amount_mismatch");
                var payment = await db.PlatformBillingPayments.AsNoTracking()
                                  .Where(value =>
                                      value.TenantId == order.TenantId && value.OrderId == order.Id &&
                                      value.Provider == parsed.Provider)
                                  .OrderByDescending(value => value.CreatedAt)
                                  .FirstOrDefaultAsync(token)
                              ?? throw Error("Platform billing payment was not found.",
                                  "platform_billing_payment_not_found");
                await services.GetRequiredService<IPlatformBillingSettlementService>().MarkPaidAsync(
                    payment.Id,
                    parsed.EventId,
                    parsed.EventType,
                    parsed.ProviderTradeNo,
                    parsed.PaidAt ?? DateTimeOffset.UtcNow,
                    parsed.RawPayload,
                    null,
                    token);
            },
            cancellationToken);
    }

    private static PlatformBillingException Error(string message, string code)
    {
        return new PlatformBillingException(message, code);
    }
}