using Fitness_core_BE.Models; namespace Fitness_core_BE.Interfaces { public interface ICustomerSubscriptionRepository { Task AddCustomerSubscriptionAsync( CustomerSubscription subscription, CustomerSubItem initialSubItem, SubscriptionMember primaryMember, IReadOnlyCollection installments, CancellationToken ct = default); Task GetPlanByIdAsync(Guid customerPlanId, CancellationToken ct = default); Task IsActiveCustomerAsync(Guid customerId, CancellationToken ct = default); Task> GetSubItemsAsync( Guid subscriptionId, bool activeOnly, bool newestFirst, CancellationToken ct = default); Task AddSubItemAsync(CustomerSubItem subItem, CancellationToken ct = default); Task GetLatestInvoiceAsync(Guid subscriptionId, CancellationToken ct = default); Task GetApprovedPaymentTotalAsync(Guid invoiceId, CancellationToken ct = default); Task GetCardByIdAsync(Guid cardId, CancellationToken ct = default); Task GetCustomerSubscriptionById(Guid subscriptionId); Task> GetCustomerSubscriptionsByCustomerId(Guid customerId); /// True for an ordinary plan (no CustomerPlanClass links) or a class-linked /// plan with GrantsGymAccess == true. Task PlanGrantsGymAccessAsync(Guid customerPlanId, CancellationToken ct = default); /// The customer's active subscription whose plan grants gym access, if any -- /// at most one can exist (enforced by HasActiveGymAccessPlanAsync at assignment time). /// Primary-only, same as the old GetActiveSubscriptionByCustomerId this replaces. Task GetActiveGymAccessSubscriptionAsync(Guid customerId); /// The customer's active subscription on this exact plan, if any -- the /// idempotency check for re-assigning a class-only plan (which, unlike a gym plan, /// doesn't block on ANY other active plan, only a duplicate of itself). Task GetActiveSubscriptionForPlanAsync(Guid customerId, Guid customerPlanId); /// The subscription that covers booking a specific class, preferring one with /// quota left this cycle. Null if none of the customer's active subscriptions link to /// this class at all. Task GetActiveSubscriptionForClassAsync(Guid customerId, Guid facilityClassId); // Whether this customer is currently an active member (primary OR secondary/group) of // ANY gym-access subscription at this gym -- a class-only subscription never counts, so // unlimited concurrent class-only subscriptions are allowed. "One customer, one // GYM-ACCESS plan at a time" is enforced everywhere a gym plan gets assigned, not just // the primary path. Task HasActiveGymAccessPlanAsync(Guid customerId, CancellationToken ct = default); Task AddSubscriptionMemberAsync(SubscriptionMember member); Task GetActiveMemberCountAsync(Guid subscriptionId); Task GetSubscriptionMemberAsync(Guid subscriptionMemberId); Task GetActiveSubscriptionMemberByCustomerAsync(Guid subscriptionId, Guid customerId); Task> GetSubscriptionsForCustomerAsync(Guid customerId); Task> GetInstallmentsAsync(Guid subscriptionId); // Renewal-approval workflow (SubscriptionRenewalApprovalService.ApproveAsync): advances // RenewalDate only -- ExpiryDate is never touched again after creation. Task AdvanceRenewalDateAsync(Guid subscriptionId, DateTime newRenewalDate, CancellationToken ct = default); // Removes the subscription's current-cycle installment rows before a fresh schedule is // added for the newly-approved cycle. Hard delete, not soft-deactivate: a unique index on // (FKSubscriptionId, InstallmentNo) means a soft-deactivated row would still collide with // the new cycle's schedule restarting at InstallmentNo 1 on the same subscription. Task DeactivatePriorInstallmentsAsync(Guid subscriptionId, CancellationToken ct = default); Task AddInstallmentsAsync(IReadOnlyCollection installments, CancellationToken ct = default); } }