package handler import ( "github.com/Wei-Shaw/sub2api/internal/model" "github.com/Wei-Shaw/sub2api/internal/pkg/response" "github.com/Wei-Shaw/sub2api/internal/service" "github.com/gin-gonic/gin" ) // SubscriptionSummaryItem represents a subscription item in summary type SubscriptionSummaryItem struct { ID int64 `json:"id"` GroupID int64 `json:"group_id"` GroupName string `json:"group_name"` Status string `json:"status"` DailyUsedUSD float64 `json:"daily_used_usd,omitempty"` DailyLimitUSD float64 `json:"daily_limit_usd,omitempty"` WeeklyUsedUSD float64 `json:"weekly_used_usd,omitempty"` WeeklyLimitUSD float64 `json:"weekly_limit_usd,omitempty"` MonthlyUsedUSD float64 `json:"monthly_used_usd,omitempty"` MonthlyLimitUSD float64 `json:"monthly_limit_usd,omitempty"` ExpiresAt *string `json:"expires_at,omitempty"` } // SubscriptionProgressInfo represents subscription with progress info type SubscriptionProgressInfo struct { Subscription *model.UserSubscription `json:"subscription"` Progress *service.SubscriptionProgress `json:"progress"` } // SubscriptionHandler handles user subscription operations type SubscriptionHandler struct { subscriptionService *service.SubscriptionService } // NewSubscriptionHandler creates a new user subscription handler func NewSubscriptionHandler(subscriptionService *service.SubscriptionService) *SubscriptionHandler { return &SubscriptionHandler{ subscriptionService: subscriptionService, } } // List handles listing current user's subscriptions // GET /api/v1/subscriptions func (h *SubscriptionHandler) List(c *gin.Context) { user, exists := c.Get("user") if !exists { response.Unauthorized(c, "User not found in context") return } u, ok := user.(*model.User) if !ok { response.InternalError(c, "Invalid user in context") return } subscriptions, err := h.subscriptionService.ListUserSubscriptions(c.Request.Context(), u.ID) if err != nil { response.InternalError(c, "Failed to list subscriptions: "+err.Error()) return } response.Success(c, subscriptions) } // GetActive handles getting current user's active subscriptions // GET /api/v1/subscriptions/active func (h *SubscriptionHandler) GetActive(c *gin.Context) { user, exists := c.Get("user") if !exists { response.Unauthorized(c, "User not found in context") return } u, ok := user.(*model.User) if !ok { response.InternalError(c, "Invalid user in context") return } subscriptions, err := h.subscriptionService.ListActiveUserSubscriptions(c.Request.Context(), u.ID) if err != nil { response.InternalError(c, "Failed to get active subscriptions: "+err.Error()) return } response.Success(c, subscriptions) } // GetProgress handles getting subscription progress for current user // GET /api/v1/subscriptions/progress func (h *SubscriptionHandler) GetProgress(c *gin.Context) { user, exists := c.Get("user") if !exists { response.Unauthorized(c, "User not found in context") return } u, ok := user.(*model.User) if !ok { response.InternalError(c, "Invalid user in context") return } // Get all active subscriptions with progress subscriptions, err := h.subscriptionService.ListActiveUserSubscriptions(c.Request.Context(), u.ID) if err != nil { response.InternalError(c, "Failed to get subscriptions: "+err.Error()) return } result := make([]SubscriptionProgressInfo, 0, len(subscriptions)) for i := range subscriptions { sub := &subscriptions[i] progress, err := h.subscriptionService.GetSubscriptionProgress(c.Request.Context(), sub.ID) if err != nil { // Skip subscriptions with errors continue } result = append(result, SubscriptionProgressInfo{ Subscription: sub, Progress: progress, }) } response.Success(c, result) } // GetSummary handles getting a summary of current user's subscription status // GET /api/v1/subscriptions/summary func (h *SubscriptionHandler) GetSummary(c *gin.Context) { user, exists := c.Get("user") if !exists { response.Unauthorized(c, "User not found in context") return } u, ok := user.(*model.User) if !ok { response.InternalError(c, "Invalid user in context") return } // Get all active subscriptions subscriptions, err := h.subscriptionService.ListActiveUserSubscriptions(c.Request.Context(), u.ID) if err != nil { response.InternalError(c, "Failed to get subscriptions: "+err.Error()) return } var totalUsed float64 items := make([]SubscriptionSummaryItem, 0, len(subscriptions)) for _, sub := range subscriptions { item := SubscriptionSummaryItem{ ID: sub.ID, GroupID: sub.GroupID, Status: sub.Status, DailyUsedUSD: sub.DailyUsageUSD, WeeklyUsedUSD: sub.WeeklyUsageUSD, MonthlyUsedUSD: sub.MonthlyUsageUSD, } // Add group info if preloaded if sub.Group != nil { item.GroupName = sub.Group.Name if sub.Group.DailyLimitUSD != nil { item.DailyLimitUSD = *sub.Group.DailyLimitUSD } if sub.Group.WeeklyLimitUSD != nil { item.WeeklyLimitUSD = *sub.Group.WeeklyLimitUSD } if sub.Group.MonthlyLimitUSD != nil { item.MonthlyLimitUSD = *sub.Group.MonthlyLimitUSD } } // Format expiration time if !sub.ExpiresAt.IsZero() { formatted := sub.ExpiresAt.Format("2006-01-02T15:04:05Z07:00") item.ExpiresAt = &formatted } // Track total usage (use monthly as the most comprehensive) totalUsed += sub.MonthlyUsageUSD items = append(items, item) } summary := struct { ActiveCount int `json:"active_count"` TotalUsedUSD float64 `json:"total_used_usd"` Subscriptions []SubscriptionSummaryItem `json:"subscriptions"` }{ ActiveCount: len(subscriptions), TotalUsedUSD: totalUsed, Subscriptions: items, } response.Success(c, summary) }