package pgstore

import (
	"context"
	"testing"
	"time"

	"textmachine/platform/internal/money"
)

// SpendBound says "the SMALLEST meter reading any LATER attempt of the same book recorded before it
// started", and the smallest is the only correct choice: a later reading already contains the spend
// of everything between, so bounding by a bigger one charges this attempt for its successors' work —
// which is PD-159, measured at $0.10 charged as $2.10.
//
// The pin PD-159 names puts exactly ONE later attempt in the book, and on a set of one min and max
// agree, so the word "smallest" is not executed by it. Here there are THREE later readings and the
// smallest is neither the first nor the last of them.
func TestR1TheSpendBoundIsTheSmallestLaterBaselineAndNotMerelyALaterOne(t *testing.T) {
	s, ctx := testDB(t)
	now := fundedAccount(t, s, ctx, "u1", "10")
	seedBook(t, s, ctx, "bk1", "u1", 500)

	subject := r1AttemptWithBaseline(t, s, ctx, now, 50_000)
	// Three attempts start AFTER it. Their baselines are deliberately out of id order: the smallest
	// sits in the middle, so neither "the first later one" nor "the last later one" is it.
	r1AttemptWithBaseline(t, s, ctx, now, 900_000)
	r1AttemptWithBaseline(t, s, ctx, now, 200_000)
	r1AttemptWithBaseline(t, s, ctx, now, 700_000)

	bound, err := s.SpendBound(ctx, "bk1", subject)
	if err != nil {
		t.Fatal(err)
	}
	if bound == nil {
		t.Fatal("no bound at all, want the smallest later baseline")
	}
	if want := money.MicroUSD(200_000); *bound != want {
		t.Fatalf("the bound is %s, want %s — the SMALLEST later baseline", bound.USD(), want.USD())
	}
}

// r1AttemptWithBaseline admits one run on bk1, records a spawn for it carrying the given book-meter
// reading, then finishes the run so the next one can be admitted (runs_one_live_per_book).
func r1AttemptWithBaseline(t *testing.T, s *Store, ctx context.Context, now time.Time, baseline money.MicroUSD) int64 {
	t.Helper()
	run, err := s.StartRun(ctx, StartRunInput{UserID: "u1", BookID: "bk1", CeilingChapters: 10,
		Ceiling: money.MicroUSD(300_000), Now: now}, 0, nil)
	if err != nil {
		t.Fatal(err)
	}
	claimed, err := s.RecordSpawn(ctx, SpawnRecord{AttemptID: run.AttemptID, Unit: "tm-run-" + run.ID,
		Binary: "/opt/engine/1/tmctl", Ceiling: 300_000, CeilingArg: 300_000 + baseline, Baseline: baseline})
	if err != nil || !claimed {
		t.Fatalf("record spawn: claimed=%v err=%v", claimed, err)
	}
	exec(t, s, ctx, `update runs set finished_at = $2, status = 'ready' where id = $1`, run.ID, now)
	return run.AttemptID
}
