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) }