using System.Text.Json; using Tiku.Application.Backoffice; using Tiku.Application.PlatformBilling; using Tiku.Domain.Platform; namespace Tiku.Application.PlatformAdmin; public static class PlatformApprovalPolicyCodes { public const string TenantArchive = "tenant.archive"; public const string SuperAdminGrant = "security.super-admin-grant"; public const string PaymentChannelChange = "payment.channel-change"; public const string FinancialAdjustment = "finance.adjustment"; } public sealed record PlatformApprovalActor(Guid UserId, IReadOnlySet Permissions); public sealed record PlatformCommandSubmission( string ExecutionStatus, JsonElement? Result, PlatformApprovalRequestItem? ApprovalRequest); public sealed record PlatformApprovalRequestItem( Guid Id, string RequestNo, string PolicyCode, int PolicyVersion, PlatformApprovalRequestStatus Status, Guid RequestedBy, Guid? DecidedBy, string RequiredPermission, string CommandType, string TargetType, string TargetId, int? AmountCents, JsonElement RequestSnapshot, string? RequestReason, string? DecisionReason, string? Error, DateTimeOffset ExpiresAt, DateTimeOffset CreatedAt, DateTimeOffset UpdatedAt); public sealed record PlatformApprovalPolicyItem( Guid Id, string Code, string Name, string RequiredPermission, bool Enabled, bool AlwaysRequireApproval, int? AmountThresholdCents, int Version, int ExpiresAfterHours, JsonElement Conditions); public sealed record UpdatePlatformApprovalPolicyCommand( string Code, bool Enabled, bool AlwaysRequireApproval, int? AmountThresholdCents, int ExpiresAfterHours, JsonElement Conditions); public interface IPlatformApprovalService { Task> ListAsync(PlatformApprovalActor actor, PlatformApprovalRequestStatus? status, int limit, CancellationToken cancellationToken = default); Task GetAsync(PlatformApprovalActor actor, Guid requestId, CancellationToken cancellationToken = default); Task> ListPoliciesAsync(PlatformApprovalActor actor, CancellationToken cancellationToken = default); Task UpdatePolicyAsync(PlatformApprovalActor actor, UpdatePlatformApprovalPolicyCommand command, CancellationToken cancellationToken = default); Task ApproveAsync(PlatformApprovalActor actor, Guid requestId, string reason, CancellationToken cancellationToken = default); Task RejectAsync(PlatformApprovalActor actor, Guid requestId, string reason, CancellationToken cancellationToken = default); Task CancelAsync(PlatformApprovalActor actor, Guid requestId, string reason, CancellationToken cancellationToken = default); Task ProcessApprovedAsync(int batchSize = 20, CancellationToken cancellationToken = default); Task SubmitRefundAsync(SaasCatalogActor actor, RequestPlatformRefundCommand command, CancellationToken cancellationToken = default); Task ConfirmManualPaymentAsync(SaasCatalogActor actor, ConfirmManualPaymentCommand command, string idempotencyKey, CancellationToken cancellationToken = default); Task UpdateTenantStatusAsync(PlatformAdminActor actor, UpdatePlatformTenantStatusCommand command, string idempotencyKey, CancellationToken cancellationToken = default); Task UpsertPaymentChannelAsync(PlatformCapabilityActor actor, UpsertPlatformPaymentChannelCommand command, string idempotencyKey, CancellationToken cancellationToken = default); Task ReplaceRoleBindingsAsync(BackofficeActor actor, ReplaceRoleBindingsCommand command, string idempotencyKey, CancellationToken cancellationToken = default); } public sealed class PlatformApprovalException(string message, string code) : Exception(message) { public string Code { get; } = code; } public static class PlatformApprovalRules { public static bool RequiresApproval(bool enabled, bool alwaysRequireApproval, int? amountThresholdCents, int? amountCents) { return enabled && (alwaysRequireApproval || (amountThresholdCents.HasValue && amountCents >= amountThresholdCents)); } public static string? DecisionDenialCode( PlatformApprovalRequestStatus status, Guid requestedBy, Guid decisionActor, DateTimeOffset expiresAt, string requiredPermission, IReadOnlySet permissions, DateTimeOffset now) { if (status != PlatformApprovalRequestStatus.Pending) return "approval_request_not_pending"; if (expiresAt <= now) return "approval_request_expired"; if (requestedBy == decisionActor) return "approval_maker_checker_required"; return permissions.Contains(requiredPermission) ? null : "approval_business_permission_required"; } }