package tagger

import (
	"os"
	"path/filepath"
	"testing"
)

// createTestRulesDir creates temporary test rule files
func createTestRulesDir(t *testing.T) string {
	t.Helper()

	dir := t.TempDir()

	// Create doc_type_rules.yaml
	docTypeRules := `doc_types:
  Lehrplan:
    strong_terms:
      - Lehrplan
      - Kernlehrplan
      - Bildungsplan
    medium_terms:
      - Curriculum
    url_patterns:
      - /lehrplan
  Arbeitsblatt:
    strong_terms:
      - Arbeitsblatt
      - Übungsblatt
    medium_terms:
      - Aufgaben
    url_patterns:
      - /arbeitsblatt
  Studie_Bericht:
    strong_terms:
      - Studie
      - PISA
    medium_terms:
      - Ergebnis
    url_patterns:
      - /studie
priority_order:
  - Lehrplan
  - Arbeitsblatt
  - Studie_Bericht
  - Sonstiges
`
	if err := os.WriteFile(filepath.Join(dir, "doc_type_rules.yaml"), []byte(docTypeRules), 0644); err != nil {
		t.Fatal(err)
	}

	// Create subject_rules.yaml
	subjectRules := `subjects:
  Mathematik:
    strong:
      - Mathematik
      - Algebra
      - Geometrie
    weak:
      - rechnen
      - Zahlen
    negative:
      - Geschichte der Mathematik
  Deutsch:
    strong:
      - Deutsch
      - Grammatik
      - Rechtschreibung
    weak:
      - Lesen
      - Schreiben
    negative:
      - Deutsch als Fremdsprache
  Geschichte:
    strong:
      - Geschichte
      - Historisch
    weak:
      - Epoche
      - Jahrhundert
    negative:
      - Naturgeschichte
threshold: 4
max_subjects: 3
`
	if err := os.WriteFile(filepath.Join(dir, "subject_rules.yaml"), []byte(subjectRules), 0644); err != nil {
		t.Fatal(err)
	}

	// Create level_rules.yaml
	levelRules := `levels:
  Grundschule:
    strong:
      - Grundschule
      - Primarstufe
      - Klasse 1-4
    weak:
      - Erstklässler
    negative:
      - Sekundarstufe
  Gymnasium:
    strong:
      - Gymnasium
      - Abitur
      - Oberstufe
    weak:
      - Sekundarstufe II
    negative:
      - Realschule
  Sek_I:
    strong:
      - Sekundarstufe I
      - Klasse 5-10
      - Hauptschule
    weak:
      - Mittelstufe
    negative:
      - Grundschule
priority_order:
  - Gymnasium
  - Sek_I
  - Grundschule
  - NA
`
	if err := os.WriteFile(filepath.Join(dir, "level_rules.yaml"), []byte(levelRules), 0644); err != nil {
		t.Fatal(err)
	}

	// Create trust_rules.yaml
	trustRules := `domain_boosts:
  - match: "*.kmk.org"
    add: 0.30
    reason: "Kultusministerkonferenz"
  - match: "*.bildungsserver.de"
    add: 0.25
    reason: "Deutscher Bildungsserver"
  - match: "*.bayern.de"
    add: 0.20
    reason: "Bayerische Landesregierung"
tld_boosts:
  - tld: ".gov"
    add: 0.15
    reason: "Government domain"
penalties:
  - if_url_contains:
      - "forum"
      - "blog"
    add: -0.10
    reason: "User generated content"
content_penalties:
  - if_ad_density_gt: 0.3
    add: -0.15
    reason: "High ad density"
  - if_content_length_lt: 200
    add: -0.10
    reason: "Very short content"
`
	if err := os.WriteFile(filepath.Join(dir, "trust_rules.yaml"), []byte(trustRules), 0644); err != nil {
		t.Fatal(err)
	}

	return dir
}

func TestNewTagger_Success(t *testing.T) {
	rulesDir := createTestRulesDir(t)

	tagger, err := NewTagger(rulesDir)
	if err != nil {
		t.Fatalf("NewTagger failed: %v", err)
	}

	if tagger == nil {
		t.Fatal("Expected non-nil tagger")
	}

	if tagger.docTypeRules == nil {
		t.Error("docTypeRules not loaded")
	}
	if tagger.subjectRules == nil {
		t.Error("subjectRules not loaded")
	}
	if tagger.levelRules == nil {
		t.Error("levelRules not loaded")
	}
	if tagger.trustRules == nil {
		t.Error("trustRules not loaded")
	}
}

func TestNewTagger_MissingFile(t *testing.T) {
	_, err := NewTagger("/nonexistent/path")
	if err == nil {
		t.Error("Expected error for nonexistent rules directory")
	}
}

func TestTagger_TagDocType_Lehrplan(t *testing.T) {
	rulesDir := createTestRulesDir(t)
	tagger, err := NewTagger(rulesDir)
	if err != nil {
		t.Fatal(err)
	}

	tests := []struct {
		name     string
		url      string
		content  string
		expected string
	}{
		{
			name:     "Strong term in content",
			url:      "https://example.com/page",
			content:  "Dies ist der Lehrplan für Mathematik in der Sekundarstufe",
			expected: "Lehrplan",
		},
		{
			name:     "URL pattern match",
			url:      "https://example.com/lehrplan/mathe",
			content:  "Allgemeine Informationen zum Fach",
			expected: "Lehrplan",
		},
		{
			name:     "Multiple strong terms",
			url:      "https://example.com/bildung",
			content:  "Kernlehrplan und Bildungsplan für das Curriculum",
			expected: "Lehrplan",
		},
		{
			name:     "Arbeitsblatt detection",
			url:      "https://example.com/material",
			content:  "Arbeitsblatt zum Thema Rechnen mit Übungsblatt",
			expected: "Arbeitsblatt",
		},
		{
			name:     "No match returns Sonstiges",
			url:      "https://example.com/page",
			content:  "Eine allgemeine Webseite ohne spezifische Bildungsinhalte",
			expected: "Sonstiges",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := tagger.Tag(tt.url, "", tt.content, ContentFeatures{})
			if result.DocType != tt.expected {
				t.Errorf("Expected DocType %q, got %q", tt.expected, result.DocType)
			}
		})
	}
}

func TestTagger_TagSubjects(t *testing.T) {
	rulesDir := createTestRulesDir(t)
	tagger, err := NewTagger(rulesDir)
	if err != nil {
		t.Fatal(err)
	}

	tests := []struct {
		name            string
		content         string
		expectedContain []string
		expectedMissing []string
	}{
		{
			name:            "Mathematik detection",
			content:         "In Mathematik lernen wir Algebra und Geometrie sowie das Rechnen mit Zahlen",
			expectedContain: []string{"Mathematik"},
		},
		{
			name:            "Deutsch detection",
			content:         "Im Fach Deutsch geht es um Grammatik, Rechtschreibung und das Lesen von Texten",
			expectedContain: []string{"Deutsch"},
		},
		{
			name:            "Multiple subjects",
			content:         "Mathematik und Algebra verbinden sich mit Geschichte und historischen Epochen",
			expectedContain: []string{"Mathematik", "Geschichte"},
		},
		{
			name:            "No subjects detected",
			content:         "Ein Text ohne spezifische Fachbegriffe",
			expectedContain: []string{},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := tagger.Tag("https://example.com", "", tt.content, ContentFeatures{})

			for _, expected := range tt.expectedContain {
				found := false
				for _, subject := range result.Subjects {
					if subject == expected {
						found = true
						break
					}
				}
				if !found {
					t.Errorf("Expected subject %q not found in %v", expected, result.Subjects)
				}
			}
		})
	}
}

func TestTagger_TagSchoolLevel(t *testing.T) {
	rulesDir := createTestRulesDir(t)
	tagger, err := NewTagger(rulesDir)
	if err != nil {
		t.Fatal(err)
	}

	tests := []struct {
		name     string
		content  string
		expected string
	}{
		{
			name:     "Grundschule detection",
			content:  "Material für die Grundschule und Primarstufe",
			expected: "Grundschule",
		},
		{
			name:     "Gymnasium detection",
			content:  "Vorbereitung auf das Abitur am Gymnasium in der Oberstufe",
			expected: "Gymnasium",
		},
		{
			name:     "Sekundarstufe I detection",
			content:  "Aufgaben für Sekundarstufe I in Klasse 5-10",
			expected: "Sek_I",
		},
		{
			name:     "No level detected",
			content:  "Allgemeine Bildungsinformationen",
			expected: "NA",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := tagger.Tag("https://example.com", "", tt.content, ContentFeatures{})
			if result.SchoolLevel != tt.expected {
				t.Errorf("Expected SchoolLevel %q, got %q", tt.expected, result.SchoolLevel)
			}
		})
	}
}

func TestTagger_TrustScore(t *testing.T) {
	rulesDir := createTestRulesDir(t)
	tagger, err := NewTagger(rulesDir)
	if err != nil {
		t.Fatal(err)
	}

	tests := []struct {
		name        string
		url         string
		features    ContentFeatures
		minExpected float64
		maxExpected float64
	}{
		{
			name:        "Base score for unknown domain",
			url:         "https://unknown-domain.com/page",
			features:    ContentFeatures{ContentLength: 500},
			minExpected: 0.40,
			maxExpected: 0.60,
		},
		{
			name:        "KMK domain boost",
			url:         "https://www.kmk.org/bildung",
			features:    ContentFeatures{ContentLength: 500},
			minExpected: 0.70,
			maxExpected: 0.90,
		},
		{
			name:        "Bayern domain boost",
			url:         "https://www.km.bayern.de/lehrplan",
			features:    ContentFeatures{ContentLength: 500},
			minExpected: 0.60,
			maxExpected: 0.80,
		},
		{
			name:        "Forum penalty",
			url:         "https://example.com/forum/thread",
			features:    ContentFeatures{ContentLength: 500},
			minExpected: 0.30,
			maxExpected: 0.50,
		},
		{
			name:        "High ad density penalty",
			url:         "https://example.com/page",
			features:    ContentFeatures{AdDensity: 0.5, ContentLength: 500},
			minExpected: 0.25,
			maxExpected: 0.50,
		},
		{
			name:        "Short content penalty",
			url:         "https://example.com/page",
			features:    ContentFeatures{ContentLength: 100},
			minExpected: 0.30,
			maxExpected: 0.50,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := tagger.Tag(tt.url, "", "Some content text", tt.features)
			if result.TrustScore < tt.minExpected || result.TrustScore > tt.maxExpected {
				t.Errorf("TrustScore %f not in expected range [%f, %f]",
					result.TrustScore, tt.minExpected, tt.maxExpected)
			}
		})
	}
}

func TestTagger_DetectState(t *testing.T) {
	rulesDir := createTestRulesDir(t)
	tagger, err := NewTagger(rulesDir)
	if err != nil {
		t.Fatal(err)
	}

	tests := []struct {
		name     string
		url      string
		expected string
	}{
		{
			name:     "Bayern detection",
			url:      "https://www.km.bayern.de/lehrplan",
			expected: "BY",
		},
		{
			name:     "NRW detection",
			url:      "https://www.schulministerium.nrw.de/themen",
			expected: "NW",
		},
		{
			name:     "Berlin detection",
			url:      "https://www.berlin.de/sen/bildung/schule",
			expected: "BE",
		},
		{
			name:     "Hessen detection",
			url:      "https://kultusministerium.hessen.de",
			expected: "HE",
		},
		{
			name:     "No state (federal)",
			url:      "https://www.kmk.org/bildung",
			expected: "",
		},
		{
			name:     "Unknown domain",
			url:      "https://www.example.com/page",
			expected: "",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := tagger.Tag(tt.url, "", "Some content", ContentFeatures{})
			if result.State != tt.expected {
				t.Errorf("Expected State %q, got %q", tt.expected, result.State)
			}
		})
	}
}

func TestMatchDomainPattern(t *testing.T) {
	tests := []struct {
		name     string
		url      string
		pattern  string
		expected bool
	}{
		{
			name:     "Exact match",
			url:      "https://kmk.org/page",
			pattern:  "kmk.org",
			expected: true,
		},
		{
			name:     "Wildcard subdomain",
			url:      "https://www.kmk.org/page",
			pattern:  "*.kmk.org",
			expected: true,
		},
		{
			name:     "No match",
			url:      "https://example.com/page",
			pattern:  "*.kmk.org",
			expected: false,
		},
		{
			name:     "Case insensitive",
			url:      "https://WWW.KMK.ORG/page",
			pattern:  "*.kmk.org",
			expected: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := matchDomainPattern(tt.url, tt.pattern)
			if result != tt.expected {
				t.Errorf("matchDomainPattern(%q, %q) = %v, expected %v",
					tt.url, tt.pattern, result, tt.expected)
			}
		})
	}
}

func TestTagger_CombinedTitleAndContent(t *testing.T) {
	rulesDir := createTestRulesDir(t)
	tagger, err := NewTagger(rulesDir)
	if err != nil {
		t.Fatal(err)
	}

	// Test that title is combined with content for tagging
	result := tagger.Tag(
		"https://example.com/page",
		"Lehrplan Mathematik Bayern", // Title with keywords
		"Allgemeiner Text ohne spezifische Begriffe", // Content without keywords
		ContentFeatures{ContentLength: 500},
	)

	if result.DocType != "Lehrplan" {
		t.Errorf("Expected DocType 'Lehrplan' from title, got %q", result.DocType)
	}
}

func TestTrustScoreClamping(t *testing.T) {
	rulesDir := createTestRulesDir(t)
	tagger, err := NewTagger(rulesDir)
	if err != nil {
		t.Fatal(err)
	}

	// Test that score is clamped to [0, 1]
	result := tagger.Tag(
		"https://www.kmk.org/page", // High trust domain
		"",
		"Content",
		ContentFeatures{ContentLength: 1000},
	)

	if result.TrustScore < 0 || result.TrustScore > 1 {
		t.Errorf("TrustScore %f should be in range [0, 1]", result.TrustScore)
	}
}
