package cmd

import (
	"strings"
	"testing"

	"github.com/yourorg/devour/internal/scraper"
)

func TestDeriveSearchTerms(t *testing.T) {
	terms := deriveSearchTerms("go", "how to regex match http path")

	if len(terms) == 0 {
		t.Fatal("expected at least one derived search term")
	}

	joined := strings.Join(terms, ",")
	if !strings.Contains(joined, "regexp") {
		t.Fatalf("expected regexp term in %v", terms)
	}
	if !strings.Contains(joined, "net/http") {
		t.Fatalf("expected net/http term in %v", terms)
	}
}

func TestScoreDocument(t *testing.T) {
	query := "regex match in go"
	docTitleMatch := &scraper.Document{
		Title:   "Package regexp",
		Content: "Use MustCompile and MatchString to match values.",
		Type:    "go-package",
		URL:     "https://pkg.go.dev/regexp",
	}
	docNoMatch := &scraper.Document{
		Title:   "Package archive/tar",
		Content: "Read and write tar archives.",
		Type:    "go-package",
		URL:     "https://pkg.go.dev/archive/tar",
	}

	if scoreDocument(query, docTitleMatch) <= scoreDocument(query, docNoMatch) {
		t.Fatal("expected regex-related document to have a higher score")
	}
}

func TestExtractRecommendedAPI(t *testing.T) {
	docs := []rankedDoc{
		{
			doc: &scraper.Document{
				Title:   "regexp.func MustCompile ¶",
				URL:     "https://pkg.go.dev/regexp",
				Content: "re := regexp.MustCompile(`\\\\d+`)\nif re.MatchString(input) { fmt.Println(\"ok\") }",
			},
		},
	}

	apis := extractRecommendedAPI(docs)
	if len(apis) == 0 {
		t.Fatal("expected API extraction to return at least one call")
	}
}

func TestExtractSnippet(t *testing.T) {
	content := "The regexp package implements regular expression search. Use MustCompile for fixed patterns."
	snippet := extractSnippet(content, []string{"regexp"})
	if snippet == "" {
		t.Fatal("expected non-empty snippet")
	}
	if !strings.Contains(strings.ToLower(snippet), "regexp") {
		t.Fatalf("snippet should mention regexp, got: %q", snippet)
	}
}

func TestCandidateDocURLs_FrameworkFallbacks(t *testing.T) {
	next, err := candidateDocURLs("nextjs", "routing")
	if err != nil {
		t.Fatalf("candidateDocURLs(nextjs) error: %v", err)
	}
	if len(next) < 2 {
		t.Fatalf("expected fallback URLs for nextjs, got %v", next)
	}
	if next[0] != "https://nextjs.org/docs/app/building-your-application/routing" {
		t.Fatalf("unexpected primary nextjs URL: %q", next[0])
	}

	remix, err := candidateDocURLs("remix", "routes")
	if err != nil {
		t.Fatalf("candidateDocURLs(remix) error: %v", err)
	}
	if len(remix) == 0 || remix[0] != "https://v2.remix.run/docs/file-conventions/routes" {
		t.Fatalf("unexpected remix candidate URLs: %v", remix)
	}

	solid, err := candidateDocURLs("solid", "router")
	if err != nil {
		t.Fatalf("candidateDocURLs(solid) error: %v", err)
	}
	if len(solid) == 0 || !strings.Contains(solid[0], "github.com/solidjs/solid-docs") {
		t.Fatalf("unexpected solid candidate URLs: %v", solid)
	}
}

func TestPrimaryQueryTokenSkipsQuestionWords(t *testing.T) {
	token := primaryQueryToken("what does routing do in remix")
	if token == "" {
		t.Fatal("expected non-empty token")
	}
	if token == "what" || token == "does" {
		t.Fatalf("expected informative token, got %q", token)
	}
}

func TestDeriveSearchTermsSolidRouting(t *testing.T) {
	terms := deriveSearchTerms("solid", "how to do routing in solid")
	joined := strings.Join(terms, ",")
	if !strings.Contains(joined, "solid-router") {
		t.Fatalf("expected solid-router term in %v", terms)
	}
	if strings.Contains(joined, "signals") {
		t.Fatalf("did not expect signals default for routing question, got %v", terms)
	}
}

func TestShouldFallbackToLive(t *testing.T) {
	strong := []rankedDoc{
		{
			doc:   &scraper.Document{Title: "Routing Guide", Content: "routing with file based routes", URL: "https://nextjs.org/docs/routing"},
			score: 2.2,
		},
	}
	if shouldFallbackToLive(strong, []string{"routing"}) {
		t.Fatal("expected strong local match to skip live fallback")
	}

	weak := []rankedDoc{
		{
			doc:   &scraper.Document{Title: "Misc", Content: "unrelated", URL: "https://example.com"},
			score: 0.1,
		},
	}
	if !shouldFallbackToLive(weak, []string{"routing"}) {
		t.Fatal("expected weak local match to trigger live fallback")
	}
}
