package store

import (
	"context"
	"errors"
	"testing"
	"time"
)

func seedTrace(t *testing.T, s *Store, utterance, intent string, now time.Time) int64 {
	t.Helper()
	id, err := s.WriteRoutingTrace(context.Background(), RoutingTrace{
		Ts: now, Utterance: utterance, Intent: intent, Source: "tap:text", EncoderID: "e5-small",
	})
	if err != nil {
		t.Fatal(err)
	}
	return id
}

// The label carries the pair, and it is what survives the transcript.
func TestCorrectTurnPromotesTheLabel(t *testing.T) {
	s := newTestStore(t)
	ctx := context.Background()
	now := time.Date(2026, 8, 6, 12, 0, 0, 0, time.UTC)
	id := seedTrace(t, s, "поужинал", "query", now)

	if err := s.CorrectTurn(ctx, id, "fact", now); err != nil {
		t.Fatal(err)
	}
	labels, err := s.RoutingLabels(ctx, 10)
	if err != nil {
		t.Fatal(err)
	}
	if len(labels) != 1 {
		t.Fatalf("got %d labels, want 1", len(labels))
	}
	l := labels[0]
	if l.Utterance != "поужинал" || l.Was != "query" || l.ShouldBe != "fact" {
		t.Errorf("label %+v: the pair is what names the confusion", l)
	}
	if l.EncoderID != "e5-small" {
		t.Errorf("encoder_id %q: a fitted distance means nothing without the body", l.EncoderID)
	}
	// The trace is stamped too, so the same turn cannot be corrected twice into
	// two labels without the surface knowing.
	traces, err := s.RecentRoutingTraces(ctx, 10)
	if err != nil {
		t.Fatal(err)
	}
	if traces[0].Correction != "fact" {
		t.Errorf("trace correction %q, want fact", traces[0].Correction)
	}
}

// "Wrong, and I am not telling you what it was" is the cheap half of the
// gesture, and it must not cost more than the full answer.
func TestCorrectTurnWithNoTarget(t *testing.T) {
	s := newTestStore(t)
	ctx := context.Background()
	now := time.Date(2026, 8, 6, 12, 0, 0, 0, time.UTC)
	id := seedTrace(t, s, "закрывай", "act", now)

	if err := s.CorrectTurn(ctx, id, "  ", now); err != nil {
		t.Fatal(err)
	}
	labels, err := s.RoutingLabels(ctx, 10)
	if err != nil {
		t.Fatal(err)
	}
	if len(labels) != 1 || labels[0].ShouldBe != "" {
		t.Fatalf("labels %+v: an untargeted negative is still a label", labels)
	}
	traces, _ := s.RecentRoutingTraces(ctx, 10)
	if traces[0].Correction != "wrong" {
		t.Errorf("trace correction %q, want wrong", traces[0].Correction)
	}
}

// His second answer is the one he meant, so a re-correction replaces.
func TestCorrectTurnTwiceReplaces(t *testing.T) {
	s := newTestStore(t)
	ctx := context.Background()
	now := time.Date(2026, 8, 6, 12, 0, 0, 0, time.UTC)
	first := seedTrace(t, s, "поужинал", "query", now)
	second := seedTrace(t, s, "поужинал", "chat", now.Add(time.Minute))

	if err := s.CorrectTurn(ctx, first, "note", now); err != nil {
		t.Fatal(err)
	}
	if err := s.CorrectTurn(ctx, second, "fact", now.Add(time.Minute)); err != nil {
		t.Fatal(err)
	}
	labels, err := s.RoutingLabels(ctx, 10)
	if err != nil {
		t.Fatal(err)
	}
	if len(labels) != 1 {
		t.Fatalf("got %d labels for one sentence, want 1", len(labels))
	}
	if labels[0].ShouldBe != "fact" || labels[0].Was != "chat" {
		t.Errorf("label %+v, want the second correction", labels[0])
	}
}

// A turn past the 14-day bound cannot be corrected, and the surface has to be
// able to say that rather than report a broken database.
func TestCorrectTurnUnknownTrace(t *testing.T) {
	s := newTestStore(t)
	err := s.CorrectTurn(context.Background(), 999, "fact", time.Now())
	if !errors.Is(err, ErrNoSuchTrace) {
		t.Fatalf("err %v, want ErrNoSuchTrace", err)
	}
	labels, _ := s.RoutingLabels(context.Background(), 10)
	if len(labels) != 0 {
		t.Errorf("wrote %d labels for a trace that does not exist", len(labels))
	}
}
