using DysonNetwork.Sphere.Account;
using Microsoft.EntityFrameworkCore;
using NodaTime;
namespace DysonNetwork.Sphere.Post;
public class PublisherSubscriptionService(AppDatabase db, NotificationService nty)
{
///
/// Checks if a subscription exists between the account and publisher
///
/// The account ID
/// The publisher ID
/// True if a subscription exists, false otherwise
public async Task SubscriptionExistsAsync(long accountId, long publisherId)
{
return await db.PublisherSubscriptions
.AnyAsync(ps => ps.AccountId == accountId &&
ps.PublisherId == publisherId &&
ps.Status == SubscriptionStatus.Active);
}
///
/// Gets a subscription by account and publisher ID
///
/// The account ID
/// The publisher ID
/// The subscription or null if not found
public async Task GetSubscriptionAsync(long accountId, long publisherId)
{
return await db.PublisherSubscriptions
.Include(ps => ps.Publisher)
.FirstOrDefaultAsync(ps => ps.AccountId == accountId && ps.PublisherId == publisherId);
}
///
/// Notifies all subscribers about a new post from a publisher
///
/// The new post
/// The number of subscribers notified
public async Task NotifySubscribersPostAsync(Post post)
{
var subscribers = await db.PublisherSubscriptions
.Include(ps => ps.Account)
.Where(ps => ps.PublisherId == post.Publisher.Id &&
ps.Status == SubscriptionStatus.Active)
.ToListAsync();
if (subscribers.Count == 0)
return 0;
// Create notification data
var title = $"@{post.Publisher.Name} Posted";
var message = !string.IsNullOrEmpty(post.Title)
? post.Title
: (post.Content?.Length > 100
? string.Concat(post.Content.AsSpan(0, 97), "...")
: post.Content);
// Data to include with the notification
var data = new Dictionary
{
{ "post_id", post.Id.ToString() },
{ "publisher_id", post.Publisher.Id.ToString() }
};
// Notify each subscriber
var notifiedCount = 0;
foreach (var subscription in subscribers)
{
try
{
await nty.SendNotification(
subscription.Account,
"posts.new",
title,
post.Description?.Length > 40 ? post.Description[..37] + "..." : post.Description,
message,
data
);
notifiedCount++;
}
catch (Exception)
{
// Log the error but continue with other notifications
// We don't want one failed notification to stop the others
}
}
return notifiedCount;
}
///
/// Gets all active subscriptions for an account
///
/// The account ID
/// A list of active subscriptions
public async Task> GetAccountSubscriptionsAsync(long accountId)
{
return await db.PublisherSubscriptions
.Include(ps => ps.Publisher)
.Where(ps => ps.AccountId == accountId && ps.Status == SubscriptionStatus.Active)
.ToListAsync();
}
///
/// Gets all active subscribers for a publisher
///
/// The publisher ID
/// A list of active subscriptions
public async Task> GetPublisherSubscribersAsync(long publisherId)
{
return await db.PublisherSubscriptions
.Include(ps => ps.Account)
.Where(ps => ps.PublisherId == publisherId && ps.Status == SubscriptionStatus.Active)
.ToListAsync();
}
///
/// Creates a new subscription between an account and a publisher
///
/// The account ID
/// The publisher ID
/// Optional subscription tier
/// The created subscription
public async Task CreateSubscriptionAsync(
long accountId,
long publisherId,
int tier = 0
)
{
// Check if a subscription already exists
var existingSubscription = await GetSubscriptionAsync(accountId, publisherId);
if (existingSubscription != null)
{
// If it exists but is not active, reactivate it
if (existingSubscription.Status == SubscriptionStatus.Active) return existingSubscription;
existingSubscription.Status = SubscriptionStatus.Active;
existingSubscription.Tier = tier;
await db.SaveChangesAsync();
return existingSubscription;
// If it's already active, just return it
}
// Create a new subscription
var subscription = new PublisherSubscription
{
AccountId = accountId,
PublisherId = publisherId,
Status = SubscriptionStatus.Active,
Tier = tier,
};
db.PublisherSubscriptions.Add(subscription);
await db.SaveChangesAsync();
return subscription;
}
///
/// Cancels a subscription
///
/// The account ID
/// The publisher ID
/// True if the subscription was cancelled, false if it wasn't found
public async Task CancelSubscriptionAsync(long accountId, long publisherId)
{
var subscription = await GetSubscriptionAsync(accountId, publisherId);
if (subscription is not { Status: SubscriptionStatus.Active })
{
return false;
}
subscription.Status = SubscriptionStatus.Cancelled;
await db.SaveChangesAsync();
return true;
}
}