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

namespace Tiku.Infrastructure.Commerce;

internal sealed partial class RefundAdministrationService
{
    public async Task<CommerceRefundRequest> ProcessRefundNotificationAsync(
        Guid tenantId,
        RefundNotificationCommand command,
        CancellationToken cancellationToken = default)
    {
        var provider = NormalizeProvider(command.Provider);
        var refund = await commercePersistence.CommerceRefundRequests.SingleOrDefaultAsync(
            item => item.TenantId == tenantId && item.RefundNo == command.RefundNo,
            cancellationToken) ?? throw new CommerceException("Refund request was not found.", "refund_not_found");
        var eventId = string.IsNullOrWhiteSpace(command.EventId)
            ? $"{provider}:{command.RefundNo}:{command.Status}"
            : command.EventId.Trim();
        var duplicate = await commercePersistence.PaymentEvents.AnyAsync(
            item => item.TenantId == tenantId &&
                    item.Provider == provider &&
                    item.EventType == "refund" &&
                    item.EventId == eventId,
            cancellationToken);
        if (duplicate) return refund;

        commercePersistence.PaymentEvents.Add(new PaymentEvent
        {
            TenantId = tenantId,
            Provider = provider,
            EventType = "refund",
            EventId = eventId,
            SignatureValid = true,
            Payload = JsonObjectOrDefault(command.Payload),
            ProcessedAt = DateTimeOffset.UtcNow
        });
        var fromStatus = refund.Status;
        if (fromStatus != command.Status && IsAllowedRefundTransition(fromStatus, command.Status))
        {
            refund.Status = command.Status;
            refund.ProviderRefundNo = string.IsNullOrWhiteSpace(command.ProviderRefundNo)
                ? refund.ProviderRefundNo
                : command.ProviderRefundNo.Trim();
            if (command.Status == CommerceRefundStatus.Succeeded)
            {
                refund.SucceededAt = DateTimeOffset.UtcNow;
                await ApplyRefundToOrderAsync(refund, cancellationToken);
            }
            else if (command.Status == CommerceRefundStatus.Failed)
            {
                refund.FailedAt = DateTimeOffset.UtcNow;
            }

            commercePersistence.CommerceRefundEvents.Add(new CommerceRefundEvent
            {
                TenantId = tenantId,
                RefundRequestId = refund.Id,
                FromStatus = fromStatus,
                ToStatus = command.Status,
                EventType = "provider_notify",
                Details = JsonObjectOrDefault(command.Payload)
            });
        }

        await unitOfWork.SaveChangesAsync(cancellationToken);
        return refund;
    }
}
