package placement

import (
	"context"
	"strings"

	libthreads "haixun-backend/internal/library/threadsapi"
)

const replyFetchTopPosts = 10

// ReplyCandidate is a normalized reply attached to a scan post.
type ReplyCandidate struct {
	ExternalID string
	Author     string
	Text       string
	Permalink  string
	LikeCount  int
	PostedAt   string
}

type ScrapeRepliesInput struct {
	Posts          []ScanCandidate
	Member         MemberContext
	RepliesPerPost int
	MaxPosts       int
}

// AttachReplies fetches replies for top-priority posts when scrape is enabled.
func AttachReplies(ctx context.Context, input ScrapeRepliesInput) []ScanCandidate {
	if len(input.Posts) == 0 {
		return input.Posts
	}
	perPost := input.RepliesPerPost
	if perPost <= 0 {
		perPost = 10
	}
	if perPost > 25 {
		perPost = 25
	}
	maxPosts := input.MaxPosts
	if maxPosts <= 0 {
		maxPosts = replyFetchTopPosts
	}

	targets := pickReplyTargets(input.Posts, maxPosts)
	if len(targets) == 0 {
		return input.Posts
	}

	client := libthreads.NewClient(input.Member.ThreadsAPIAccessToken)
	byKey := make(map[string][]ReplyCandidate, len(targets))
	for _, target := range targets {
		externalID := strings.TrimSpace(target.ExternalID)
		if externalID == "" {
			continue
		}
		var replies []ReplyCandidate
		if input.Member.ApiConnected && input.Member.ThreadsAPIAccessToken != "" {
			items, err := client.MediaReplies(ctx, externalID, perPost)
			if err == nil {
				for _, item := range items {
					text := strings.TrimSpace(item.Text)
					if text == "" {
						continue
					}
					replies = append(replies, ReplyCandidate{
						ExternalID: strings.TrimSpace(item.ID),
						Author:     strings.TrimSpace(item.Username),
						Text:       text,
						Permalink:  strings.TrimSpace(item.Permalink),
						LikeCount:  item.LikeCount,
						PostedAt:   strings.TrimSpace(item.Timestamp),
					})
				}
			}
		}
		key := candidateKey(target)
		byKey[key] = replies
	}

	out := make([]ScanCandidate, 0, len(input.Posts))
	for _, post := range input.Posts {
		key := candidateKey(post)
		if replies, ok := byKey[key]; ok && len(replies) > 0 {
			post.Replies = replies
		}
		out = append(out, post)
	}
	return out
}

func pickReplyTargets(posts []ScanCandidate, maxPosts int) []ScanCandidate {
	ranked := append([]ScanCandidate(nil), posts...)
	for i := 0; i < len(ranked); i++ {
		for j := i + 1; j < len(ranked); j++ {
			if replyTargetRank(ranked[j]) > replyTargetRank(ranked[i]) {
				ranked[i], ranked[j] = ranked[j], ranked[i]
			}
		}
	}
	out := make([]ScanCandidate, 0, maxPosts)
	for _, post := range ranked {
		if strings.TrimSpace(post.ExternalID) == "" && strings.TrimSpace(post.Permalink) == "" {
			continue
		}
		out = append(out, post)
		if len(out) >= maxPosts {
			break
		}
	}
	return out
}

func replyTargetRank(post ScanCandidate) int {
	switch post.Priority {
	case "gold":
		return 300 + post.PlacementScore
	case "recent":
		return 200 + post.PlacementScore
	case "relevant":
		return 100 + post.PlacementScore
	default:
		return post.PlacementScore
	}
}

func candidateKey(post ScanCandidate) string {
	if id := strings.TrimSpace(post.ExternalID); id != "" {
		return "id:" + id
	}
	return "url:" + strings.TrimSpace(post.Permalink)
}
