package main

import (
	"context"
	"strings"
	"testing"
	"time"

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

// contQueryAPI records which core call a continued query reached. DayPlan and
// LatestFact are here to be caught, not to be used: a continuation must never
// reach them, and the counters are how the test says so.
type contQueryAPI struct {
	ipc.UnimplementedCoreAPI
	from, to  time.Time
	events    int
	plans     int
	factLooks int
}

func (a *contQueryAPI) CalendarEvents(_ context.Context, from, to time.Time) ([]ipc.Fact, error) {
	a.events++
	a.from, a.to = from, to
	return []ipc.Fact{{Key: "calendar", Value: "Планёрка @ 14:00", Confidence: 1.0, Ts: from.Add(14 * time.Hour)}}, nil
}

func (a *contQueryAPI) DayPlan(context.Context) (ipc.DayPlan, error) {
	a.plans++
	return ipc.DayPlan{Spoken: "план на сегодня"}, nil
}

func (a *contQueryAPI) LatestFact(_ context.Context, key string) (ipc.Fact, error) {
	a.factLooks++
	return ipc.Fact{Key: key, Value: "2л", Ts: contNow.Add(-time.Hour)}, nil
}

func contQueryHandler() (*reactiveHandler, *contQueryAPI) {
	api := &contQueryAPI{}
	return &reactiveHandler{api: api, now: func() time.Time { return contNow }}, api
}

// A continuation is a question about another day, so the one source that can
// read a day answers it — for the day the ellipsis named, not for today.
func TestContinuedQueryReachesTheCalendar(t *testing.T) {
	h, api := contQueryHandler()
	reply := h.actionQuery(context.Background(), router.Decision{
		Intent:    router.IntentQuery,
		Utterance: "а завтра?",
		Continued: true,
		Slots:     router.Slots{Text: "что у меня сегодня", Time: contNow.Add(24 * time.Hour), HasTime: true},
	})
	if api.events != 1 {
		t.Fatalf("CalendarEvents called %d times, want 1", api.events)
	}
	if got, want := api.from.Format("2006-01-02"), "2026-08-02"; got != want {
		t.Errorf("asked the calendar for %s, want %s", got, want)
	}
	if reply == "" {
		t.Error("empty reply")
	}
}

// The regression this gate exists for: every other source is date-blind, so
// letting one claim a continuation answers a question about tomorrow with
// today's data. queryFactByKey was the live case — HasKey plus HasTime, both
// set by the continuation, and it replies with a stored fact's own timestamp.
func TestContinuedQuerySkipsDateBlindSources(t *testing.T) {
	h, api := contQueryHandler()
	h.actionQuery(context.Background(), router.Decision{
		Intent:    router.IntentQuery,
		Utterance: "а вчера?",
		Continued: true,
		Slots: router.Slots{
			Key: "water", HasKey: true,
			Text: "когда я пил воду",
			Time: contNow.Add(-24 * time.Hour), HasTime: true,
		},
	})
	if api.factLooks != 0 {
		t.Errorf("fact-by-key claimed a continuation (%d lookups)", api.factLooks)
	}
	if api.plans != 0 {
		t.Errorf("day-plan claimed a continuation (%d calls)", api.plans)
	}
}

// Nothing date-aware claimed it: say that, rather than "не знаю", which reads
// as "no data for that day" when she never looked.
func TestContinuedQueryWithNoDateAwareAnswerSaysSo(t *testing.T) {
	h, _ := contQueryHandler()
	// No parseable day in the utterance, so even the calendar passes.
	reply := h.actionQuery(context.Background(), router.Decision{
		Intent:    router.IntentQuery,
		Utterance: "а?",
		Continued: true,
		Slots:     router.Slots{Text: "какая погода", HasTime: true},
	})
	if reply == "не знаю." || !strings.Contains(reply, "спроси целиком") {
		t.Fatalf("reply = %q, want the honest continuation refusal", reply)
	}
}

// An ordinary query is untouched by the gate — every source still runs.
func TestOrdinaryQueryStillReachesEverySource(t *testing.T) {
	h, api := contQueryHandler()
	h.actionQuery(context.Background(), router.Decision{
		Intent:    router.IntentQuery,
		Utterance: "какие планы на сегодня?",
	})
	if api.plans != 1 {
		t.Fatalf("day-plan called %d times on an ordinary query, want 1", api.plans)
	}
}
