using System.Text.Json;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Tiku.Application.PlatformAdmin;
using Tiku.Application.Security;
using Tiku.Application.Tenancy;
using Tiku.Domain.Common;
using Tiku.Domain.Growth;
using Tiku.Domain.Operations;
using Tiku.Domain.Platform;
using Tiku.Domain.Tenancy;
using Tiku.Infrastructure.Persistence;

namespace Tiku.Infrastructure.PlatformAdmin;

internal sealed class PlatformTenantPaymentAdminService(ITenantExecutionScope tenantExecutionScope)
    : IPlatformTenantPaymentAdminService
{
    public Task<PlatformTenantCapabilityList<TenantExternalProviderItem>> GetAppsAsync(PlatformCapabilityActor actor,
        PlatformCapabilityQuery query, CancellationToken cancellationToken = default)
    {
        return ExecuteAsync("platform tenant payment apps list", async (services, token) =>
        {
            var db = services.GetRequiredService<IPlatformTenantCapabilitiesPersistence>();
            var values = db.TenantExternalProviders.AsNoTracking()
                .Where(value => value.Capability == TenantExternalProviderCapability.Payment);
            if (query.TenantId.HasValue) values = values.Where(value => value.TenantId == query.TenantId);
            if (!string.IsNullOrWhiteSpace(query.Status))
                values = values.Where(value =>
                    value.Status == ParseEnum(query.Status, TenantExternalProviderStatus.Active));
            var items = await values.OrderBy(value => value.TenantId).ThenBy(value => value.Priority)
                .Take(Limit(query.Limit))
                .Select(value => new TenantExternalProviderItem(value.Id, value.Capability, value.Provider,
                    value.Status, value.DisplayName, value.SecretRef, value.Priority, value.ConfigPublic,
                    value.Metadata, value.CreatedAt, value.UpdatedAt))
                .ToArrayAsync(token);
            return new PlatformTenantCapabilityList<TenantExternalProviderItem>(items);
        }, cancellationToken);
    }

    public Task<TenantExternalProviderItem> UpsertAppAsync(PlatformCapabilityActor actor, Guid tenantId,
        UpsertTenantExternalProviderCommand command, CancellationToken cancellationToken = default)
    {
        return ExecuteAsync("platform tenant payment app upsert", async (services, token) =>
        {
            if (command.Capability != TenantExternalProviderCapability.Payment)
                throw Error("Tenant payment capability is required.", "tenant_payment_capability_required");
            var item = await services.GetRequiredService<ITenantExternalProviderConfigService>()
                .UpsertProviderAsync(tenantId, command, token);
            services.GetRequiredService<IPlatformTenantCapabilitiesPersistence>().AuditLogs.Add(new AuditLog
            {
                TenantId = tenantId,
                ActorUserId = actor.UserId,
                Action = "platform.tenant_payment.app.upserted",
                TargetType = "tenant_external_providers",
                TargetId = item.Id.ToString(),
                Details = JsonSerializer.SerializeToElement(new { item.Provider, item.Status, item.SecretRef })
            });
            await services.GetRequiredService<IPlatformTenantCapabilitiesPersistence>().SaveChangesAsync(token);
            return item;
        }, cancellationToken);
    }

    public Task<PlatformTenantCapabilityList<object>> GetEventsAsync(PlatformCapabilityActor actor,
        PlatformCapabilityQuery query, CancellationToken cancellationToken = default)
    {
        return ExecuteAsync("platform tenant payment events list", async (services, token) =>
        {
            var db = services.GetRequiredService<IPlatformTenantCapabilitiesPersistence>();
            var values = db.PaymentEvents.AsNoTracking();
            if (query.TenantId.HasValue) values = values.Where(value => value.TenantId == query.TenantId);
            var items = await values.OrderByDescending(value => value.CreatedAt).Take(Limit(query.Limit))
                .Select(value => new
                {
                    value.Id,
                    value.TenantId,
                    value.PaymentId,
                    value.Provider,
                    value.EventType,
                    value.EventId,
                    value.SignatureValid,
                    value.ProcessedAt,
                    value.Error,
                    value.CreatedAt
                })
                .Cast<object>()
                .ToArrayAsync(token);
            return new PlatformTenantCapabilityList<object>(items);
        }, cancellationToken);
    }

    private Task<TResult> ExecuteAsync<TResult>(string reason,
        Func<IServiceProvider, CancellationToken, Task<TResult>> operation, CancellationToken cancellationToken)
    {
        return tenantExecutionScope.ExecuteAsync(
            new SystemScopeRequest(null, SystemScopeCallerType.Platform, nameof(PlatformTenantPaymentAdminService),
                reason, Guid.NewGuid().ToString("N"), true), operation, cancellationToken);
    }

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

    private static int Limit(int value)
    {
        return Math.Clamp(value, 1, 500);
    }

    private static T ParseEnum<T>(string? value, T fallback) where T : struct, Enum
    {
        return Enum.TryParse<T>(value?.Trim().Replace("-", "_", StringComparison.Ordinal), true, out var parsed)
            ? parsed
            : fallback;
    }
}