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

namespace Tiku.Infrastructure.PlatformBilling;

internal sealed class PlatformBillingAdminService(
    ITenantExecutionScope tenantExecutionScope) : IPlatformBillingAdminService
{
    public Task<IReadOnlyCollection<PlatformBillingOrder>> GetOrdersAsync(SaasCatalogActor actor,
        PlatformBillingAdminQuery query, CancellationToken cancellationToken = default)
    {
        return ExecuteAsync<IReadOnlyCollection<PlatformBillingOrder>>("list SaaS orders", async (services, token) =>
        {
            var db = services.GetRequiredService<TikuDbContext>();
            var values = db.PlatformBillingOrders.AsNoTracking();
            if (query.TenantId.HasValue) values = values.Where(value => value.TenantId == query.TenantId);
            if (!string.IsNullOrWhiteSpace(query.Status))
                values = values.Where(value => value.Status == Parse<PlatformBillingOrderStatus>(query.Status));
            return await values.OrderByDescending(value => value.CreatedAt).Take(Limit(query.Limit))
                .ToArrayAsync(token);
        }, cancellationToken);
    }

    public Task<IReadOnlyCollection<PlatformBillingPayment>> GetPaymentsAsync(SaasCatalogActor actor,
        PlatformBillingAdminQuery query, CancellationToken cancellationToken = default)
    {
        return ExecuteAsync<IReadOnlyCollection<PlatformBillingPayment>>("list SaaS payments",
            async (services, token) =>
            {
                var db = services.GetRequiredService<TikuDbContext>();
                var values = db.PlatformBillingPayments.AsNoTracking();
                if (query.TenantId.HasValue) values = values.Where(value => value.TenantId == query.TenantId);
                if (!string.IsNullOrWhiteSpace(query.Status))
                    values = values.Where(value => value.Status == Parse<PlatformBillingPaymentStatus>(query.Status));
                return await values.OrderByDescending(value => value.CreatedAt).Take(Limit(query.Limit))
                    .ToArrayAsync(token);
            }, cancellationToken);
    }

    public Task<IReadOnlyCollection<PlatformBillingRefund>> GetRefundsAsync(SaasCatalogActor actor,
        PlatformBillingAdminQuery query, CancellationToken cancellationToken = default)
    {
        return ExecuteAsync<IReadOnlyCollection<PlatformBillingRefund>>("list SaaS refunds", async (services, token) =>
        {
            var db = services.GetRequiredService<TikuDbContext>();
            var values = db.PlatformBillingRefunds.AsNoTracking();
            if (query.TenantId.HasValue) values = values.Where(value => value.TenantId == query.TenantId);
            if (!string.IsNullOrWhiteSpace(query.Status))
                values = values.Where(value => value.Status == Parse<PlatformBillingRefundStatus>(query.Status));
            return await values.OrderByDescending(value => value.CreatedAt).Take(Limit(query.Limit))
                .ToArrayAsync(token);
        }, cancellationToken);
    }

    public Task<IReadOnlyCollection<PlatformBillingInvoice>> GetInvoicesAsync(SaasCatalogActor actor,
        PlatformBillingAdminQuery query, CancellationToken cancellationToken = default)
    {
        return ExecuteAsync<IReadOnlyCollection<PlatformBillingInvoice>>("list SaaS invoices",
            async (services, token) =>
            {
                var db = services.GetRequiredService<TikuDbContext>();
                var values = db.PlatformBillingInvoices.AsNoTracking();
                if (query.TenantId.HasValue) values = values.Where(value => value.TenantId == query.TenantId);
                if (!string.IsNullOrWhiteSpace(query.Status))
                    values = values.Where(value => value.Status == Parse<PlatformBillingInvoiceStatus>(query.Status));
                return await values.OrderByDescending(value => value.CreatedAt).Take(Limit(query.Limit))
                    .ToArrayAsync(token);
            }, cancellationToken);
    }

    public Task<IReadOnlyCollection<TenantFeatureUsage>> GetUsageAsync(SaasCatalogActor actor,
        PlatformBillingAdminQuery query, CancellationToken cancellationToken = default)
    {
        return ExecuteAsync<IReadOnlyCollection<TenantFeatureUsage>>("list SaaS usage", async (services, token) =>
        {
            var db = services.GetRequiredService<TikuDbContext>();
            var values = db.TenantFeatureUsages.AsNoTracking();
            if (query.TenantId.HasValue) values = values.Where(value => value.TenantId == query.TenantId);
            return await values.OrderByDescending(value => value.PeriodStart).ThenBy(value => value.MetricCode)
                .Take(Limit(query.Limit)).ToArrayAsync(token);
        }, cancellationToken);
    }

    public Task<IReadOnlyCollection<PlatformBillingInvoiceReminder>> GetInvoiceRemindersAsync(SaasCatalogActor actor,
        PlatformBillingAdminQuery query, CancellationToken cancellationToken = default)
    {
        return ExecuteAsync<IReadOnlyCollection<PlatformBillingInvoiceReminder>>("list SaaS invoice reminders",
            async (services, token) =>
            {
                var db = services.GetRequiredService<TikuDbContext>();
                var values = db.PlatformBillingInvoiceReminders.AsNoTracking();
                if (query.TenantId.HasValue) values = values.Where(value => value.TenantId == query.TenantId);
                if (!string.IsNullOrWhiteSpace(query.Status))
                    values = values.Where(value =>
                        value.Status == Parse<PlatformBillingInvoiceReminderStatus>(query.Status));
                return await values.OrderByDescending(value => value.ReminderDate)
                    .ThenByDescending(value => value.CreatedAt).Take(Limit(query.Limit)).ToArrayAsync(token);
            }, cancellationToken);
    }

    public Task<IReadOnlyCollection<TenantSaasSubscription>> GetSubscriptionsAsync(SaasCatalogActor actor,
        PlatformBillingAdminQuery query, CancellationToken cancellationToken = default)
    {
        return ExecuteAsync<IReadOnlyCollection<TenantSaasSubscription>>("list SaaS subscriptions",
            async (services, token) =>
            {
                var db = services.GetRequiredService<TikuDbContext>();
                var values = db.TenantSaasSubscriptions.AsNoTracking();
                if (query.TenantId.HasValue) values = values.Where(value => value.TenantId == query.TenantId);
                if (!string.IsNullOrWhiteSpace(query.Status))
                    values = values.Where(value => value.Status == Parse<TenantSaasSubscriptionStatus>(query.Status));
                return await values.OrderByDescending(value => value.UpdatedAt).Take(Limit(query.Limit))
                    .ToArrayAsync(token);
            }, cancellationToken);
    }

    public Task<PlatformBillingPayment> ConfirmManualPaymentAsync(
        SaasCatalogActor actor,
        ConfirmManualPaymentCommand command,
        CancellationToken cancellationToken = default)
    {
        return ExecuteAsync("confirm manual SaaS payment", async (services, token) =>
        {
            var db = services.GetRequiredService<TikuDbContext>();
            var payment = await db.PlatformBillingPayments.AsNoTracking()
                              .SingleOrDefaultAsync(value => value.Id == command.PaymentId, token)
                          ?? throw Error("Platform billing payment was not found.",
                              "platform_billing_payment_not_found");
            if (payment.Provider != "manual")
                throw Error("Only a manual payment can be confirmed by an operator.",
                    "platform_billing_manual_payment_required");

            if (string.IsNullOrWhiteSpace(command.Reason))
                throw Error("Manual payment confirmation reason is required.", "platform_billing_reason_required");

            return await services.GetRequiredService<IPlatformBillingSettlementService>().MarkPaidAsync(
                payment.Id,
                $"manual-{payment.Id:N}",
                "manual_payment_confirmed",
                command.ProviderTradeNo,
                command.PaidAt ?? DateTimeOffset.UtcNow,
                JsonSerializer.SerializeToElement(new { command.Reason }),
                actor.UserId,
                token);
        }, cancellationToken);
    }

    public Task<TenantFeatureOverride> UpsertFeatureOverrideAsync(
        SaasCatalogActor actor,
        UpsertTenantFeatureOverrideCommand command,
        CancellationToken cancellationToken = default)
    {
        return ExecuteAsync("upsert tenant feature override", async (services, token) =>
        {
            var db = services.GetRequiredService<TikuDbContext>();
            var featureCode = command.FeatureCode.Trim().ToLowerInvariant();
            if (string.IsNullOrWhiteSpace(command.Reason))
                throw Error("Override reason is required.", "platform_billing_reason_required");
            if (!await db.Tenants.AnyAsync(value => value.Id == command.TenantId, token))
                throw Error("Tenant was not found.", "tenant_not_found");
            if (!await db.SaasFeatures.AnyAsync(value => value.Code == featureCode && !value.IsCore, token))
                throw Error("SaaS feature was not found.", "saas_feature_not_found");
            var item = await db.TenantFeatureOverrides.SingleOrDefaultAsync(
                value => value.TenantId == command.TenantId && value.FeatureCode == featureCode, token);
            if (item is null)
            {
                item = new TenantFeatureOverride { TenantId = command.TenantId, FeatureCode = featureCode };
                db.TenantFeatureOverrides.Add(item);
            }

            item.Mode = command.Mode;
            item.ExpiresAt = command.ExpiresAt;
            item.Reason = command.Reason.Trim();
            db.AuditLogs.Add(new AuditLog
            {
                TenantId = command.TenantId,
                ActorUserId = actor.UserId,
                Action = "platform.saas.feature_override.updated",
                TargetType = "tenant_feature_overrides",
                TargetId = item.Id.ToString(),
                Details = JsonSerializer.SerializeToElement(new
                    { item.FeatureCode, item.Mode, item.ExpiresAt, item.Reason })
            });
            await db.SaveChangesAsync(token);
            await services.GetRequiredService<ITenantFeatureCacheInvalidator>()
                .InvalidateAsync(command.TenantId, token);
            return item;
        }, cancellationToken);
    }

    public Task<TenantSaasSubscription> GrantTrialAsync(
        SaasCatalogActor actor,
        GrantTenantTrialCommand command,
        CancellationToken cancellationToken = default)
    {
        return ExecuteAsync("grant tenant SaaS trial", async (services, token) =>
        {
            var db = services.GetRequiredService<TikuDbContext>();
            var key = Required(command.IdempotencyKey, "idempotencyKey");
            var existingRequest = await db.PlatformOperationIdempotencies.AsNoTracking()
                .SingleOrDefaultAsync(value => value.ActorUserId == actor.UserId &&
                                               value.Scope == "platform.saas.trial" &&
                                               value.IdempotencyKey == key, token);
            if (existingRequest is not null)
            {
                var requestHash = Hash($"{command.TenantId:N}|{command.BaseOfferingVersionId:N}|{command.TrialDays}");
                if (!string.Equals(existingRequest.RequestHash, requestHash, StringComparison.Ordinal))
                    throw Error("Idempotency key was used with a different trial request.", "idempotency_conflict");

                return await db.TenantSaasSubscriptions.SingleAsync(value => value.Id == existingRequest.ResourceId,
                    token);
            }

            if (await db.TenantSaasSubscriptions.AnyAsync(value => value.TenantId == command.TenantId &&
                                                                   value.Status != TenantSaasSubscriptionStatus
                                                                       .Cancelled &&
                                                                   value.Status != TenantSaasSubscriptionStatus.Expired,
                    token))
                throw Error("Tenant already has an effective subscription.", "tenant_saas_subscription_exists");

            var version = await (
                              from value in db.SaasOfferingVersions.AsNoTracking()
                              join offering in db.SaasOfferings.AsNoTracking() on value.OfferingId equals offering.Id
                              where value.Id == command.BaseOfferingVersionId &&
                                    value.Status == SaasOfferingVersionStatus.Published &&
                                    offering.Type == SaasOfferingType.BasePlan
                              select value).SingleOrDefaultAsync(token)
                          ?? throw Error("Published base offering version was not found.",
                              "saas_offering_version_not_found");
            var tenant = await db.Tenants.SingleOrDefaultAsync(value => value.Id == command.TenantId, token)
                         ?? throw Error("Tenant was not found.", "tenant_not_found");
            var now = DateTimeOffset.UtcNow;
            var end = now.AddDays(Math.Clamp(command.TrialDays, 1, 365));
            var subscription = new TenantSaasSubscription
            {
                TenantId = tenant.Id,
                BaseOfferingVersionId = version.Id,
                Status = TenantSaasSubscriptionStatus.Trial,
                StartsAt = now,
                CurrentPeriodStart = now,
                CurrentPeriodEnd = end,
                LifecycleVersion = 1
            };
            db.TenantSaasSubscriptions.Add(subscription);
            db.TenantSaasSubscriptionItems.Add(new TenantSaasSubscriptionItem
            {
                TenantId = tenant.Id,
                SubscriptionId = subscription.Id,
                OfferingVersionId = version.Id,
                ItemType = TenantSaasSubscriptionItemType.BasePlan,
                Status = TenantSaasSubscriptionItemStatus.Active,
                StartsAt = now,
                EndsAt = end
            });
            tenant.BillingStatus = BillingStatus.Trial;
            db.PlatformOperationIdempotencies.Add(new PlatformOperationIdempotency
            {
                ActorUserId = actor.UserId,
                Scope = "platform.saas.trial",
                IdempotencyKey = key,
                RequestHash = Hash($"{command.TenantId:N}|{command.BaseOfferingVersionId:N}|{command.TrialDays}"),
                ResourceId = subscription.Id
            });
            AddAudit(db, actor.UserId, tenant.Id, "platform.saas.trial.granted", subscription.Id, command.Reason);
            await db.SaveChangesAsync(token);
            await services.GetRequiredService<ITenantFeatureCacheInvalidator>().InvalidateAsync(tenant.Id, token);
            return subscription;
        }, cancellationToken);
    }

    public Task<TenantSaasSubscription> SuspendSubscriptionAsync(SaasCatalogActor actor,
        ChangePlatformSubscriptionCommand command, CancellationToken cancellationToken = default)
    {
        return ChangeSubscriptionStatusAsync(actor, command, "suspended", cancellationToken);
    }

    public Task<TenantSaasSubscription> ResumeSubscriptionAsync(SaasCatalogActor actor,
        ChangePlatformSubscriptionCommand command, CancellationToken cancellationToken = default)
    {
        return ChangeSubscriptionStatusAsync(actor, command, "resumed", cancellationToken);
    }

    public Task<TenantSaasSubscription> CancelSubscriptionAsync(SaasCatalogActor actor,
        ChangePlatformSubscriptionCommand command, CancellationToken cancellationToken = default)
    {
        return ChangeSubscriptionStatusAsync(actor, command, "cancelled", cancellationToken);
    }

    public Task<TenantSaasSubscription> ExtendSubscriptionAsync(SaasCatalogActor actor,
        ChangePlatformSubscriptionCommand command, CancellationToken cancellationToken = default)
    {
        return ChangeSubscriptionStatusAsync(actor, command, "extended", cancellationToken);
    }

    public Task<PlatformBillingRefund> RequestRefundAsync(
        SaasCatalogActor actor,
        RequestPlatformRefundCommand command,
        CancellationToken cancellationToken = default)
    {
        return ExecuteAsync("request SaaS refund", async (services, token) =>
        {
            var db = services.GetRequiredService<TikuDbContext>();
            var key = Required(command.IdempotencyKey, "idempotencyKey");
            var payment = await db.PlatformBillingPayments.AsNoTracking()
                              .SingleOrDefaultAsync(value => value.Id == command.PaymentId, token)
                          ?? throw Error("Payment was not found.", "platform_billing_payment_not_found");
            var reason = Required(command.Reason, "reason");
            var existing = await db.PlatformBillingRefunds.SingleOrDefaultAsync(value =>
                value.TenantId == payment.TenantId && value.IdempotencyKey == key, token);
            if (existing is not null)
            {
                if (existing.PaymentId != command.PaymentId || existing.AmountCents != command.AmountCents ||
                    existing.SubscriptionEffect != command.SubscriptionEffect ||
                    !string.Equals(existing.Reason, reason, StringComparison.Ordinal))
                    throw Error("Idempotency key was used with a different refund request.", "idempotency_conflict");

                return existing;
            }

            if (payment.Status != PlatformBillingPaymentStatus.Succeeded)
                throw Error("Only a succeeded payment can be refunded.", "platform_billing_payment_not_refundable");

            var refunded = await db.PlatformBillingRefunds.AsNoTracking()
                .Where(value => value.TenantId == payment.TenantId && value.PaymentId == payment.Id &&
                                value.Status != PlatformBillingRefundStatus.Cancelled &&
                                value.Status != PlatformBillingRefundStatus.Failed)
                .SumAsync(value => (int?)value.AmountCents, token) ?? 0;
            if (command.AmountCents <= 0 || refunded + command.AmountCents > payment.AmountCents)
                throw Error("Refund amount exceeds the refundable payment balance.",
                    "platform_billing_refund_amount_invalid");

            var refund = new PlatformBillingRefund
            {
                TenantId = payment.TenantId,
                OrderId = payment.OrderId,
                PaymentId = payment.Id,
                RefundNo = Number("SR"),
                Status = PlatformBillingRefundStatus.Requested,
                AmountCents = command.AmountCents,
                Reason = reason,
                IdempotencyKey = key,
                SubscriptionEffect = command.SubscriptionEffect,
                RequestedBy = actor.UserId
            };
            db.PlatformBillingRefunds.Add(refund);
            AddAudit(db, actor.UserId, refund.TenantId, "platform.saas.refund.requested", refund.Id, refund.Reason);
            await db.SaveChangesAsync(token);
            return refund;
        }, cancellationToken);
    }

    public Task<PlatformBillingRefund> ApproveRefundAsync(SaasCatalogActor actor, ReviewPlatformRefundCommand command,
        CancellationToken cancellationToken = default)
    {
        return ReviewRefundAsync(actor, command, true, cancellationToken);
    }

    public Task<PlatformBillingRefund> RejectRefundAsync(SaasCatalogActor actor, ReviewPlatformRefundCommand command,
        CancellationToken cancellationToken = default)
    {
        return ReviewRefundAsync(actor, command, false, cancellationToken);
    }

    public Task<PlatformBillingRefund> RetryRefundAsync(SaasCatalogActor actor, ReviewPlatformRefundCommand command,
        CancellationToken cancellationToken = default)
    {
        return ExecuteAsync("retry SaaS refund", async (services, token) =>
        {
            var db = services.GetRequiredService<TikuDbContext>();
            var refund =
                await db.PlatformBillingRefunds.SingleOrDefaultAsync(value => value.Id == command.RefundId, token)
                ?? throw Error("Refund was not found.", "platform_billing_refund_not_found");
            if (refund.Status != PlatformBillingRefundStatus.Failed)
                throw Error("Only a failed refund can be retried.", "platform_billing_refund_not_retryable");

            refund.Status = PlatformBillingRefundStatus.Processing;
            refund.LastError = null;
            AddAudit(db, actor.UserId, refund.TenantId, "platform.saas.refund.retried", refund.Id, command.Reason);
            await db.SaveChangesAsync(token);
            return refund;
        }, cancellationToken);
    }

    public Task<CommercialMetrics> GetCommercialMetricsAsync(SaasCatalogActor actor,
        CancellationToken cancellationToken = default)
    {
        return ExecuteAsync<CommercialMetrics>("get SaaS commercial metrics", async (services, token) =>
        {
            var db = services.GetRequiredService<TikuDbContext>();
            var now = DateTimeOffset.UtcNow;
            var periodStart = new DateTimeOffset(now.Year, now.Month, 1, 0, 0, 0, TimeSpan.Zero);
            var subscriptions = await (
                from subscription in db.TenantSaasSubscriptions.AsNoTracking()
                join version in db.SaasOfferingVersions.AsNoTracking() on subscription.BaseOfferingVersionId equals
                    version.Id
                select new { subscription.Status, version.AmountCents, version.BillingCycle }).ToArrayAsync(token);
            var mrr = subscriptions.Where(value => value.Status == TenantSaasSubscriptionStatus.Active)
                .Sum(value => value.BillingCycle switch
                {
                    PlatformBillingCycle.Monthly => value.AmountCents,
                    PlatformBillingCycle.Quarterly => value.AmountCents / 3,
                    PlatformBillingCycle.Yearly => value.AmountCents / 12,
                    _ => 0
                });
            var collected = await db.PlatformBillingPayments.AsNoTracking()
                .Where(value => value.Status == PlatformBillingPaymentStatus.Succeeded && value.PaidAt >= periodStart)
                .SumAsync(value => (int?)value.AmountCents, token) ?? 0;
            var refunded = await db.PlatformBillingRefunds.AsNoTracking()
                .Where(value =>
                    value.Status == PlatformBillingRefundStatus.Succeeded && value.CompletedAt >= periodStart)
                .SumAsync(value => (int?)value.AmountCents, token) ?? 0;
            var outstanding = await db.PlatformBillingInvoices.AsNoTracking()
                .Where(value =>
                    value.Status == PlatformBillingInvoiceStatus.Issued ||
                    value.Status == PlatformBillingInvoiceStatus.Overdue)
                .SumAsync(value => (int?)value.TotalAmountCents, token) ?? 0;
            var overdue = await db.PlatformBillingInvoices.AsNoTracking()
                .Where(value => value.Status == PlatformBillingInvoiceStatus.Overdue)
                .SumAsync(value => (int?)value.TotalAmountCents, token) ?? 0;
            return new CommercialMetrics(
                subscriptions.Count(value => value.Status == TenantSaasSubscriptionStatus.Active),
                subscriptions.Count(value => value.Status == TenantSaasSubscriptionStatus.Trial),
                subscriptions.Count(value => value.Status == TenantSaasSubscriptionStatus.PastDue),
                mrr,
                mrr * 12,
                collected,
                outstanding,
                overdue,
                refunded,
                subscriptions.Count(value =>
                    value.Status is TenantSaasSubscriptionStatus.Cancelled or TenantSaasSubscriptionStatus.Expired));
        }, cancellationToken);
    }

    private Task<TenantSaasSubscription> ChangeSubscriptionStatusAsync(
        SaasCatalogActor actor,
        ChangePlatformSubscriptionCommand command,
        string action,
        CancellationToken cancellationToken)
    {
        return ExecuteAsync($"{action} SaaS subscription", async (services, token) =>
        {
            var db = services.GetRequiredService<TikuDbContext>();
            var subscription =
                await db.TenantSaasSubscriptions.SingleOrDefaultAsync(value => value.Id == command.SubscriptionId,
                    token)
                ?? throw Error("Subscription was not found.", "tenant_saas_subscription_not_found");
            var tenant = await db.Tenants.SingleAsync(value => value.Id == subscription.TenantId, token);
            var items = await db.TenantSaasSubscriptionItems
                .Where(value => value.TenantId == subscription.TenantId && value.SubscriptionId == subscription.Id)
                .ToArrayAsync(token);
            _ = Required(command.Reason, "reason");
            switch (action)
            {
                case "suspended":
                    if (subscription.Status is TenantSaasSubscriptionStatus.Cancelled
                        or TenantSaasSubscriptionStatus.Expired)
                        throw Error("Subscription cannot be suspended.", "tenant_saas_subscription_status_invalid");
                    subscription.Status = TenantSaasSubscriptionStatus.Suspended;
                    tenant.BillingStatus = BillingStatus.Suspended;
                    break;
                case "resumed":
                    if (subscription.Status != TenantSaasSubscriptionStatus.Suspended)
                        throw Error("Only a suspended subscription can be resumed.",
                            "tenant_saas_subscription_status_invalid");
                    subscription.Status = subscription.CurrentPeriodEnd > DateTimeOffset.UtcNow
                        ? TenantSaasSubscriptionStatus.Active
                        : TenantSaasSubscriptionStatus.PastDue;
                    tenant.BillingStatus = subscription.Status == TenantSaasSubscriptionStatus.Active
                        ? BillingStatus.Active
                        : BillingStatus.PastDue;
                    break;
                case "cancelled":
                    subscription.Status = TenantSaasSubscriptionStatus.Cancelled;
                    subscription.CancelAtPeriodEnd = false;
                    subscription.CancelledAt = DateTimeOffset.UtcNow;
                    tenant.BillingStatus = BillingStatus.Cancelled;
                    foreach (var item in items.Where(value =>
                                 value.Status is TenantSaasSubscriptionItemStatus.Active
                                     or TenantSaasSubscriptionItemStatus.Scheduled))
                    {
                        item.Status = TenantSaasSubscriptionItemStatus.Cancelled;
                        item.EndsAt = DateTimeOffset.UtcNow > item.StartsAt
                            ? DateTimeOffset.UtcNow
                            : item.StartsAt.AddTicks(1);
                    }

                    break;
                case "extended":
                    var days = Math.Clamp(command.ExtendDays ?? 0, 1, 3650);
                    subscription.CurrentPeriodEnd = subscription.CurrentPeriodEnd.AddDays(days);
                    foreach (var item in items.Where(value =>
                                 value.Status is TenantSaasSubscriptionItemStatus.Active
                                     or TenantSaasSubscriptionItemStatus.Scheduled))
                        item.EndsAt = item.EndsAt.AddDays(days);
                    break;
                default:
                    throw new ArgumentOutOfRangeException(nameof(action));
            }

            subscription.LifecycleVersion++;
            AddAudit(db, actor.UserId, subscription.TenantId, $"platform.saas.subscription.{action}", subscription.Id,
                command.Reason);
            await db.SaveChangesAsync(token);
            await services.GetRequiredService<ITenantFeatureCacheInvalidator>()
                .InvalidateAsync(subscription.TenantId, token);
            return subscription;
        }, cancellationToken);
    }

    private Task<PlatformBillingRefund> ReviewRefundAsync(
        SaasCatalogActor actor,
        ReviewPlatformRefundCommand command,
        bool approve,
        CancellationToken cancellationToken)
    {
        return ExecuteAsync(approve ? "approve SaaS refund" : "reject SaaS refund", async (services, token) =>
        {
            var db = services.GetRequiredService<TikuDbContext>();
            var refund =
                await db.PlatformBillingRefunds.SingleOrDefaultAsync(value => value.Id == command.RefundId, token)
                ?? throw Error("Refund was not found.", "platform_billing_refund_not_found");
            if (refund.Status != PlatformBillingRefundStatus.Requested)
                throw Error("Refund is not awaiting review.", "platform_billing_refund_status_invalid");

            refund.Status = approve ? PlatformBillingRefundStatus.Processing : PlatformBillingRefundStatus.Cancelled;
            refund.ReviewedBy = actor.UserId;
            refund.ReviewedAt = DateTimeOffset.UtcNow;
            refund.ReviewReason = Required(command.Reason, "reason");
            AddAudit(db, actor.UserId, refund.TenantId,
                approve ? "platform.saas.refund.approved" : "platform.saas.refund.rejected",
                refund.Id,
                refund.ReviewReason);
            await db.SaveChangesAsync(token);
            return refund;
        }, cancellationToken);
    }

    private static void AddAudit(TikuDbContext db, Guid actorUserId, Guid tenantId, string action, Guid targetId,
        string? reason)
    {
        db.AuditLogs.Add(new AuditLog
        {
            TenantId = tenantId,
            ActorUserId = actorUserId,
            Action = action,
            TargetType = action.Contains("refund", StringComparison.Ordinal)
                ? "platform_billing_refunds"
                : "tenant_saas_subscriptions",
            TargetId = targetId.ToString(),
            Details = JsonSerializer.SerializeToElement(new { reason })
        });
    }

    private static string Required(string? value, string field)
    {
        return string.IsNullOrWhiteSpace(value) ? throw Error($"{field} is required.", "required_field") : value.Trim();
    }

    private static string Number(string prefix)
    {
        return $"{prefix}{DateTimeOffset.UtcNow:yyyyMMddHHmmss}{Guid.NewGuid():N}"[..32];
    }

    private static string Hash(string value)
    {
        return Convert
            .ToHexString(SHA256.HashData(Encoding.UTF8.GetBytes(value)))
            .ToLowerInvariant();
    }

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

    private static T Parse<T>(string value) where T : struct, Enum
    {
        return Enum.TryParse<T>(value, true, out var parsed)
            ? parsed
            : throw Error("Status is invalid.", "status_invalid");
    }

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

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