package knowledge

import (
	"strings"
	"unicode/utf8"
)

const (
	maxThreadsSearchRunes   = 14
	maxWebRecencyCoreRunes  = 12
	maxWebRelevanceTagRunes = 16
)

var searchIntentMarkers = []string{"推薦", "請問", "怎麼辦", "好用嗎", "有人", "求助", "請益", "有推薦"}

// ShortPatrolSearchCore compresses a patrol tag for Threads API / recency Search API.
// Keeps pain + product tokens; drops filler and redundant intent words (RECENT/TOP handles freshness).
func ShortPatrolSearchCore(tag string) string {
	tag = strings.TrimSpace(tag)
	if tag == "" {
		return ""
	}
	tokens := selectSearchTokens(tag, 3)
	if len(tokens) == 0 {
		return truncateRunes(stripSearchIntent(tag), maxThreadsSearchRunes)
	}
	core := strings.Join(tokens, " ")
	return truncateRunes(core, maxWebRecencyCoreRunes)
}

// ThreadsAPIKeyword is the plain keyword sent to official Threads keyword search.
func ThreadsAPIKeyword(tag string) string {
	core := ShortPatrolSearchCore(tag)
	if core != "" {
		return core
	}
	return truncateRunes(stripSearchIntent(tag), maxThreadsSearchRunes)
}

// FullPatrolWebSearchTag keeps a slightly longer phrase for relevance Search API (exact-match quotes).
func FullPatrolWebSearchTag(tag string) string {
	tag = strings.TrimSpace(tag)
	if tag == "" {
		return ""
	}
	if !hasSearchIntentMarker(tag) {
		candidate := strings.TrimSpace(tag + " 推薦")
		if utf8.RuneCountInString(candidate) <= maxWebRelevanceTagRunes {
			tag = candidate
		}
	}
	if utf8.RuneCountInString(tag) <= maxWebRelevanceTagRunes {
		return tag
	}
	tokens := selectSearchTokens(tag, 4)
	if len(tokens) == 0 {
		return truncateRunes(tag, maxWebRelevanceTagRunes)
	}
	full := strings.Join(tokens, " ")
	if !hasSearchIntentMarker(full) {
		withIntent := strings.TrimSpace(full + " 推薦")
		if utf8.RuneCountInString(withIntent) <= maxWebRelevanceTagRunes {
			return withIntent
		}
	}
	return truncateRunes(full, maxWebRelevanceTagRunes)
}

func selectSearchTokens(tag string, maxTokens int) []string {
	tag = stripSearchIntent(tag)
	parts := []string{}
	seen := map[string]struct{}{}
	add := func(token string) {
		token = strings.TrimSpace(token)
		if isPatrolFiller(token) || len([]rune(token)) < 2 {
			return
		}
		if _, ok := seen[token]; ok {
			return
		}
		seen[token] = struct{}{}
		parts = append(parts, token)
	}

	for _, anchor := range patrolTopicAnchors {
		if strings.Contains(tag, anchor) {
			add(anchor)
		}
	}
	for _, hint := range productFormHints {
		if strings.Contains(tag, hint) {
			add(hint)
		}
	}
	for _, token := range intentTokenize(tag) {
		add(token)
		if len(parts) >= maxTokens {
			break
		}
	}
	if len(parts) > maxTokens {
		parts = parts[:maxTokens]
	}
	return parts
}

func stripSearchIntent(tag string) string {
	tag = strings.TrimSpace(tag)
	for _, marker := range searchIntentMarkers {
		tag = strings.ReplaceAll(tag, marker, " ")
	}
	return strings.Join(strings.Fields(tag), " ")
}

func hasSearchIntentMarker(tag string) bool {
	for _, marker := range searchIntentMarkers {
		if strings.Contains(tag, marker) {
			return true
		}
	}
	return false
}
