package main

import (
	"context"
	"strings"
	"testing"

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

// gapPhraser — a phraser whose world model is configured and asleep, which is
// the state the naming half exists for.
type gapPhraser struct {
	*phraser.Stub
	worldCalls int
}

func (g *gapPhraser) PhraseWorld(context.Context, string, []string) (string, error) {
	g.worldCalls++
	return "", phraser.ErrNoWorldModel
}

func worldTurn(utterance string) *queryTurn {
	return &queryTurn{dec: router.Decision{Intent: router.IntentQuery, Utterance: utterance}}
}

// A world question with the workstation asleep says so. The resident model is
// not asked, because what it produces here is an invention with no signal that
// it is one.
func TestQueryGeneralNamesTheGap(t *testing.T) {
	g := &gapPhraser{Stub: phraser.NewStub()}
	h := &reactiveHandler{phraser: g}
	reply, ok := h.queryGeneral(context.Background(), worldTurn("почему небо голубое"))
	if !ok {
		t.Fatal("queryGeneral passed on the last source in the chain")
	}
	if reply != worldGap() {
		t.Fatalf("reply = %q, want the named gap", reply)
	}
	if g.worldCalls != 1 {
		t.Fatalf("PhraseWorld called %d times, want 1", g.worldCalls)
	}
}

// A phraser with no world seam at all — the Stub, and every box with no
// `workstation` block — answers exactly as it did before this seam existed.
func TestQueryGeneralWithoutAWorldModelIsUnchanged(t *testing.T) {
	h := &reactiveHandler{phraser: phraser.NewStub()}
	reply, ok := h.queryGeneral(context.Background(), worldTurn("почему небо голубое"))
	if !ok {
		t.Fatal("queryGeneral passed on the last source in the chain")
	}
	if !phraser.IsUnknownFallback(reply) {
		t.Fatalf("reply = %q, want the Stub's answer", reply)
	}
}

// The gap is spoken aloud by a Russian voice, so it is Russian, feminine and
// informal. "не хочу" and "не могу" are her own verbs; there is no "вы" and no
// English in it.
func TestWorldGapIsInPersona(t *testing.T) {
	for _, bad := range []string{"вы", "ваш", "рад ", "дорогой", "милый"} {
		if strings.Contains(worldGap(), bad) {
			t.Errorf("the gap phrase contains %q: %s", bad, worldGap())
		}
	}
	if strings.ContainsAny(worldGap(), "abcdefghijklmnopqrstuvwxyz") {
		t.Errorf("the gap phrase has Latin letters in it: %s", worldGap())
	}
}

// The sources that hold a passage read it back rather than name a gap. He gets a
// real quote instead of "не могу сейчас", and nothing is invented either way.
func TestASourceWithAPassageReadsItBackInsteadOfNamingTheGap(t *testing.T) {
	g := &gapPhraser{Stub: phraser.NewStub()}
	h := &reactiveHandler{phraser: g}
	if got := h.phraseSource(context.Background(), "search", "почему небо голубое",
		[]string{"Рэлеевское рассеяние."}); got != "" {
		t.Fatalf("phraseSource = %q, want \"\" so the caller's own floor reads the passage back", got)
	}
	if g.worldCalls != 1 {
		t.Fatalf("PhraseWorld called %d times, want 1", g.worldCalls)
	}
}
