using System.Data; using Dapper; using PlotLine.Models; namespace PlotLine.Data; public interface ISubscriptionRepository { Task GetCurrentSubscriptionAsync(int userId); Task GetSubscriptionLevelAsync(int subscriptionLevelId); Task> GetActiveSubscriptionLevelsAsync(); Task GetOwnedBookCountAsync(int userId); Task GetChapterCountForBookAsync(int bookId); Task GetSceneCountForBookAsync(int bookId); Task GetCharacterCountForProjectAsync(int projectId); Task GetCollaboratorCountForOwnerAsync(int userId); Task GetUsageSummaryForOwnerAsync(int userId); Task GetSubscriptionLevelByStripePriceAsync(string stripePriceId); Task SetStripeCustomerAsync(int userId, string stripeCustomerId); Task TryBeginStripeWebhookEventAsync(string stripeEventId, string eventType); Task CompleteStripeWebhookEventAsync(string stripeEventId, string processingStatus, string? errorMessage = null); Task ApplyStripeStateAsync(StripeSubscriptionSyncState state); Task DowngradeToDraftDeskAsync(string? stripeCustomerId, string? stripeSubscriptionId, string? lastStripeEventId); Task RecordPaymentFailureAsync(string? stripeCustomerId, string? stripeSubscriptionId, string? lastStripeEventId); Task ClearPaymentFailureAsync(string? stripeCustomerId, string? stripeSubscriptionId, string? lastStripeEventId); } public sealed class SubscriptionRepository(ISqlConnectionFactory connectionFactory) : ISubscriptionRepository { public async Task GetCurrentSubscriptionAsync(int userId) { using var connection = connectionFactory.CreateConnection(); return await connection.QuerySingleOrDefaultAsync( "dbo.UserSubscription_GetCurrent", new { UserID = userId }, commandType: CommandType.StoredProcedure); } public async Task GetSubscriptionLevelAsync(int subscriptionLevelId) { using var connection = connectionFactory.CreateConnection(); return await connection.QuerySingleOrDefaultAsync( "dbo.SubscriptionLevel_Get", new { SubscriptionLevelID = subscriptionLevelId }, commandType: CommandType.StoredProcedure); } public async Task> GetActiveSubscriptionLevelsAsync() { using var connection = connectionFactory.CreateConnection(); var rows = await connection.QueryAsync( "dbo.SubscriptionLevel_ListActive", commandType: CommandType.StoredProcedure); return rows.ToList(); } public async Task GetOwnedBookCountAsync(int userId) { using var connection = connectionFactory.CreateConnection(); return await connection.QuerySingleAsync( "dbo.SubscriptionUsage_BookCountForOwner", new { UserID = userId }, commandType: CommandType.StoredProcedure); } public async Task GetChapterCountForBookAsync(int bookId) { using var connection = connectionFactory.CreateConnection(); return await connection.QuerySingleAsync( "dbo.SubscriptionUsage_ChapterCountForBook", new { BookID = bookId }, commandType: CommandType.StoredProcedure); } public async Task GetSceneCountForBookAsync(int bookId) { using var connection = connectionFactory.CreateConnection(); return await connection.QuerySingleAsync( "dbo.SubscriptionUsage_SceneCountForBook", new { BookID = bookId }, commandType: CommandType.StoredProcedure); } public async Task GetCharacterCountForProjectAsync(int projectId) { using var connection = connectionFactory.CreateConnection(); return await connection.QuerySingleAsync( "dbo.SubscriptionUsage_CharacterCountForProject", new { ProjectID = projectId }, commandType: CommandType.StoredProcedure); } public async Task GetCollaboratorCountForOwnerAsync(int userId) { using var connection = connectionFactory.CreateConnection(); return await connection.QuerySingleAsync( "dbo.SubscriptionUsage_CollaboratorCountForOwner", new { UserID = userId }, commandType: CommandType.StoredProcedure); } public async Task GetUsageSummaryForOwnerAsync(int userId) { using var connection = connectionFactory.CreateConnection(); return await connection.QuerySingleAsync( "dbo.SubscriptionUsage_GetSummaryForOwner", new { UserID = userId }, commandType: CommandType.StoredProcedure); } public async Task GetSubscriptionLevelByStripePriceAsync(string stripePriceId) { using var connection = connectionFactory.CreateConnection(); return await connection.QuerySingleOrDefaultAsync( "dbo.SubscriptionLevel_GetByStripePrice", new { StripePriceId = stripePriceId }, commandType: CommandType.StoredProcedure); } public async Task SetStripeCustomerAsync(int userId, string stripeCustomerId) { using var connection = connectionFactory.CreateConnection(); await connection.ExecuteAsync( "dbo.UserSubscription_SetStripeCustomer", new { UserID = userId, StripeCustomerId = stripeCustomerId }, commandType: CommandType.StoredProcedure); } public async Task TryBeginStripeWebhookEventAsync(string stripeEventId, string eventType) { using var connection = connectionFactory.CreateConnection(); return await connection.QuerySingleAsync( "dbo.StripeWebhookEvent_TryBegin", new { StripeEventId = stripeEventId, EventType = eventType }, commandType: CommandType.StoredProcedure); } public async Task CompleteStripeWebhookEventAsync(string stripeEventId, string processingStatus, string? errorMessage = null) { using var connection = connectionFactory.CreateConnection(); await connection.ExecuteAsync( "dbo.StripeWebhookEvent_Complete", new { StripeEventId = stripeEventId, ProcessingStatus = processingStatus, ErrorMessage = errorMessage }, commandType: CommandType.StoredProcedure); } public async Task ApplyStripeStateAsync(StripeSubscriptionSyncState state) { using var connection = connectionFactory.CreateConnection(); await connection.ExecuteAsync( "dbo.UserSubscription_ApplyStripeState", state, commandType: CommandType.StoredProcedure); } public async Task DowngradeToDraftDeskAsync(string? stripeCustomerId, string? stripeSubscriptionId, string? lastStripeEventId) { using var connection = connectionFactory.CreateConnection(); await connection.ExecuteAsync( "dbo.UserSubscription_DowngradeToDraftDesk", new { StripeCustomerId = stripeCustomerId, StripeSubscriptionId = stripeSubscriptionId, LastStripeEventId = lastStripeEventId }, commandType: CommandType.StoredProcedure); } public async Task RecordPaymentFailureAsync(string? stripeCustomerId, string? stripeSubscriptionId, string? lastStripeEventId) { using var connection = connectionFactory.CreateConnection(); await connection.ExecuteAsync( "dbo.UserSubscription_RecordPaymentFailure", new { StripeCustomerId = stripeCustomerId, StripeSubscriptionId = stripeSubscriptionId, LastStripeEventId = lastStripeEventId }, commandType: CommandType.StoredProcedure); } public async Task ClearPaymentFailureAsync(string? stripeCustomerId, string? stripeSubscriptionId, string? lastStripeEventId) { using var connection = connectionFactory.CreateConnection(); await connection.ExecuteAsync( "dbo.UserSubscription_ClearPaymentFailure", new { StripeCustomerId = stripeCustomerId, StripeSubscriptionId = stripeSubscriptionId, LastStripeEventId = lastStripeEventId }, commandType: CommandType.StoredProcedure); } }