package api

import (
	"net/http"
	"strconv"

	"git.fabledsword.com/bvandeusen/minstrel/internal/apierror"
	"git.fabledsword.com/bvandeusen/minstrel/internal/db/dbq"
)

const (
	recMetricsDefaultDays = 30
	recMetricsMaxDays     = 365
)

// recommendationMetric is one recommendation surface's outcomes.
type recommendationMetric struct {
	Source        string  `json:"source"`         // 'for_you' | 'discover' | mixes
	Plays         int64   `json:"plays"`          // plays launched from this surface
	Skips         int64   `json:"skips"`          // of those, marked skipped
	SkipRate      float64 `json:"skip_rate"`      // skips / plays, [0,1]
	AvgCompletion float64 `json:"avg_completion"` // mean completion ratio, [0,1]
}

type recommendationMetricsResp struct {
	WindowDays int                    `json:"window_days"`
	Sources    []recommendationMetric `json:"sources"`
}

// handleGetRecommendationMetrics implements GET /api/me/recommendation-metrics.
// Per-source play outcomes (plays / skips / skip-rate / avg-completion) for the
// caller over the last `days` (default 30, capped at 365), so the operator can
// see which recommendation surfaces are landing and tune the taste weights.
// Only plays tagged with a system-playlist source count; library/radio plays
// (no source) are excluded.
func (h *handlers) handleGetRecommendationMetrics(w http.ResponseWriter, r *http.Request) {
	caller, ok := requireUser(w, r)
	if !ok {
		return
	}
	days, ok := parseMetricsDays(w, r)
	if !ok {
		return
	}

	rows, err := dbq.New(h.pool).RecommendationSourceMetricsForUser(r.Context(),
		dbq.RecommendationSourceMetricsForUserParams{UserID: caller.ID, Column2: float64(days)})
	if err != nil {
		h.logger.Error("api: recommendation metrics", "err", err)
		writeErr(w, apierror.InternalMsg("lookup failed", err))
		return
	}

	out := recommendationMetricsResp{
		WindowDays: days,
		Sources:    make([]recommendationMetric, 0, len(rows)),
	}
	for _, row := range rows {
		source := ""
		if row.Source != nil {
			source = *row.Source
		}
		var skipRate float64
		if row.Plays > 0 {
			skipRate = float64(row.Skips) / float64(row.Plays)
		}
		out.Sources = append(out.Sources, recommendationMetric{
			Source:        source,
			Plays:         row.Plays,
			Skips:         row.Skips,
			SkipRate:      skipRate,
			AvgCompletion: row.AvgCompletion,
		})
	}
	writeJSON(w, http.StatusOK, out)
}

// parseMetricsDays reads the `days` query param (default 30, capped at 365).
// Writes a 400 and returns ok=false on a malformed value.
func parseMetricsDays(w http.ResponseWriter, r *http.Request) (int, bool) {
	v := r.URL.Query().Get("days")
	if v == "" {
		return recMetricsDefaultDays, true
	}
	n, err := strconv.Atoi(v)
	if err != nil || n < 1 {
		writeErr(w, apierror.BadRequest("bad_request", "invalid days"))
		return 0, false
	}
	if n > recMetricsMaxDays {
		n = recMetricsMaxDays
	}
	return n, true
}
