package ucca

import (
	"context"
	"fmt"
	"os"
	"strings"
	"testing"
)

func TestInKBScope(t *testing.T) {
	inScope := []string{
		"Welche neun Kriterien nennt WP248 fuer ein hohes Risiko?",
		"Wie greifen CRA und Maschinenverordnung bei einer vernetzten Maschine ineinander?",
		"Wann ist eine Datenschutz-Folgenabschaetzung erforderlich?",
		"Welche Anforderungen stellt die DSGVO an die Einwilligung?",
		"Brauche ich einen Datenschutzbeauftragten?",
		"Wann muss eine aktiv ausgenutzte Schwachstelle gemeldet werden?",
	}
	outScope := []string{
		"Welche OWASP-Kontrollen gibt es fuer Authentifizierung?",
		"Was sagt NIST SP 800-53 zu Access Control?",
		"Wie funktioniert ISO 27001 Zertifizierung?",
		"Welche IFRS-Standards gelten fuer Leasing?",
	}
	for _, q := range inScope {
		if !inKBScope(q) {
			t.Errorf("inKBScope(%q) = false, want true", q)
		}
	}
	for _, q := range outScope {
		if inKBScope(q) {
			t.Errorf("inKBScope(%q) = true, want false", q)
		}
	}
}

func TestResolveCollection(t *testing.T) {
	c := &LegalRAGClient{collection: "bp_compliance_ce", kbSliceCollection: "kb_2026_1_build", kbScopeRoutingEnabled: true}
	if got := c.resolveCollection("Welche Kriterien nennt WP248?", ""); got != "kb_2026_1_build" {
		t.Errorf("in-scope default -> %s, want kb_2026_1_build", got)
	}
	if got := c.resolveCollection("Was sagt NIST SP 800-53?", ""); got != "bp_compliance_ce" {
		t.Errorf("out-of-scope default -> %s, want bp_compliance_ce", got)
	}
	if got := c.resolveCollection("Welche Kriterien nennt WP248?", "explicit_coll"); got != "explicit_coll" {
		t.Errorf("explicit request must be honoured -> %s", got)
	}
	c.kbScopeRoutingEnabled = false
	if got := c.resolveCollection("Welche Kriterien nennt WP248?", ""); got != "bp_compliance_ce" {
		t.Errorf("disabled routing -> %s, want bp_compliance_ce", got)
	}
}

// TestKBScopeRoutingE2E (RUN_E2E=1) verifies the routing against the REAL collections: a default
// Search() of an in-scope query must hit the KB-2026.1 slice (WP248/MaschVO live there but NOT in
// the broad CE pool = clean discriminator); an out-of-scope query stays on CE.
func TestKBScopeRoutingE2E(t *testing.T) {
	if os.Getenv("RUN_E2E") != "1" {
		t.Skip("set RUN_E2E=1 + QDRANT_URL/OLLAMA_URL/QDRANT_API_KEY")
	}
	c := NewLegalRAGClient()
	cases := []struct {
		q         string
		wantToken string // expected in top-8 when routed to the slice
		wantInKB  bool
	}{
		{"Welche neun Kriterien nennt WP248 fuer ein voraussichtlich hohes Risiko?", "WP248", true},
		{"Welche grundlegenden Sicherheits- und Gesundheitsschutzanforderungen enthaelt Anhang III der Maschinenverordnung?", "MASCH", true},
		{"Wie greifen CRA und Maschinenverordnung bei einer vernetzten Maschine ineinander?", "MASCH", true},
		{"Was sagt NIST SP 800-53 zu Access Control?", "", false},
	}
	for _, tc := range cases {
		routed := c.resolveCollection(tc.q, "")
		res, err := c.Search(context.Background(), tc.q, nil, 8)
		if err != nil {
			t.Fatalf("%q: %v", tc.q, err)
		}
		codes := map[string]bool{}
		for _, r := range res {
			codes[strings.ToUpper(r.RegulationCode)] = true
		}
		hit := false
		if tc.wantToken != "" {
			for cd := range codes {
				if strings.Contains(cd, tc.wantToken) {
					hit = true
					break
				}
			}
		}
		col := make([]string, 0, len(codes))
		for cd := range codes {
			col = append(col, cd)
		}
		fmt.Printf("inKB=%-5v routed=%-16s wantTok=%-6s found=%-5v | %v\n", tc.wantInKB, routed, tc.wantToken, hit, col)
		if tc.wantInKB && tc.wantToken != "" && !hit {
			t.Errorf("%q routed to %s but %s not in top-8 (slice not active?)", tc.q, routed, tc.wantToken)
		}
	}
}
