using System.Text.Json;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Tiku.Application.Commerce;
using Tiku.Application.PlatformBilling;
using Tiku.Application.Security;
using Tiku.Domain.Platform;
using Tiku.Domain.Tenancy;
using Tiku.Infrastructure.Persistence;

namespace Tiku.Infrastructure.PlatformBilling;

internal sealed class PlatformBillingPaymentGateway(
    ITenantExecutionScope tenantExecutionScope) : IPlatformBillingPaymentGateway
{
    public Task<CreatePaymentProviderResult> CreatePaymentAsync(
        string provider,
        CreatePaymentProviderRequest request,
        CancellationToken cancellationToken = default) =>
        ExecuteAsync(provider, request.OrderNo, async (resolvedProvider, account, token) =>
            await resolvedProvider.CreatePaymentAsync(account, request, token), cancellationToken);

    public Task<PaymentNotificationResult> ParseNotificationAsync(
        string provider,
        PaymentNotificationRequest request,
        CancellationToken cancellationToken = default) =>
        ExecuteAsync(provider, "payment-notification", async (resolvedProvider, account, token) =>
            await resolvedProvider.ParsePaymentNotificationAsync(account, request, token), cancellationToken);

    private Task<TResult> ExecuteAsync<TResult>(
        string provider,
        string correlationId,
        Func<IPaymentProvider, PaymentProviderAccount, CancellationToken, Task<TResult>> operation,
        CancellationToken cancellationToken)
    {
        return tenantExecutionScope.ExecuteAsync(
            new SystemScopeRequest(
                null,
                SystemScopeCallerType.Platform,
                nameof(PlatformBillingPaymentGateway),
                "Resolve the platform-owned SaaS billing payment account",
                correlationId,
                IsGlobal: true),
            async (services, token) =>
            {
                var dbContext = services.GetRequiredService<TikuDbContext>();
                var platformTenantId = await dbContext.Tenants.AsNoTracking()
                    .Where(value => value.Mode == TenantMode.PlatformOwned)
                    .Select(value => value.Id)
                    .SingleOrDefaultAsync(token);
                if (platformTenantId == Guid.Empty)
                {
                    throw new PaymentProviderException("Platform-owned tenant is missing.", "platform_payment_owner_missing");
                }

                var normalized = NormalizeProvider(provider);
                var resolvedProvider = services.GetServices<IPaymentProvider>()
                    .SingleOrDefault(value => value.Provider == normalized)
                    ?? throw new PaymentProviderException("Payment provider is not supported.", "payment_provider_not_supported");
                PaymentProviderAccount account;
                if (normalized == PaymentProviders.Manual)
                {
                    account = new PaymentProviderAccount(
                        platformTenantId,
                        PaymentProviders.Manual,
                        "PlatformCollect",
                        JsonDocument.Parse("{}").RootElement.Clone(),
                        JsonDocument.Parse("{}").RootElement.Clone());
                }
                else
                {
                    var channel = await dbContext.PlatformPaymentChannels.AsNoTracking()
                        .Where(value => value.Provider == normalized && value.Status == PlatformPaymentChannelStatus.Active)
                        .OrderBy(value => value.Priority)
                        .FirstOrDefaultAsync(token)
                        ?? throw new PaymentProviderException("Platform payment channel is missing.", "platform_payment_channel_missing");
                    account = new PaymentProviderAccount(
                        platformTenantId,
                        normalized,
                        channel.Mode,
                        channel.ConfigPublic,
                        JsonDocument.Parse("{}").RootElement.Clone());
                }

                return await operation(resolvedProvider, account, token);
            },
            cancellationToken);
    }

    private static string NormalizeProvider(string provider)
    {
        var normalized = provider.Trim().ToLowerInvariant().Replace("-", "_", StringComparison.Ordinal);
        return normalized switch
        {
            "wechat" or "wechatpay" or "wxpay" or "wx_pay" => PaymentProviders.WechatPay,
            "ali_pay" => PaymentProviders.Alipay,
            PaymentProviders.WechatPay or PaymentProviders.Alipay or PaymentProviders.Manual => normalized,
            _ => throw new PaymentProviderException("Payment provider is not supported.", "payment_provider_not_supported")
        };
    }
}
