package placement

import (
	"context"
	"fmt"
)

// DiscoverChannel identifies which backend fulfilled a placement discover query.
type DiscoverChannel string

const (
	DiscoverThreadsAPI DiscoverChannel = "threads_api"
	DiscoverBrave      DiscoverChannel = "brave"
	DiscoverExa        DiscoverChannel = "exa"
	DiscoverCrawler    DiscoverChannel = "crawler"
)

// DiscoverRequest is used by scan jobs; expand-graph only uses Brave knowledge_expand.
type DiscoverRequest struct {
	Query   string
	Keyword string // plain tag for crawler; optional
	Recency bool
	Limit   int
	Member  MemberContext
	Crawler CrawlerSearchFn
}

type DiscoverPost struct {
	Text           string
	Permalink      string
	ExternalID     string
	Author         string
	PostedAt       string
	AuthorVerified bool
	FollowerCount  int
	LikeCount      int
	ReplyCount     int
	Source         DiscoverChannel
}

// Discover runs keyword discovery respecting search_source_mode and available connections.
// Crawler-first modes skip Threads API when the browser session returns posts (saves API quota).
func Discover(ctx context.Context, req DiscoverRequest) ([]DiscoverPost, DiscoverChannel, error) {
	m := req.Member
	if !m.HasDiscoverPath() {
		return nil, "", discoverMissingPathError(m)
	}

	if ShouldTryCrawlerFirst(m) {
		posts, err := runCrawlerDiscover(ctx, req)
		if err == nil && len(posts) > 0 {
			return posts, DiscoverCrawler, nil
		}
		if m.SearchSourceMode == SearchSourceCrawler {
			if err != nil {
				return nil, DiscoverCrawler, err
			}
			return posts, DiscoverCrawler, nil
		}
	}

	if m.AllowsThreadsAPI {
		if !m.ApiConnected {
			if !m.CrawlerFallbackAllowed() {
				return nil, "", fmt.Errorf("正式模式需先完成 Threads API 連線")
			}
		} else {
			posts, err := keywordSearchViaThreadsAPI(ctx, req)
			if err == nil && len(posts) > 0 {
				return posts, DiscoverThreadsAPI, nil
			}
			if err != nil {
				if m.CrawlerFallbackAllowed() {
					cPosts, cErr := runCrawlerDiscover(ctx, req)
					if cErr == nil && len(cPosts) > 0 {
						return cPosts, DiscoverCrawler, nil
					}
				}
				if !m.AllowsBrave && !m.CrawlerFallbackAllowed() {
					// Optional API field gaps must not fail the whole patrol; return empty for this keyword.
					return []DiscoverPost{}, DiscoverThreadsAPI, nil
				}
			}
		}
	}

	if m.AllowsBrave {
		if m.WebSearchAPIKey() == "" {
			if m.CrawlerFallbackAllowed() {
				posts, err := runCrawlerDiscover(ctx, req)
				if err == nil {
					return posts, DiscoverCrawler, nil
				}
			}
			return nil, "", fmt.Errorf("請在設定頁設定 %s Search API key（跟隨此登入帳號）", m.WebSearchProviderLabel())
		}
		return nil, m.WebSearchDiscoverChannel(), fmt.Errorf("web search threads discover delegated to worker")
	}

	if m.CrawlerFallbackAllowed() {
		posts, err := runCrawlerDiscover(ctx, req)
		if err != nil {
			return nil, DiscoverCrawler, err
		}
		return posts, DiscoverCrawler, nil
	}

	return nil, "", fmt.Errorf("目前搜尋來源模式無可用管道：%s", m.SearchSourceMode)
}
