package pipeline

import (
	"context"
	"testing"

	"textmachine/backend/internal/obs"
)

// runner_readonly_test.go pins the operator-visibility contract (пакет №4): the
// read-only runner (tmctl status/report) must work WHILE a write runner owns the
// project — «оператор слеп ровно когда висит 300-я глава» was the top smoke
// finding, caused by status/report sharing the writer's exclusive flock.

// TestReadOnlyRunnerWorksDuringLiveRun: a NewReadOnlyRunner must open and project
// Status while a NewRunner (write path, flock held) is alive on the same book.
func TestReadOnlyRunnerWorksDuringLiveRun(t *testing.T) {
	rec := &reqRec{}
	srv := newJSONProvider(rec, draftEdit)
	defer srv.Close()
	bookPath := setupProject(t, srv.URL)
	ctx := obs.WithReqInfo(context.Background(), obs.ReqInfo{TraceID: obs.NewTraceID()})

	// The write runner owns the project (flock held for its whole lifetime) and
	// has completed a run, so there are rows to project.
	w := newRunner(t, bookPath)
	defer w.Close()
	if _, err := w.TranslateBook(ctx); err != nil {
		t.Fatal(err)
	}

	// Simulates `tmctl status` from a second shell mid-run: the write runner is
	// still open. The old store.Open path failed here with "project database is
	// in use by another tmctl process".
	ro, err := NewReadOnlyRunner(bookPath, obs.NewLogger())
	if err != nil {
		t.Fatalf("read-only runner must open during a live run: %v", err)
	}
	defer ro.Close()

	rep, err := ro.Status(ctx)
	if err != nil {
		t.Fatal(err)
	}
	if rep.TotalChunks != 1 || rep.Done != 1 || rep.Flagged != 0 {
		t.Fatalf("status projection over a live-locked book = %+v", rep)
	}
	if rep.CommittedUSD == 0 {
		t.Fatal("status must see the writer's committed spend")
	}
}

// TestReadOnlyRunnerFirstTouchCreates: before the first run there is no DB file —
// the read-only runner falls back to the creating Open (мигрирует пустую базу), so
// «status до первого прогона» keeps showing 0/N pending instead of erroring.
func TestReadOnlyRunnerFirstTouchCreates(t *testing.T) {
	rec := &reqRec{}
	srv := newJSONProvider(rec, draftEdit)
	defer srv.Close()
	bookPath := setupProject(t, srv.URL)
	ctx := obs.WithReqInfo(context.Background(), obs.ReqInfo{TraceID: obs.NewTraceID()})

	ro, err := NewReadOnlyRunner(bookPath, obs.NewLogger())
	if err != nil {
		t.Fatal(err)
	}
	defer ro.Close()
	rep, err := ro.Status(ctx)
	if err != nil {
		t.Fatal(err)
	}
	if rep.TotalChunks != 1 || rep.Done != 0 || rep.Pending != 1 {
		t.Fatalf("pre-run status = %+v", rep)
	}
	if rec.count() != 0 {
		t.Fatalf("read-only status must make zero provider calls, got %d", rec.count())
	}
}
