package handler

import (
	"log/slog"
	"net/http"
	"time"

	"github.com/go-chi/chi/v5"

	"github.com/denisovdennis/autohero/internal/storage"
)

// DailyTaskHandler serves daily/weekly task API endpoints.
type DailyTaskHandler struct {
	taskStore *storage.DailyTaskStore
	heroStore *storage.HeroStore
	logger    *slog.Logger
}

// NewDailyTaskHandler creates a new DailyTaskHandler.
func NewDailyTaskHandler(taskStore *storage.DailyTaskStore, heroStore *storage.HeroStore, logger *slog.Logger) *DailyTaskHandler {
	return &DailyTaskHandler{
		taskStore: taskStore,
		heroStore: heroStore,
		logger:    logger,
	}
}

// ListHeroTasks returns current daily and weekly tasks with progress.
// GET /api/v1/hero/tasks
func (h *DailyTaskHandler) ListHeroTasks(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 tasks", "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
	}

	// Lazily create task rows for the current period.
	now := time.Now()
	if err := h.taskStore.EnsureHeroTasks(r.Context(), hero.ID, now); err != nil {
		h.logger.Error("failed to ensure hero tasks", "hero_id", hero.ID, "error", err)
		writeJSON(w, http.StatusInternalServerError, map[string]string{
			"error": "failed to initialize tasks",
		})
		return
	}

	tasks, err := h.taskStore.ListHeroTasks(r.Context(), hero.ID)
	if err != nil {
		h.logger.Error("failed to list hero tasks", "hero_id", hero.ID, "error", err)
		writeJSON(w, http.StatusInternalServerError, map[string]string{
			"error": "failed to load tasks",
		})
		return
	}

	writeJSON(w, http.StatusOK, tasks)
}

// ClaimTask claims a completed task's reward.
// POST /api/v1/hero/tasks/{taskId}/claim
func (h *DailyTaskHandler) ClaimTask(w http.ResponseWriter, r *http.Request) {
	telegramID, ok := resolveTelegramID(r)
	if !ok {
		writeJSON(w, http.StatusBadRequest, map[string]string{
			"error": "missing telegramId",
		})
		return
	}

	taskID := chi.URLParam(r, "taskId")
	if taskID == "" {
		writeJSON(w, http.StatusBadRequest, map[string]string{
			"error": "taskId is required",
		})
		return
	}

	hero, err := h.heroStore.GetByTelegramID(r.Context(), telegramID)
	if err != nil {
		h.logger.Error("failed to get hero for task claim", "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
	}

	reward, err := h.taskStore.ClaimTask(r.Context(), hero.ID, taskID)
	if err != nil {
		h.logger.Warn("failed to claim task", "hero_id", hero.ID, "task_id", taskID, "error", err)
		writeJSON(w, http.StatusBadRequest, map[string]string{
			"error": err.Error(),
		})
		return
	}

	// Apply reward to hero.
	switch reward.RewardType {
	case "gold":
		hero.Gold += int64(reward.RewardAmount)
	case "potion":
		hero.Potions += reward.RewardAmount
	}

	if err := h.heroStore.Save(r.Context(), hero); err != nil {
		h.logger.Error("failed to save hero after task claim", "hero_id", hero.ID, "error", err)
		writeJSON(w, http.StatusInternalServerError, map[string]string{
			"error": "failed to save hero",
		})
		return
	}

	h.logger.Info("task reward claimed", "hero_id", hero.ID, "task_id", taskID,
		"reward_type", reward.RewardType, "reward_amount", reward.RewardAmount)

	now := time.Now()
	hero.RefreshDerivedCombatStats(now)
	writeJSON(w, http.StatusOK, map[string]any{
		"reward": reward,
		"hero":   hero,
	})
}
