package viral

import (
	"fmt"
	"sort"
	"strings"

	"haixun-backend/internal/library/placement"
	missionentity "haixun-backend/internal/model/copy_mission/domain/entity"
)

const (
	RefAccountMinBestEngagement  = 50
	RefAccountMinBestLikes       = 18
	RefAccountMinTotalEngagement = 80
	RefVerifiedMinBestEngagement = 35
	RefVerifiedMinBestLikes      = 10
)

type ReferenceAccountInput struct {
	SeedQuery string
	Label     string
	Posts     []placement.ScanCandidate
	Limit     int
}

type referenceAuthorAgg struct {
	username        string
	verified        bool
	followerCount   int
	totalEngagement int
	bestEngagement  int
	bestLikes       int
	bestReplies     int
	postCount       int
	sampleText      string
	sampleSearchTag string
}

// BuildReferenceAccountsFromScan lists authors from patrol posts that match the
// mission topic and pass quality gates. Verified/follower are optional bonuses;
// if strict gates yield nothing, falls back to baseline viral engagement.
func BuildReferenceAccountsFromScan(in ReferenceAccountInput) []missionentity.SimilarAccount {
	out := buildReferenceAccountsFromScan(in, true)
	if len(out) > 0 {
		return out
	}
	return buildReferenceAccountsFromScan(in, false)
}

func buildReferenceAccountsFromScan(in ReferenceAccountInput, strictQuality bool) []missionentity.SimilarAccount {
	limit := in.Limit
	if limit <= 0 {
		limit = MaxSimilarAccounts
	}
	byUser := map[string]referenceAuthorAgg{}
	for _, post := range in.Posts {
		user := strings.TrimSpace(post.Author)
		if user == "" || !isValidUsername(user) {
			continue
		}
		if !postTopicRelevant(post, in.SeedQuery, in.Label) {
			continue
		}
		if strictQuality {
			if !PassesMissionQualityCandidate(
				post.Text, post.LikeCount, post.ReplyCount, post.EngagementScore,
				post.AuthorVerified, post.FollowerCount, nil,
			) {
				continue
			}
		} else if !PassesViralCandidate(
			post.Text, post.LikeCount, post.ReplyCount, post.EngagementScore, nil,
		) {
			continue
		}
		key := strings.ToLower(user)
		prev := byUser[key]
		prev.username = user
		if post.AuthorVerified {
			prev.verified = true
		}
		if post.FollowerCount > prev.followerCount {
			prev.followerCount = post.FollowerCount
		}
		prev.postCount++
		prev.totalEngagement += post.EngagementScore
		if post.EngagementScore > prev.bestEngagement {
			prev.bestEngagement = post.EngagementScore
			prev.bestLikes = post.LikeCount
			prev.bestReplies = post.ReplyCount
			text := strings.TrimSpace(post.Text)
			if len([]rune(text)) > 80 {
				text = string([]rune(text)[:80])
			}
			prev.sampleText = text
			prev.sampleSearchTag = strings.TrimSpace(post.SearchTag)
		}
		byUser[key] = prev
	}

	ranked := make([]referenceAuthorAgg, 0, len(byUser))
	for _, item := range byUser {
		if qualifiesReferenceAuthor(item, strictQuality) {
			ranked = append(ranked, item)
		}
	}
	sort.Slice(ranked, func(i, j int) bool {
		if ranked[i].verified != ranked[j].verified {
			return ranked[i].verified
		}
		if ranked[i].followerCount != ranked[j].followerCount {
			return ranked[i].followerCount > ranked[j].followerCount
		}
		if ranked[i].totalEngagement != ranked[j].totalEngagement {
			return ranked[i].totalEngagement > ranked[j].totalEngagement
		}
		return ranked[i].bestEngagement > ranked[j].bestEngagement
	})
	if len(ranked) > limit {
		ranked = ranked[:limit]
	}

	out := make([]missionentity.SimilarAccount, 0, len(ranked))
	for _, item := range ranked {
		conf := "medium"
		if item.verified {
			conf = "high"
		} else if item.bestEngagement >= HotEngagementScore || item.totalEngagement >= 120 {
			conf = "high"
		}
		out = append(out, missionentity.SimilarAccount{
			Username:        item.username,
			Reason:          formatReferenceReason(item),
			Source:          "scan",
			Confidence:      conf,
			ProfileURL:      "https://www.threads.net/@" + item.username,
			AuthorVerified:  item.verified,
			FollowerCount:   item.followerCount,
			EngagementScore: item.bestEngagement,
			LikeCount:       item.bestLikes,
			ReplyCount:      item.bestReplies,
			PostCount:       item.postCount,
		})
	}
	return out
}

func qualifiesReferenceAuthor(item referenceAuthorAgg, strictQuality bool) bool {
	if item.postCount == 0 {
		return false
	}
	if item.verified {
		return item.bestLikes >= RefVerifiedMinBestLikes &&
			(item.bestEngagement >= RefVerifiedMinBestEngagement || item.totalEngagement >= 60)
	}
	if strictQuality {
		if item.bestLikes < RefAccountMinBestLikes {
			return false
		}
		return item.bestEngagement >= RefAccountMinBestEngagement || item.totalEngagement >= RefAccountMinTotalEngagement
	}
	return item.bestLikes >= 8 && item.bestEngagement >= MinEngagementScore
}

func postTopicRelevant(post placement.ScanCandidate, seed, label string) bool {
	text := strings.ToLower(strings.TrimSpace(post.Text))
	tag := strings.ToLower(strings.TrimSpace(post.SearchTag))
	terms := topicTerms(seed, label)
	if len(terms) == 0 {
		return text != "" || tag != ""
	}
	for _, term := range terms {
		term = strings.ToLower(term)
		if strings.Contains(text, term) || strings.Contains(tag, term) {
			return true
		}
	}
	return false
}

func topicTerms(seed, label string) []string {
	out := []string{}
	if s := strings.TrimSpace(seed); s != "" {
		out = append(out, s)
	}
	if l := strings.TrimSpace(label); l != "" {
		out = append(out, l)
	}
	return out
}

func formatReferenceReason(item referenceAuthorAgg) string {
	if item.sampleText != "" {
		return item.sampleText
	}
	if item.sampleSearchTag != "" {
		return fmt.Sprintf("標籤「%s」高互動作者", item.sampleSearchTag)
	}
	return "本次海巡高互動作者"
}
