package app

import (
	"strings"
	"testing"
)

// splitCursor must report the caret's display column/row from a composited frame
// (ANSI ignored) and swap the marker for the replacement glyph.
func TestSplitCursorCoords(t *testing.T) {
	// marker sits after 3 visible cells ("a", "b", "c") on the second line.
	raw := "first line\n" + "ab\x1b[38;2;1;2;3mc\x1b[m" + cursorMarker + "d"
	clean, cur := splitCursor(raw, " ")
	if cur == nil {
		t.Fatal("expected a cursor when the marker is present")
	}
	if cur.X != 3 || cur.Y != 1 {
		t.Fatalf("cursor at (%d,%d), want (3,1)", cur.X, cur.Y)
	}
	if strings.Contains(clean, cursorMarker) {
		t.Error("marker must be stripped from the returned frame")
	}
	if strings.Count(clean, " ") == 0 {
		t.Error("marker should have been replaced by the repl glyph")
	}
}

// No marker (e.g. normal mode) → no cursor, frame unchanged.
func TestSplitCursorAbsent(t *testing.T) {
	raw := "no caret here"
	clean, cur := splitCursor(raw, "▏")
	if cur != nil {
		t.Error("no marker must yield a nil cursor (hidden)")
	}
	if clean != raw {
		t.Error("frame must be unchanged when no marker is present")
	}
}

// The live View hides the cursor in normal mode and shows a real one in insert
// mode; the marker must never leak into the rendered content either way.
func TestViewCursorByMode(t *testing.T) {
	m := inSessionModel(120, 40)

	m.editMode = ModeNormal
	v := m.View()
	if v.Cursor != nil {
		t.Error("normal mode must hide the cursor (nil)")
	}
	if strings.Contains(v.Content, cursorMarker) {
		t.Error("content must not contain the raw cursor marker")
	}

	m.editMode = ModeInsert
	m.inputMode = ModeRouter
	m.inputBuffer = "hello"
	m.inputCursor = len(m.inputBuffer)
	v = m.View()
	if v.Cursor == nil {
		t.Fatal("insert mode must show a real cursor")
	}
	if v.Cursor.Y < 0 || v.Cursor.Y >= m.height || v.Cursor.X < 0 {
		t.Errorf("cursor (%d,%d) out of frame bounds", v.Cursor.X, v.Cursor.Y)
	}
	if strings.Contains(v.Content, cursorMarker) {
		t.Error("content must not contain the raw cursor marker")
	}

	// The static render keeps a visible glyph and also strips the marker.
	if strings.Contains(m.render(), cursorMarker) {
		t.Error("render() must strip the marker")
	}

	// An open overlay keeps editMode==Insert (so closing resumes typing) but the
	// composer must not draw a live cursor behind the modal.
	m.overlay = OverlayPalette
	if v = m.View(); v.Cursor != nil {
		t.Error("an open overlay must suppress the composer cursor")
	}
	if strings.Contains(v.Content, cursorMarker) {
		t.Error("content must not contain the marker with an overlay open")
	}
}
