// Package mood maps folksonomy tags (from the track_tags cache, #1490) to a
// small set of canonical mood buckets (#1534, milestone #160 Opt 2b). Both the
// taste-profile builder and the recommendation scorer classify a track's tags
// through Of() so a track's mood is derived the same way on both sides.
//
// The vocabulary is intentionally narrow: only words that clearly denote mood
// (not genre). A track with no mood-word tags contributes nothing — the mood
// facet is a supplement whose coverage grows with folksonomy enrichment
// (thin on MusicBrainz-only, richer once Last.fm is keyed).
package mood

import "strings"

// vocab maps a lowercased folksonomy tag to its canonical mood bucket. Several
// synonyms collapse to one bucket so "sad" / "melancholy" / "melancholic" all
// score the same dimension.
var vocab = map[string]string{
	// melancholic
	"melancholic": "melancholic", "melancholy": "melancholic", "sad": "melancholic",
	"sombre": "melancholic", "somber": "melancholic", "wistful": "melancholic",
	"bittersweet": "melancholic", "mournful": "melancholic",
	// energetic
	"energetic": "energetic", "energy": "energetic", "upbeat": "energetic",
	"uplifting": "energetic", "party": "energetic", "anthemic": "energetic",
	// chill
	"chill": "chill", "chillout": "chill", "chilled": "chill", "mellow": "chill",
	"relaxing": "chill", "relaxed": "chill", "calm": "chill", "laid-back": "chill",
	"laidback": "chill", "soothing": "chill",
	// aggressive
	"aggressive": "aggressive", "angry": "aggressive", "intense": "aggressive",
	"heavy": "aggressive", "brutal": "aggressive",
	// dark
	"dark": "dark", "moody": "dark", "brooding": "dark", "haunting": "dark", "eerie": "dark",
	// dreamy
	"dreamy": "dreamy", "ethereal": "dreamy", "atmospheric": "dreamy",
	"ambient": "dreamy", "hypnotic": "dreamy",
	// happy
	"happy": "happy", "cheerful": "happy", "feel good": "happy", "feel-good": "happy",
	"feelgood": "happy", "joyful": "happy", "fun": "happy",
	// romantic
	"romantic": "romantic", "sensual": "romantic", "sexy": "romantic", "sentimental": "romantic",
}

// Of returns the distinct canonical mood buckets present in tags, lowercasing +
// trimming each tag before lookup. Returns nil when no tag maps to a mood, so
// callers get no mood signal for a track with only genre/descriptive tags.
func Of(tags []string) []string {
	var out []string
	seen := make(map[string]bool)
	for _, t := range tags {
		key := strings.ToLower(strings.TrimSpace(t))
		if m, ok := vocab[key]; ok && !seen[m] {
			seen[m] = true
			out = append(out, m)
		}
	}
	return out
}
