package knowledge

import (
	"sort"
	"strings"
)

const MaxTopPatrolTags = 8

type PatrolTagCandidate struct {
	Tag    string
	Score  int
	Reason string
	Intent string
}

func CollectPatrolTagsFromGraph(in PatrolTagInput, nodes []Node) []string {
	return SelectTopPatrolTags(BuildPatrolCandidates(in, nodes), MaxTopPatrolTags)
}

func BuildPatrolCandidates(in PatrolTagInput, nodes []Node) []PatrolTagCandidate {
	out := []PatrolTagCandidate{}
	for i, tag := range SanitizePatrolKeywordList(in.PatrolKeywords) {
		addPatrolCandidate(&out, tag, 120-i, "手動精選", patrolIntentRelevance)
	}
	for i, q := range in.Questions {
		addPatrolCandidate(&out, PatrolTagFromQuestion(q), scoreQuestion(i)+12, "受眾提問", patrolIntentRelevance)
	}
	for i, pillar := range in.Pillars {
		addPatrolCandidate(&out, PatrolTagFromPillar(pillar), scorePillar(i)+6, "內容支柱", patrolIntentRelevance)
	}
	for i, matchTag := range in.MatchTags {
		addPatrolCandidate(&out, patrolTagFromSource(matchTag, patrolIntentRelevance), scoreMatchTag(i), "產品匹配", patrolIntentRelevance)
	}
	if in.ProductName != "" {
		addPatrolCandidate(&out, patrolTagFromSource(in.ProductName, patrolIntentRelevance), scoreProduct(), "置入產品", patrolIntentRelevance)
	}
	for _, node := range nodes {
		if !IsPainNode(node) && node.Layer > 1 {
			continue
		}
		nodeScore := scoreNode(node)
		for _, tag := range node.DerivedTags.Relevance {
			addPatrolCandidate(&out, tag, nodeScore, nodePatrolReason(node), patrolIntentRelevance)
		}
		for _, tag := range node.DerivedTags.Recency {
			addPatrolCandidate(&out, tag, nodeScore-3, nodePatrolReason(node), patrolIntentRecency)
		}
	}
	return out
}

func SelectTopPatrolTags(candidates []PatrolTagCandidate, limit int) []string {
	if limit <= 0 {
		return nil
	}
	picked := selectTopPatrolCandidates(candidates, limit)
	out := make([]string, 0, len(picked))
	for _, item := range picked {
		out = append(out, item.Tag)
	}
	return out
}

func selectBestDerivedTags(candidates []PatrolTagCandidate, relLimit, recLimit int) DerivedTags {
	sort.SliceStable(candidates, func(i, j int) bool {
		if candidates[i].Score == candidates[j].Score {
			return candidates[i].Tag < candidates[j].Tag
		}
		return candidates[i].Score > candidates[j].Score
	})
	seenRelKey := map[string]struct{}{}
	seenRecTag := map[string]struct{}{}
	relevance := []string{}
	recency := []string{}
	for _, item := range candidates {
		if item.Intent == patrolIntentRecency {
			continue
		}
		key := patrolTagDedupeKey(item.Tag)
		if _, ok := seenRelKey[key]; ok {
			continue
		}
		seenRelKey[key] = struct{}{}
		relevance = append(relevance, item.Tag)
		if len(relevance) >= relLimit {
			break
		}
	}
	for _, item := range candidates {
		if item.Intent != patrolIntentRecency {
			continue
		}
		if _, ok := seenRecTag[item.Tag]; ok {
			continue
		}
		seenRecTag[item.Tag] = struct{}{}
		recency = append(recency, item.Tag)
		if len(recency) >= recLimit {
			break
		}
	}
	if len(recency) < recLimit && len(relevance) > 0 {
		for _, rel := range relevance {
			alt := patrolRecencyFallback(rel)
			if alt == "" || containsString(relevance, alt) {
				continue
			}
			if _, ok := seenRecTag[alt]; ok {
				continue
			}
			recency = append(recency, alt)
			if len(recency) >= recLimit {
				break
			}
		}
	}
	return DerivedTags{
		Relevance: capTags(uniqueTags(relevance), relLimit),
		Recency:   capTags(uniqueTags(recency), recLimit),
	}
}

func selectTopPatrolCandidates(candidates []PatrolTagCandidate, limit int) []PatrolTagCandidate {
	if limit <= 0 || len(candidates) == 0 {
		return nil
	}
	sort.SliceStable(candidates, func(i, j int) bool {
		if candidates[i].Score == candidates[j].Score {
			return candidates[i].Tag < candidates[j].Tag
		}
		return candidates[i].Score > candidates[j].Score
	})
	seenTag := map[string]struct{}{}
	seenKey := map[string]struct{}{}
	out := make([]PatrolTagCandidate, 0, limit)
	for _, item := range candidates {
		tag := strings.TrimSpace(item.Tag)
		if tag == "" {
			continue
		}
		key := patrolTagDedupeKey(tag)
		if _, ok := seenTag[tag]; ok {
			continue
		}
		if _, ok := seenKey[key]; ok {
			continue
		}
		seenTag[tag] = struct{}{}
		seenKey[key] = struct{}{}
		out = append(out, PatrolTagCandidate{
			Tag:    tag,
			Score:  item.Score,
			Reason: item.Reason,
			Intent: item.Intent,
		})
		if len(out) >= limit {
			break
		}
	}
	return out
}

func addPatrolCandidate(out *[]PatrolTagCandidate, tag string, score int, reason, intent string) {
	tag = strings.TrimSpace(tag)
	if tag == "" || score <= 0 {
		return
	}
	*out = append(*out, PatrolTagCandidate{
		Tag:    tag,
		Score:  score,
		Reason: reason,
		Intent: intent,
	})
}

func scoreQuestion(index int) int {
	score := 100 - index*4
	if score < 70 {
		return 70
	}
	return score
}

func scorePillar(index int) int {
	score := 74 - index*3
	if score < 55 {
		return 55
	}
	return score
}

func scoreMatchTag(index int) int {
	score := 90 - index*4
	if score < 70 {
		return 70
	}
	return score
}

func scoreProduct() int {
	return 58
}

func scoreNode(node Node) int {
	base := 48
	switch node.Layer {
	case 0:
		base = 82
	case 1:
		base = 68
	case 2:
		base = 52
	}
	if IsPainNode(node) {
		base += 8
	}
	if node.ProductFitScore > 0 {
		base += node.ProductFitScore / 6
	}
	return base
}

func nodePatrolReason(node Node) string {
	switch node.Layer {
	case 0:
		return "核心痛點"
	case 1:
		return "高相關延伸"
	default:
		return "周邊情境"
	}
}

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

func patrolRecencyFallback(relevanceTag string) string {
	alt := patrolTagFromSource(relevanceTag, patrolIntentRecency)
	if alt != "" && alt != relevanceTag {
		return alt
	}
	if strings.Contains(relevanceTag, "請問") {
		return ""
	}
	return patrolTagFromSource(relevanceTag+" 請問", patrolIntentRecency)
}

func containsString(items []string, target string) bool {
	for _, item := range items {
		if item == target {
			return true
		}
	}
	return false
}
