You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

94 lines
2.7 KiB
Go

package handler
import (
"log/slog"
"net/http"
"github.com/denisovdennis/autohero/internal/model"
"github.com/denisovdennis/autohero/internal/storage"
)
// AchievementHandler serves achievement API endpoints.
type AchievementHandler struct {
achievementStore *storage.AchievementStore
heroStore *storage.HeroStore
logger *slog.Logger
}
// NewAchievementHandler creates a new AchievementHandler.
func NewAchievementHandler(achievementStore *storage.AchievementStore, heroStore *storage.HeroStore, logger *slog.Logger) *AchievementHandler {
return &AchievementHandler{
achievementStore: achievementStore,
heroStore: heroStore,
logger: logger,
}
}
// GetHeroAchievements returns all achievements with unlocked status for the hero.
// GET /api/v1/hero/achievements
func (h *AchievementHandler) GetHeroAchievements(w http.ResponseWriter, r *http.Request) {
telegramID, ok := resolveTelegramID(r)
if !ok {
writeJSON(w, http.StatusBadRequest, map[string]string{
"error": "missing telegramId",
})
return
}
hero, err := h.heroStore.GetByTelegramID(r.Context(), telegramID)
if err != nil {
h.logger.Error("failed to get hero for achievements", "error", err)
writeJSON(w, http.StatusInternalServerError, map[string]string{
"error": "failed to load hero",
})
return
}
if hero == nil {
writeJSON(w, http.StatusNotFound, map[string]string{
"error": "hero not found",
})
return
}
// Load all achievement definitions.
allAchievements, err := h.achievementStore.ListAchievements(r.Context())
if err != nil {
h.logger.Error("failed to list achievements", "error", err)
writeJSON(w, http.StatusInternalServerError, map[string]string{
"error": "failed to load achievements",
})
return
}
// Load hero's unlocked achievements.
heroAchievements, err := h.achievementStore.GetHeroAchievements(r.Context(), hero.ID)
if err != nil {
h.logger.Error("failed to get hero achievements", "hero_id", hero.ID, "error", err)
writeJSON(w, http.StatusInternalServerError, map[string]string{
"error": "failed to load hero achievements",
})
return
}
// Build lookup of unlocked achievement IDs.
unlockedMap := make(map[string]*model.HeroAchievement, len(heroAchievements))
for i := range heroAchievements {
unlockedMap[heroAchievements[i].AchievementID] = &heroAchievements[i]
}
// Build response combining definitions with unlock status.
views := make([]model.AchievementView, 0, len(allAchievements))
for _, a := range allAchievements {
view := model.AchievementView{
Achievement: a,
}
if ha, ok := unlockedMap[a.ID]; ok {
view.Unlocked = true
view.UnlockedAt = &ha.UnlockedAt
}
views = append(views, view)
}
writeJSON(w, http.StatusOK, views)
}