package app

import "testing"

// History is a single global ring shared by every input surface (free-chat, the
// workflow-intent line, in-session chat), so ↑/↓ recalls anything you've sent — including
// outside a session, which the per-session map could not do.
func TestRecordInputGlobalRing(t *testing.T) {
	m := NewModel(nil)

	m.recordInput("first")
	m.recordInput("  second  ") // trimmed
	m.recordInput("second")     // immediate dup ignored
	m.recordInput("")           // blank ignored
	m.recordInput("   ")        // blank-after-trim ignored

	want := []string{"first", "second"}
	if len(m.inputHistory) != len(want) {
		t.Fatalf("inputHistory = %q, want %q", m.inputHistory, want)
	}
	for i := range want {
		if m.inputHistory[i] != want[i] {
			t.Fatalf("inputHistory[%d] = %q, want %q", i, m.inputHistory[i], want[i])
		}
	}
}

func TestRecordInputCaps(t *testing.T) {
	m := NewModel(nil)
	for i := 0; i < 250; i++ {
		m.recordInput(string(rune('a'+i%26)) + itoa(i))
	}
	if len(m.inputHistory) != 100 {
		t.Fatalf("ring len = %d, want 100 (capped)", len(m.inputHistory))
	}
}

// (itoa is defined in view.go and reused here for the cap test.)

// ↑/↓ walks the ring with no session selected (selectedID == ""), proving history
// recall works in free-chat / intent contexts, not just in-session.
func TestHistoryWalkWithoutSession(t *testing.T) {
	m := NewModel(nil)
	m.recordInput("one")
	m.recordInput("two")
	m.inputBuffer = "draft"

	m.historyPrev() // newest first
	if m.inputBuffer != "two" {
		t.Fatalf("prev#1 = %q, want %q", m.inputBuffer, "two")
	}
	m.historyPrev()
	if m.inputBuffer != "one" {
		t.Fatalf("prev#2 = %q, want %q", m.inputBuffer, "one")
	}
	m.historyPrev() // clamp at oldest
	if m.inputBuffer != "one" {
		t.Fatalf("prev#3 (clamp) = %q, want %q", m.inputBuffer, "one")
	}
	m.historyNext()
	if m.inputBuffer != "two" {
		t.Fatalf("next#1 = %q, want %q", m.inputBuffer, "two")
	}
	m.historyNext() // back to the stashed live draft
	if m.inputBuffer != "draft" {
		t.Fatalf("next#2 = %q, want restored draft %q", m.inputBuffer, "draft")
	}
}

// (animating + caps tests below.)

// animating() gates the redraw tick: it must be FALSE on an idle screen (normal mode,
// no active session) so terminal selection survives, and TRUE while typing or spinning.
func TestAnimatingGatesIdleRedraw(t *testing.T) {
	m := NewModel(nil)
	m.editMode = ModeNormal
	if m.animating() {
		t.Fatal("idle (normal mode, no active session) must NOT animate — else selection is wiped")
	}

	// Insert mode must NOT force the frame loop anymore: the composer caret is a real
	// terminal cursor (View.Cursor) that the terminal blinks itself, so an idle insert
	// screen holds still (and native text selection survives).
	m.editMode = ModeInsert
	if m.animating() {
		t.Fatal("insert mode must NOT animate — the native cursor blinks itself")
	}
	m.editMode = ModeNormal

	// Other typing surfaces still draw their own carets and keep ticking.
	m.steering = true
	if !m.animating() {
		t.Fatal("steering (drawn caret) must animate")
	}
	m.steering = false

	m.sessions = []Session{{ID: "s1", Active: true}}
	if !m.animating() {
		t.Fatal("an active session must animate (spinner)")
	}
}
