package usecase

import (
	"fmt"
	"sort"
	"testing"

	"apps/backend/internal/module/scout/domain"
	"github.com/stretchr/testify/require"
)

type qualityGateHit struct {
	relevant bool
	score    int
	order    int
}

// TestTopicQualityGate is deliberately deterministic: it exercises the same
// hard candidate gate used by the worker, while the fixture supplies the
// human-labelled retrieval set. No external provider or network is involved.
func TestTopicQualityGate(t *testing.T) {
	fixtures := loadQualityFixtures(t)
	require.GreaterOrEqual(t, len(fixtures), 30, "quality gate requires at least 30 independent topic groups")

	var precisionSum float64
	targetHits := 0
	diagnostics := make([]string, 0)
	for _, fixture := range fixtures {
		hits := make([]qualityGateHit, 0, len(fixture.Cases))
		for i, candidate := range fixture.Cases {
			evaluator := NewCandidateEvaluator(&domain.RunBrief{
				Mode: domain.ModeActivity, Intent: fixture.Intent, ScanTerms: fixture.Terms,
			})
			got := evaluator.Evaluate(ThreadSearchResult{URL: candidate.URL, Snippet: candidate.Text})
			if got.Decision == CandidateEligible {
				hits = append(hits, qualityGateHit{relevant: candidate.Relevant, score: got.Score, order: i})
			}
		}
		sort.SliceStable(hits, func(i, j int) bool {
			if hits[i].score != hits[j].score {
				return hits[i].score > hits[j].score
			}
			return hits[i].order < hits[j].order
		})
		top := hits
		if len(top) > 10 {
			top = top[:10]
		}
		relevantTop := 0
		for _, hit := range top {
			if hit.relevant {
				relevantTop++
			}
		}
		precision := 0.0
		if len(top) > 0 {
			precision = float64(relevantTop) / float64(len(top))
		}
		target := fixture.Target
		if target <= 0 {
			target = 1
		}
		if len(hits) >= target {
			targetHits++
		}
		precisionSum += precision
		t.Logf("case=%s precision@10=%.2f target=%d eligible=%d relevant=%d", fixture.ID, precision, target, len(hits), relevantTop)
		if precision < 0.8 || len(hits) < target {
			diagnostics = append(diagnostics, fmt.Sprintf("case=%s precision=%.2f target=%d eligible=%d", fixture.ID, precision, target, len(hits)))
		}
	}

	averagePrecision := precisionSum / float64(len(fixtures))
	targetRate := float64(targetHits) / float64(len(fixtures))
	require.GreaterOrEqual(t, averagePrecision, 0.8, "precision@10 average; diagnostics=%v", diagnostics)
	require.GreaterOrEqual(t, targetRate, 0.9, "eligible target rate; diagnostics=%v", diagnostics)
	require.Empty(t, diagnostics, "quality gate failures")
}
