package server import ( "net/http" "strconv" "music-server/internal/backend" "music-server/internal/logging" "github.com/labstack/echo/v5" "go.uber.org/zap" ) // StatisticsHandler handles statistics-related HTTP requests type StatisticsHandler struct { statsBackend *backend.StatisticsHandler } // NewStatisticsHandler creates a new StatisticsHandler func NewStatisticsHandler() *StatisticsHandler { return &StatisticsHandler{ statsBackend: backend.NewStatisticsHandler(), } } // GetMostPlayedGames returns top N most played games with songs // GET /api/v1/statistics/games/most-played // // @Summary Get most played games // @Description Returns the top N most played games with their songs // @Tags statistics // @Accept json // @Produce json // @Param limit query int false "Number of results (default: 10)" // @Success 200 {array} backend.GameWithSongs // @Failure 400 {object} map[string]string // @Failure 500 {object} map[string]string // @Router /api/v1/statistics/games/most-played [get] func (h *StatisticsHandler) GetMostPlayedGames(ctx *echo.Context) error { limit := 10 // default limitStr := ctx.QueryParam("limit") if limitStr != "" { var err error limit, err = strconv.Atoi(limitStr) if err != nil || limit <= 0 { return ctx.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid limit parameter"}) } // Cap at 100 for performance if limit > 100 { limit = 100 } } games, err := h.statsBackend.GetMostPlayedGamesWithSongs(int32(limit)) if err != nil { logging.GetLogger().Error("Failed to get most played games", zap.String("error", err.Error())) return ctx.JSON(http.StatusInternalServerError, map[string]string{"error": "Failed to get statistics"}) } return ctx.JSON(http.StatusOK, games) } // GetLeastPlayedGames returns top N least played games with songs // GET /api/v1/statistics/games/least-played // // @Summary Get least played games // @Description Returns the top N least played games with their songs // @Tags statistics // @Accept json // @Produce json // @Param limit query int false "Number of results (default: 10)" // @Success 200 {array} backend.GameWithSongs // @Failure 400 {object} map[string]string // @Failure 500 {object} map[string]string // @Router /api/v1/statistics/games/least-played [get] func (h *StatisticsHandler) GetLeastPlayedGames(ctx *echo.Context) error { limit := 10 limitStr := ctx.QueryParam("limit") if limitStr != "" { var err error limit, err = strconv.Atoi(limitStr) if err != nil || limit <= 0 { return ctx.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid limit parameter"}) } if limit > 100 { limit = 100 } } games, err := h.statsBackend.GetLeastPlayedGamesWithSongs(int32(limit)) if err != nil { logging.GetLogger().Error("Failed to get least played games", zap.String("error", err.Error())) return ctx.JSON(http.StatusInternalServerError, map[string]string{"error": "Failed to get statistics"}) } return ctx.JSON(http.StatusOK, games) } // GetMostPlayedSongs returns top N most played songs with game info // GET /api/v1/statistics/songs/most-played // // @Summary Get most played songs // @Description Returns the top N most played songs with their game info // @Tags statistics // @Accept json // @Produce json // @Param limit query int false "Number of results (default: 10)" // @Success 200 {array} backend.SongInfoForStats // @Failure 400 {object} map[string]string // @Failure 500 {object} map[string]string // @Router /api/v1/statistics/songs/most-played [get] func (h *StatisticsHandler) GetMostPlayedSongs(ctx *echo.Context) error { limit := 10 limitStr := ctx.QueryParam("limit") if limitStr != "" { var err error limit, err = strconv.Atoi(limitStr) if err != nil || limit <= 0 { return ctx.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid limit parameter"}) } if limit > 100 { limit = 100 } } songs, err := h.statsBackend.GetMostPlayedSongsWithGame(int32(limit)) if err != nil { logging.GetLogger().Error("Failed to get most played songs", zap.String("error", err.Error())) return ctx.JSON(http.StatusInternalServerError, map[string]string{"error": "Failed to get statistics"}) } return ctx.JSON(http.StatusOK, songs) } // GetLeastPlayedSongs returns top N least played songs with game info // GET /api/v1/statistics/songs/least-played // // @Summary Get least played songs // @Description Returns the top N least played songs with their game info // @Tags statistics // @Accept json // @Produce json // @Param limit query int false "Number of results (default: 10)" // @Success 200 {array} backend.SongInfoForStats // @Failure 400 {object} map[string]string // @Failure 500 {object} map[string]string // @Router /api/v1/statistics/songs/least-played [get] func (h *StatisticsHandler) GetLeastPlayedSongs(ctx *echo.Context) error { limit := 10 limitStr := ctx.QueryParam("limit") if limitStr != "" { var err error limit, err = strconv.Atoi(limitStr) if err != nil || limit <= 0 { return ctx.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid limit parameter"}) } if limit > 100 { limit = 100 } } songs, err := h.statsBackend.GetLeastPlayedSongsWithGame(int32(limit)) if err != nil { logging.GetLogger().Error("Failed to get least played songs", zap.String("error", err.Error())) return ctx.JSON(http.StatusInternalServerError, map[string]string{"error": "Failed to get statistics"}) } return ctx.JSON(http.StatusOK, songs) } // GetNeverPlayedGames returns games that have never been played // GET /api/v1/statistics/games/never-played // // @Summary Get never played games // @Description Returns all games that have never been played (times_played = 0) // @Tags statistics // @Accept json // @Produce json // @Success 200 {array} backend.GameWithSongs // @Failure 500 {object} map[string]string // @Router /api/v1/statistics/games/never-played [get] func (h *StatisticsHandler) GetNeverPlayedGames(ctx *echo.Context) error { games, err := h.statsBackend.GetNeverPlayedGames() if err != nil { logging.GetLogger().Error("Failed to get never played games", zap.String("error", err.Error())) return ctx.JSON(http.StatusInternalServerError, map[string]string{"error": "Failed to get statistics"}) } return ctx.JSON(http.StatusOK, games) } // GetLastPlayedGames returns most recently played games // GET /api/v1/statistics/games/last-played // // @Summary Get last played games // @Description Returns the most recently played games // @Tags statistics // @Accept json // @Produce json // @Param limit query int false "Number of results (default: 10)" // @Success 200 {array} backend.GameWithSongs // @Failure 400 {object} map[string]string // @Failure 500 {object} map[string]string // @Router /api/v1/statistics/games/last-played [get] func (h *StatisticsHandler) GetLastPlayedGames(ctx *echo.Context) error { limit := 10 limitStr := ctx.QueryParam("limit") if limitStr != "" { var err error limit, err = strconv.Atoi(limitStr) if err != nil || limit <= 0 { return ctx.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid limit parameter"}) } if limit > 100 { limit = 100 } } games, err := h.statsBackend.GetLastPlayedGames(int32(limit)) if err != nil { logging.GetLogger().Error("Failed to get last played games", zap.String("error", err.Error())) return ctx.JSON(http.StatusInternalServerError, map[string]string{"error": "Failed to get statistics"}) } return ctx.JSON(http.StatusOK, games) } // GetOldestPlayedGames returns least recently played games // GET /api/v1/statistics/games/oldest-played // // @Summary Get oldest played games // @Description Returns the least recently played games (that have been played at least once) // @Tags statistics // @Accept json // @Produce json // @Param limit query int false "Number of results (default: 10)" // @Success 200 {array} backend.GameWithSongs // @Failure 400 {object} map[string]string // @Failure 500 {object} map[string]string // @Router /api/v1/statistics/games/oldest-played [get] func (h *StatisticsHandler) GetOldestPlayedGames(ctx *echo.Context) error { limit := 10 limitStr := ctx.QueryParam("limit") if limitStr != "" { var err error limit, err = strconv.Atoi(limitStr) if err != nil || limit <= 0 { return ctx.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid limit parameter"}) } if limit > 100 { limit = 100 } } games, err := h.statsBackend.GetOldestPlayedGames(int32(limit)) if err != nil { logging.GetLogger().Error("Failed to get oldest played games", zap.String("error", err.Error())) return ctx.JSON(http.StatusInternalServerError, map[string]string{"error": "Failed to get statistics"}) } return ctx.JSON(http.StatusOK, games) } // GetStatisticsSummary returns overall statistics // GET /api/v1/statistics/summary // // @Summary Get statistics summary // @Description Returns overall statistics about the music library // @Tags statistics // @Accept json // @Produce json // @Success 200 {object} backend.StatisticsSummary // @Failure 500 {object} map[string]string // @Router /api/v1/statistics/summary [get] func (h *StatisticsHandler) GetStatisticsSummary(ctx *echo.Context) error { summary, err := h.statsBackend.GetStatisticsSummary() if err != nil { logging.GetLogger().Error("Failed to get statistics summary", zap.String("error", err.Error())) return ctx.JSON(http.StatusInternalServerError, map[string]string{"error": "Failed to get statistics"}) } return ctx.JSON(http.StatusOK, summary) }