using System.Security.Cryptography; using System.Text.Json; using HrynCo.NotificationService.DAL.Abstract.Push; using HrynCo.NotificationService.DAL.Abstract.PushChannels; namespace HrynCo.NotificationService.Worker.Services.Push; public sealed class PushChannelTestService(IPushChannelRepository channels, IFirebasePushClient firebase) { public async Task TestAsync(PushChannelTestSettings settings, bool send, CancellationToken ct) { var message = new PushChannelTestMessage { Token = settings.RecipientToken?.Trim() ?? "", Version = settings.Version, BindingId = settings.BindingId, ActionKey = settings.ActionKey, ButtonLabel = settings.ButtonLabel }; if (send && message.Validate() is { } error) return new PushDeliveryOutcome(error); var project = settings.FirebaseProjectId?.Trim() ?? ""; var json = await ResolveCredentialsAsync(settings, project, ct); if (string.IsNullOrWhiteSpace(json)) return new PushDeliveryOutcome("Provide credentials for this project. Saved credentials can only be used with the unchanged project."); return await ExecuteAsync(json, project, message, send, ct); } private async Task ResolveCredentialsAsync(PushChannelTestSettings settings, string project, CancellationToken ct) { if (!string.IsNullOrWhiteSpace(settings.CredentialsJson)) return settings.CredentialsJson; if (settings.ChannelId is not Guid id) return null; var saved = await channels.GetByIdAsync(id, ct); return saved?.FirebaseProjectId == project ? await channels.GetCredentialsAsync(id, ct) : null; } private async Task ExecuteAsync(string json, string project, PushChannelTestMessage message, bool send, CancellationToken ct) { try { var credentials = FirebaseServiceAccountCredentials.Parse(json, project); return send ? await firebase.SendTestAsync(message, credentials, ct) : await firebase.CheckAuthorizationAsync(credentials, ct); } catch (Exception error) when (error is JsonException or FormatException or ArgumentException or CryptographicException) { return new PushDeliveryOutcome("Provide a valid Firebase service account JSON for this project."); } catch (PushRetryException) { return new PushDeliveryOutcome("Firebase is temporarily unavailable or throttled. Try again later; acceptance was not confirmed."); } } }