package main

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

	"github.com/kami/maven/internal/router"
)

// A handler with no embedder never loads the seeds, so the boundary falls back
// to the possession markers. That is the offline floor and it must keep working
// — an embedder that fails to load must not open the boundary.
func TestBoundaryFallsBackToMarkersWithNoEmbedder(t *testing.T) {
	h := personalHandler()
	if !h.isPersonalTurn(context.Background(), &queryTurn{
		dec: router.Decision{Utterance: "во сколько у меня встреча"},
	}) {
		t.Error("no embedder: a possession question must still be personal")
	}
	if h.isPersonalTurn(context.Background(), &queryTurn{
		dec: router.Decision{Utterance: "почему небо синее"},
	}) {
		t.Error("no embedder: a world question must still pass")
	}
}

// TestONNXPersonalBoundary — the number that matters, scored against the
// embedder homesrv actually runs. Opt-in via MAVEN_ONNX_LIB, exactly like
// TestONNXRecall in internal/memory/recalleval.
//
// Every case here is held out: none of these strings is a seed. The #495
// regression is the first row — "что я говорил про бэкапы?" reached SearXNG and
// was answered from a Habr article, and no possession word appears in it.
func TestONNXPersonalBoundary(t *testing.T) {
	lib := os.Getenv("MAVEN_ONNX_LIB")
	if lib == "" {
		t.Skip("MAVEN_ONNX_LIB unset — see AGENTS.md § Embedder model for intent routing")
	}
	dir := filepath.Join("../..", "models/embedder/multilingual-e5-small")
	emb, err := router.NewONNXEmbedder(filepath.Join(dir, "model_quantized.onnx"), filepath.Join(dir, "tokenizer.json"), lib)
	if err != nil {
		t.Skipf("onnx embedder unavailable: %v", err)
	}
	defer emb.Close()

	cases := []struct {
		utterance string
		personal  bool
	}{
		{"что я говорил про бэкапы?", true},
		{"что я сказал вчера про отпуск", true},
		{"я писал что-нибудь про сервер", true},
		{"я упоминал про конференцию?", true},
		{"что я отмечал по поводу переезда", true},
		{"я рассказывал тебе про новую работу?", true},
		{"во сколько у меня встреча", true},
		{"когда мой следующий отпуск", true},
		{"what did i say about backups", true},
		{"did i tell you about the doctor", true},
		{"как я говорил, почему небо синее", false},
		{"как уже я говорил, какая столица франции", false},
		{"почему трава зелёная", false},
		{"столица франции", false},
		{"как мне сварить борщ", false},
		{"что мне посмотреть вечером", false},
		{"я хочу узнать про рим", false},
		{"кто такой гагарин", false},
		{"how do i boil an egg", false},
	}

	h := &reactiveHandler{recall: recallWiring{embedder: emb}}
	ctx := context.Background()
	wrong := 0
	for _, c := range cases {
		vec, err := router.EmbedQuery(ctx, emb, c.utterance)
		if err != nil {
			t.Fatalf("embed %q: %v", c.utterance, err)
		}
		turn := &queryTurn{dec: router.Decision{Utterance: c.utterance}, vec: vec}
		got := h.isPersonalTurn(ctx, turn)
		p, w, ok := h.recall.boundary.score(vec)
		if !ok {
			t.Fatal("seeds did not load with a working embedder")
		}
		if got != c.personal {
			wrong++
			t.Errorf("%q: personal=%v want %v (personal %.4f world %.4f)", c.utterance, got, c.personal, p, w)
		}
		t.Logf("personal=%-5v personal %.4f world %.4f delta %+.4f  %s", got, p, w, p-w, c.utterance)
	}
	t.Logf("personal boundary: %d/%d held-out utterances correct", len(cases)-wrong, len(cases))
}
