using System.Text.Json;
using Microsoft.EntityFrameworkCore;
using Tiku.Application.Commerce;
using Tiku.Domain.Commerce;

namespace Tiku.Infrastructure.Commerce;

public sealed partial class CommerceService
{
    public async Task<CommercePaymentItem> CreatePaymentAsync(
        CommerceActor actor,
        CreateCommercePaymentCommand command,
        CancellationToken cancellationToken = default)
    {
        await AssertActiveMemberAsync(actor, cancellationToken);
        var order = await FindActorOrderAsync(actor, command.OrderNo, cancellationToken);
        if (order.Status != OrderStatus.Pending)
            throw new CommerceException("Only pending orders can create payments.", "order_status_invalid");

        var provider = NormalizeProvider(command.Provider);
        var method = NormalizeMethod(command.Method);
        var payment = await dbContext.Payments
            .Where(item =>
                item.TenantId == actor.TenantId &&
                item.OrderId == order.Id &&
                item.Provider == provider &&
                item.Status == PaymentStatus.Pending)
            .OrderByDescending(item => item.CreatedAt)
            .FirstOrDefaultAsync(cancellationToken);

        if (payment is null)
        {
            payment = new Payment
            {
                TenantId = actor.TenantId,
                OrderId = order.Id,
                Provider = provider,
                Method = method,
                Status = PaymentStatus.Pending,
                AmountCents = order.AmountCents
            };
            dbContext.Payments.Add(payment);
        }

        var result = await paymentGateway.CreatePaymentAsync(
            provider,
            new CreatePaymentProviderRequest(
                actor.TenantId,
                order.OrderNo,
                order.ProductName ?? order.OrderNo,
                order.AmountCents,
                method,
                command.OpenId,
                command.ReturnUrl,
                command.QuitUrl,
                $"/api/student/commerce/payments/notify/{provider.Replace("_", "-", StringComparison.Ordinal)}?tenantId={actor.TenantId}",
                JsonSerializer.SerializeToElement(new { order.Id, actor.UserId })),
            cancellationToken);

        payment.Method = result.Method;
        payment.RawPayload = result.RawPayload;
        if (!string.IsNullOrWhiteSpace(result.ProviderTradeNo)) payment.ProviderTradeNo = result.ProviderTradeNo;

        if (IsPaid(result.Status))
            await MarkPaidAsync(
                actor,
                order,
                payment,
                result.ProviderTradeNo,
                result.RawPayload,
                "payment_paid",
                result.ProviderTradeNo,
                true,
                null,
                cancellationToken);
        else
            dbContext.PaymentEvents.Add(new PaymentEvent
            {
                TenantId = actor.TenantId,
                PaymentId = payment.Id,
                Provider = provider,
                EventType = "payment_created",
                Payload = result.RawPayload
            });

        await dbContext.SaveChangesAsync(cancellationToken);
        return ToPaymentItem(payment, order.OrderNo, result.ClientPayload);
    }
}