package placement

import (
	"sort"
	"strings"

	libkg "haixun-backend/internal/library/knowledge"
	"haixun-backend/internal/library/websearch"
)

const (
	defaultPatrolProductFitNoGraph = 42
	maxPatrolQueriesPerScan        = libkg.MaxScanPatrolKeywords * 2
)

// CollectPatrolTagQueries builds lean dual-track jobs: relevance + 7d recency per keyword.
func CollectPatrolTagQueries(keywords []string, nodes []libkg.Node, provider websearch.Provider, patrolInput libkg.PatrolTagInput) []TagQuery {
	keywords = libkg.NormalizePatrolKeywordList(keywords)
	if len(keywords) == 0 {
		return nil
	}
	if len(keywords) > libkg.MaxScanPatrolKeywords {
		keywords = keywords[:libkg.MaxScanPatrolKeywords]
	}

	out := make([]TagQuery, 0, len(keywords)*2)
	for _, tag := range keywords {
		fit := productFitForPatrolTag(tag, nodes, patrolInput)
		if q := BuildRelevanceQuery(provider, tag); q != "" {
			out = append(out, TagQuery{
				Tag:             tag,
				Query:           q,
				Dimension:       QueryRelevance,
				ProductFitScore: fit,
			})
		}
		if q7 := BuildRecencyQuery(provider, tag, IdealMaxPostAgeDays); q7 != "" {
			out = append(out, TagQuery{
				Tag:             tag,
				Query:           q7,
				Dimension:       QueryRecency,
				ProductFitScore: fit,
				RecencyDays:     IdealMaxPostAgeDays,
			})
		}
	}
	return out
}

func productFitForPatrolTag(tag string, nodes []libkg.Node, patrolInput libkg.PatrolTagInput) int {
	tagKey := patrolTagMatchKey(tag)
	best := 0
	profile := libkg.BuildIntentProfile(patrolInput)
	intentFit := libkg.ScoreIntentSimilarity(tag, profile)
	for _, node := range nodes {
		score := node.ProductFitScore
		if score <= 0 {
			continue
		}
		matched := false
		for _, candidate := range append(append([]string{}, node.DerivedTags.Relevance...), node.DerivedTags.Recency...) {
			if patrolTagMatchKey(candidate) == tagKey {
				matched = true
				break
			}
		}
		if !matched && patrolTagMatchKey(node.Label) != tagKey {
			continue
		}
		if score > best {
			best = score
		}
	}
	if best > 0 {
		return maxPatrolFit(best, intentFit)
	}
	if intentFit > 0 {
		return intentFit
	}
	return defaultPatrolProductFitNoGraph
}

func maxPatrolFit(values ...int) int {
	best := 0
	for _, v := range values {
		if v > best {
			best = v
		}
	}
	return best
}

func patrolTagMatchKey(tag string) string {
	tag = strings.TrimSpace(tag)
	for _, suffix := range []string{" 推薦", " 請問", " 怎麼辦", " 好用嗎", " 有人用過嗎", " 有推薦嗎", " 請益"} {
		if strings.HasSuffix(tag, suffix) {
			tag = strings.TrimSuffix(tag, suffix)
			break
		}
	}
	return strings.TrimSpace(tag)
}

// ResolveTagQueries builds Search API jobs from the best-ranked patrol keywords.
func ResolveTagQueries(nodes []libkg.Node, patrolKeywords []string, provider websearch.Provider, patrolInput libkg.PatrolTagInput) []TagQuery {
	nodes = libkg.NodesForPatrolKeywordDerivation(nodes)
	keywords := patrolKeywords
	if len(keywords) == 0 {
		keywords = libkg.SelectBestSearchKeywords(nil, nil, patrolInput, nodes, libkg.MaxScanPatrolKeywords)
	}
	queries := CollectPatrolTagQueries(keywords, nodes, provider, patrolInput)
	if len(queries) == 0 {
		queries = CollectTagQueries(nodes, provider)
	}
	return trimPatrolQueries(queries, maxPatrolQueriesPerScan, patrolInput)
}

func trimPatrolQueries(queries []TagQuery, max int, patrolInput libkg.PatrolTagInput) []TagQuery {
	profile := libkg.BuildIntentProfile(patrolInput)
	if max <= 0 || len(queries) <= max {
		return queries
	}
	type ranked struct {
		query TagQuery
		key   string
	}
	rankedQueries := make([]ranked, 0, len(queries))
	seen := map[string]struct{}{}
	for _, item := range queries {
		key := strings.TrimSpace(item.Query)
		if key == "" {
			continue
		}
		if _, ok := seen[key]; ok {
			continue
		}
		seen[key] = struct{}{}
		rankedQueries = append(rankedQueries, ranked{query: item, key: key})
	}
	sort.SliceStable(rankedQueries, func(i, j int) bool {
		left := rankedQueries[i].query.ProductFitScore
		right := rankedQueries[j].query.ProductFitScore
		leftIntent := libkg.ScoreIntentSimilarity(rankedQueries[i].query.Tag, profile)
		rightIntent := libkg.ScoreIntentSimilarity(rankedQueries[j].query.Tag, profile)
		leftRank := left*2 + leftIntent*3
		rightRank := right*2 + rightIntent*3
		if leftRank == rightRank {
			if rankedQueries[i].query.Dimension == rankedQueries[j].query.Dimension {
				return rankedQueries[i].key < rankedQueries[j].key
			}
			if rankedQueries[i].query.Dimension == QueryRelevance {
				return true
			}
			return false
		}
		return leftRank > rightRank
	})
	out := make([]TagQuery, 0, max)
	for _, item := range rankedQueries {
		out = append(out, item.query)
		if len(out) >= max {
			break
		}
	}
	return out
}
