package usecase

import (
	"context"
	"fmt"
	"testing"

	"apps/backend/internal/module/scout/domain"
	"apps/backend/internal/module/scout/repository"
)

type stagedPipelineProvider struct {
	calls       []int
	firstBad    int
	allRelevant bool
}

type oneTermExpansionProvider struct {
	calls []string
}

func (p *oneTermExpansionProvider) SearchThreads(_ context.Context, terms []string, limit int) ([]ThreadSearchResult, error) {
	term := ""
	if len(terms) > 0 {
		term = terms[0]
	}
	p.calls = append(p.calls, term)
	out := make([]ThreadSearchResult, 0, limit)
	for i := 0; i < limit; i++ {
		text := "外包合作正在找人"
		if term != "外包" {
			text = "想找後端工程師做外包合作，歡迎推薦接案夥伴"
		}
		out = append(out, ThreadSearchResult{
			URL: fmt.Sprintf("https://www.threads.net/@source/post/%s-%d", term, i), Snippet: text,
		})
	}
	return out, nil
}

func (p *stagedPipelineProvider) SearchThreads(_ context.Context, terms []string, limit int) ([]ThreadSearchResult, error) {
	call := len(p.calls) + 1
	p.calls = append(p.calls, limit)
	term := "市集"
	if len(terms) > 0 {
		term = terms[0]
	}
	out := make([]ThreadSearchResult, 0, limit)
	for i := 0; i < limit; i++ {
		text := term + "討論內容"
		if !p.allRelevant && call == 1 && i < p.firstBad {
			text = "完全無關的內容"
		}
		out = append(out, ThreadSearchResult{
			URL: fmt.Sprintf("https://www.threads.net/@source/post/c%d-%d", call, i), Snippet: text,
		})
	}
	return out, nil
}

func newPipelineService(provider ThreadSearchProvider) *Service {
	svc := New(repository.NewMemory())
	svc.Provider = provider
	return svc
}

func TestSearchPipelineContinuesWhenRawHitsExceedEligibleHits(t *testing.T) {
	provider := &stagedPipelineProvider{firstBad: 13}
	svc := newPipelineService(provider)
	result, err := svc.searchEligiblePipeline(context.Background(), 7, []string{"市集"}, &domain.RunBrief{
		Mode: domain.ModeActivity, Intent: "市集", ScanTerms: []string{"市集"},
	}, 10, domain.PathAPI, "", 20)
	if err != nil {
		t.Fatal(err)
	}
	if len(result.Hits) != 10 || result.Diagnostics.EligibleCount != 10 {
		t.Fatalf("pipeline result=%+v", result)
	}
	if len(provider.calls) != 2 || provider.calls[0] != 20 || provider.calls[1] != 20 {
		t.Fatalf("calls=%v want initial + same-source boost", provider.calls)
	}
	if result.Diagnostics.IrrelevantCount != 13 || result.Diagnostics.RawCount != 40 {
		t.Fatalf("diagnostics=%+v", result.Diagnostics)
	}
}

func TestSearchPipelineStopsAtEligibleTarget(t *testing.T) {
	provider := &stagedPipelineProvider{allRelevant: true}
	svc := newPipelineService(provider)
	result, err := svc.searchEligiblePipeline(context.Background(), 7, []string{"市集", "活動"}, &domain.RunBrief{
		Mode: domain.ModeActivity, Intent: "市集", ScanTerms: []string{"市集", "活動"},
	}, 3, domain.PathAPI, "", 20)
	if err != nil {
		t.Fatal(err)
	}
	if len(result.Hits) != 3 || len(provider.calls) != 1 {
		t.Fatalf("result=%+v calls=%v; target should stop first query", result, provider.calls)
	}
	if result.Diagnostics.Stages != 1 {
		t.Fatalf("stages=%d want 1", result.Diagnostics.Stages)
	}
}

func TestSearchPipelineCapsRawCandidatesAt320(t *testing.T) {
	provider := &stagedPipelineProvider{allRelevant: true}
	svc := newPipelineService(provider)
	terms := []string{"一", "二", "三", "四", "五", "六", "七", "八"}
	result, err := svc.searchEligiblePipeline(context.Background(), 7, terms, &domain.RunBrief{
		Mode: domain.ModeActivity, Intent: "市集", ScanTerms: []string{"市集"},
	}, 999, domain.PathAPI, "", 20)
	if err != nil {
		t.Fatal(err)
	}
	if result.Diagnostics.RawCount != maxPipelineRawCandidates || result.Diagnostics.Stages != 2 {
		t.Fatalf("diagnostics=%+v", result.Diagnostics)
	}
	if len(provider.calls) != 16 {
		t.Fatalf("calls=%d want 16 (8 terms x 2 stages)", len(provider.calls))
	}
	if len(result.Diagnostics.ShortfallReasons) == 0 {
		t.Fatal("raw cap shortfall reason missing")
	}
}

func TestSearchPipelineExpandsOneApprovedTermWhenProviderRepeatsFirstPage(t *testing.T) {
	provider := &oneTermExpansionProvider{}
	svc := newPipelineService(provider)
	result, err := svc.searchEligiblePipeline(context.Background(), 7, []string{"外包"}, &domain.RunBrief{
		Mode: domain.ModeActivity, Intent: "外包 後端工程師", ScanTerms: []string{"外包"},
	}, 5, domain.PathAPI, "", 20)
	if err != nil {
		t.Fatal(err)
	}
	if len(result.Hits) != 5 {
		t.Fatalf("expanded result=%+v, want five eligible hits", result)
	}
	if len(provider.calls) < 3 || provider.calls[0] != "外包" || provider.calls[1] != "外包" {
		t.Fatalf("calls=%v, want repeated first query before expansion", provider.calls)
	}
	if result.Diagnostics.Stages != 3 {
		t.Fatalf("stages=%d, want initial, boost, expansion", result.Diagnostics.Stages)
	}
}
