package router

import "strings"

// Feed queries — "что нового в лентах?", "что нового по технологиям?"
// (Vikunja #258).
//
// This file is the OFFLINE FLOOR as of 05-08-2026 (V-522). Whether a turn is
// about the feeds is a question about meaning, so the frozen seeds decide it —
// topicFeed in cmd/mavend/topics.go, through turnIsAbout. The word lists below
// stay because they always answer: a box with no embedder, a turn whose vector
// never got computed, and any call that does not clear topicMargin. They are
// allowed to stay narrow now that they are not the only answer.
//
// What has not changed is that no generative model decides this. One would
// occasionally answer "что нового?" out of world knowledge, which is the one
// thing a feed reader exists to avoid.

// FeedQuery — a parsed "what's new" question. Category is the topic he named
// ("технологии"), empty when he asked about the feeds in general.
type FeedQuery struct {
	Category string
}

// feedNouns — the words that name the feeds themselves. One of these is enough,
// with an ask, to make the turn a feed question.
var feedNouns = []string{
	"лента", "ленте", "ленты", "лентах", "лентам",
	"новости", "новостей", "новостях", "новостям",
	"feed", "feeds", "news", "headlines",
}

// vagueNouns — the newness words that are NOT about the feeds by themselves.
//
// "что нового?" is the most common opener in the language and it is a greeting,
// not a request for headlines. It used to match here, so the shipping daemon —
// which has no feeds block — answered "я пока не читаю ленты, они не настроены",
// a configuration status in reply to hello. With feeds on it answered "в лентах
// пока ничего нового", which is no better. A vague noun claims the turn only
// when the utterance narrows it: a named topic ("что нового по технологиям"), or
// a feed noun somewhere in it ("что нового в лентах").
var vagueNouns = []string{"новое", "нового", "новенького", "new"}

// newnessMarkers — the "что нового" half. "нового" alone is in feedNouns
// because it carries the question on its own ("что нового?"); a bare "лента"
// needs the ask, which is what askMarkers below is for.
var askMarkers = []string{
	"что", "какие", "какое", "расскажи", "почитай", "прочитай", "покажи",
	"what", "any", "tell", "show", "read",
}

// ParseFeedQuery reports whether an utterance asks what is new in the feeds, and
// which topic if it names one after "по"/"об"/"про"/"about".
//
// A feed noun and an ask are required. "у меня новая лента в инстаграме" is a
// statement and must not be read as a request to recite headlines. A vague
// newness word counts as the noun only when a topic is named — see vagueNouns
// for why the bare "что нового?" must fall through.
func ParseFeedQuery(text string) (FeedQuery, bool) {
	toks := planTokens(text)
	noun, vague, ask := false, false, false
	for _, t := range toks {
		for _, n := range feedNouns {
			if t == n {
				noun = true
				break
			}
		}
		for _, n := range vagueNouns {
			if t == n {
				vague = true
				break
			}
		}
		for _, a := range askMarkers {
			if t == a {
				ask = true
				break
			}
		}
	}
	if !ask {
		return FeedQuery{}, false
	}
	cat := feedCategory(toks)
	if !noun && !(vague && cat != "") {
		return FeedQuery{}, false
	}
	return FeedQuery{Category: cat}, true
}

// categoryPreps — the prepositions a topic follows. Russian marks the topic with
// a preposition ("по технологиям", "про политику"), so the word after one is the
// category; there is no stemming here, and the match against the configured
// category is a prefix comparison for exactly that reason.
//
// "о" is not in the list. It is one rune and it turns up as filler, a typo and
// half of "о'кей", so any utterance carrying a stray "о" produced a category of
// whatever word came next and she answered "по этой теме в лентах пока ничего"
// to a question that named no theme. "об" and "про" carry the same meaning and
// cannot be mistaken for anything else.
var categoryPreps = map[string]bool{"по": true, "об": true, "про": true, "about": true, "on": true}

// FeedCategoryOf reads the topic out of an utterance without deciding whether the
// turn is a feed question at all. The seeds answer that now (topicFeed in
// cmd/mavend/topics.go, V-522), and they answer it for phrasings the word lists
// here never held — but a claimed turn still needs its category, and a category
// is marked by a preposition rather than recognised.
func FeedCategoryOf(text string) string { return feedCategory(planTokens(text)) }

func feedCategory(toks []string) string {
	for i, t := range toks {
		if categoryPreps[t] && i+1 < len(toks) {
			next := toks[i+1]
			// "по новостям" names no topic, it repeats the noun.
			for _, n := range append(append([]string{}, feedNouns...), vagueNouns...) {
				if next == n {
					return ""
				}
			}
			return next
		}
	}
	return ""
}

// CategoryMatches reports whether a feed note's own category tag is the one he
// named. Russian inflects the topic ("технологиям" vs the configured
// "технологии"), and there is no stemmer in this repo, so the comparison is on a
// common prefix — long enough that "полит" and "погод" stay apart, short enough
// to survive a case ending.
//
// tag is the note's stored category (rss.NoteCategory), NOT the whole note. It
// used to be the whole note, which meant "что нового про погоду" matched any
// tech headline whose link happened to contain "pogod".
func CategoryMatches(tag, category string) bool {
	if category == "" {
		return true
	}
	stem := categoryStem(category)
	if stem == "" {
		return false
	}
	return strings.Contains(strings.ToLower(tag), stem)
}

// categoryStem cuts a word down to the part inflection leaves alone. 5 runes is
// the compromise: shorter words are used whole.
func categoryStem(word string) string {
	r := []rune(strings.ToLower(strings.TrimSpace(word)))
	if len(r) > 5 {
		r = r[:5]
	}
	return string(r)
}
