package phraser

import (
	"context"
	"testing"

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

type mockCompleter struct {
	out string
	err error
}

func (m mockCompleter) Complete(_ context.Context, _ llm.Req) (string, error) { return m.out, m.err }

func TestReplierReturnsLLMReply(t *testing.T) {
	r := NewReplier(mockCompleter{out: `{"response":"записала, кофе закончился","mood":"neutral"}`}, nil)
	got, err := r.PhraseReply(context.Background(), noteDecision())
	if err != nil || got != "записала, кофе закончился" {
		t.Errorf("got %q, %v, want %q, nil", got, err, "записала, кофе закончился")
	}
}

func TestReplierFallsBackToPlainText(t *testing.T) {
	r := NewReplier(mockCompleter{out: "записала, кофе закончился"}, nil)
	got, err := r.PhraseReply(context.Background(), noteDecision())
	if err != nil || got != "записала, кофе закончился" {
		t.Errorf("got %q, %v, want %q, nil", got, err, "записала, кофе закончился")
	}
}

func TestReplierReportsTheModelError(t *testing.T) {
	r := NewReplier(mockCompleter{err: errTestLLMDown}, nil)
	got, err := r.PhraseReply(context.Background(), noteDecision())
	if err == nil {
		t.Errorf("got %q, nil error — a dead model must be reported, not phrased around", got)
	}
}

// A fragment the grammar left half-open is a failed generation. It must come
// back as an error so the daemon reaches its stub, not as a reply.
func TestReplierRejectsBrokenJSON(t *testing.T) {
	r := NewReplier(mockCompleter{out: `{"response":"запис`}, nil)
	got, err := r.PhraseReply(context.Background(), noteDecision())
	if err == nil || got != "" {
		t.Errorf("got %q, %v, want empty and an error", got, err)
	}
}

func TestReplierEmptyOutputIsEmpty(t *testing.T) {
	r := NewReplier(mockCompleter{out: ""}, nil)
	got, err := r.PhraseReply(context.Background(), noteDecision())
	if err != nil || got != "" {
		t.Errorf("got %q, %v, want empty and no error", got, err)
	}
}

// grammarRecorder captures the request so the grammar can be asserted on.
type grammarRecorder struct{ req llm.Req }

func (g *grammarRecorder) Complete(_ context.Context, r llm.Req) (string, error) {
	g.req = r
	return `{"response":"записала","mood":"neutral"}`, nil
}

func TestReplierCarriesTheResponseGrammar(t *testing.T) {
	rec := &grammarRecorder{}
	r := NewReplier(rec, nil)
	if _, err := r.PhraseReply(context.Background(), noteDecision()); err != nil {
		t.Fatalf("PhraseReply: %v", err)
	}
	if rec.req.Grammar != ResponseGrammar {
		t.Errorf("grammar = %q, want ResponseGrammar", rec.req.Grammar)
	}
	if rec.req.System != ReplySystemPrompt {
		t.Errorf("system prompt = %q, want ReplySystemPrompt", rec.req.System)
	}
}

func noteDecision() router.Decision {
	return router.Decision{Intent: router.IntentNote, Slots: router.Slots{Text: "кофе закончился"}}
}

var errTestLLMDown = errTest("llm down")

type errTest string

func (e errTest) Error() string { return string(e) }
