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(), } } // GetMostPlayedSoundtracks returns top N most played soundtracks with songs // GET /api/v1/statistics/soundtracks/most-played // // @Summary Get most played soundtracks // @Description Returns the top N most played soundtracks with their songs // @Tags statistics // @Accept json // @Produce json // @Param limit query int false "Number of results (default: 10)" // @Success 200 {array} backend.SoundtrackWithSongs // @Failure 400 {object} map[string]string // @Failure 500 {object} map[string]string // @Router /api/v1/statistics/soundtracks/most-played [get] func (h *StatisticsHandler) GetMostPlayedSoundtracks(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 } } soundtracks, err := h.statsBackend.GetMostPlayedSoundtracksWithSongs(int32(limit)) if err != nil { logging.GetLogger().Error("Failed to get most played soundtracks", zap.String("error", err.Error())) return ctx.JSON(http.StatusInternalServerError, map[string]string{"error": "Failed to get statistics"}) } return ctx.JSON(http.StatusOK, soundtracks) } // GetLeastPlayedSoundtracks returns top N least played soundtracks with songs // GET /api/v1/statistics/soundtracks/least-played // // @Summary Get least played soundtracks // @Description Returns the top N least played soundtracks with their songs // @Tags statistics // @Accept json // @Produce json // @Param limit query int false "Number of results (default: 10)" // @Success 200 {array} backend.SoundtrackWithSongs // @Failure 400 {object} map[string]string // @Failure 500 {object} map[string]string // @Router /api/v1/statistics/soundtracks/least-played [get] func (h *StatisticsHandler) GetLeastPlayedSoundtracks(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 } } soundtracks, err := h.statsBackend.GetLeastPlayedSoundtracksWithSongs(int32(limit)) if err != nil { logging.GetLogger().Error("Failed to get least played soundtracks", zap.String("error", err.Error())) return ctx.JSON(http.StatusInternalServerError, map[string]string{"error": "Failed to get statistics"}) } return ctx.JSON(http.StatusOK, soundtracks) } // GetMostPlayedSongs returns top N most played songs with soundtrack info // GET /api/v1/statistics/songs/most-played // // @Summary Get most played songs // @Description Returns the top N most played songs with their soundtrack 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.GetMostPlayedSongsWithSoundtrack(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 soundtrack info // GET /api/v1/statistics/songs/least-played // // @Summary Get least played songs // @Description Returns the top N least played songs with their soundtrack 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.GetLeastPlayedSongsWithSoundtrack(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) } // GetNeverPlayedSoundtracks returns soundtracks that have never been played // GET /api/v1/statistics/soundtracks/never-played // // @Summary Get never played soundtracks // @Description Returns all soundtracks that have never been played (times_played = 0) // @Tags statistics // @Accept json // @Produce json // @Success 200 {array} backend.SoundtrackWithSongs // @Failure 500 {object} map[string]string // @Router /api/v1/statistics/soundtracks/never-played [get] func (h *StatisticsHandler) GetNeverPlayedSoundtracks(ctx *echo.Context) error { soundtracks, err := h.statsBackend.GetNeverPlayedSoundtracks() if err != nil { logging.GetLogger().Error("Failed to get never played soundtracks", zap.String("error", err.Error())) return ctx.JSON(http.StatusInternalServerError, map[string]string{"error": "Failed to get statistics"}) } return ctx.JSON(http.StatusOK, soundtracks) } // GetLastPlayedSoundtracks returns most recently played soundtracks // GET /api/v1/statistics/soundtracks/last-played // // @Summary Get last played soundtracks // @Description Returns the most recently played soundtracks // @Tags statistics // @Accept json // @Produce json // @Param limit query int false "Number of results (default: 10)" // @Success 200 {array} backend.SoundtrackWithSongs // @Failure 400 {object} map[string]string // @Failure 500 {object} map[string]string // @Router /api/v1/statistics/soundtracks/last-played [get] func (h *StatisticsHandler) GetLastPlayedSoundtracks(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 } } soundtracks, err := h.statsBackend.GetLastPlayedSoundtracks(int32(limit)) if err != nil { logging.GetLogger().Error("Failed to get last played soundtracks", zap.String("error", err.Error())) return ctx.JSON(http.StatusInternalServerError, map[string]string{"error": "Failed to get statistics"}) } return ctx.JSON(http.StatusOK, soundtracks) } // GetOldestPlayedSoundtracks returns least recently played soundtracks // GET /api/v1/statistics/soundtracks/oldest-played // // @Summary Get oldest played soundtracks // @Description Returns the least recently played soundtracks (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.SoundtrackWithSongs // @Failure 400 {object} map[string]string // @Failure 500 {object} map[string]string // @Router /api/v1/statistics/soundtracks/oldest-played [get] func (h *StatisticsHandler) GetOldestPlayedSoundtracks(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 } } soundtracks, err := h.statsBackend.GetOldestPlayedSoundtracks(int32(limit)) if err != nil { logging.GetLogger().Error("Failed to get oldest played soundtracks", zap.String("error", err.Error())) return ctx.JSON(http.StatusInternalServerError, map[string]string{"error": "Failed to get statistics"}) } return ctx.JSON(http.StatusOK, soundtracks) } // 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) }