package term

import (
	"testing"
	"unicode"

	"go.rockorager.dev/vaxis"
)

func TestModifyOtherKeysStateTwoEncodesModifiedText(t *testing.T) {
	vt, r := newReplyTestModel(t)
	vt.resize(80, 24)

	vt.update(testCSI('m', []uint32{4, 2}, '>'))
	vt.Update(vaxis.Key{
		Keycode:     'h',
		ShiftedCode: 'H',
		Text:        "H",
		Modifiers:   vaxis.ModShift | vaxis.ModCtrl,
		EventType:   vaxis.EventPress,
	})

	if got, want := readReply(t, r, len("\x1B[27;6;72~")), "\x1B[27;6;72~"; got != want {
		t.Fatalf("modifyOtherKeys encoded key = %q, want %q", got, want)
	}
}

func TestModifyOtherKeysStateTwoEncodesAltDigit(t *testing.T) {
	vt, r := newReplyTestModel(t)
	vt.resize(80, 24)

	vt.update(testCSI('m', []uint32{4, 2}, '>'))
	vt.Update(vaxis.Key{
		Keycode:   '8',
		Text:      "8",
		Modifiers: vaxis.ModAlt,
		EventType: vaxis.EventPress,
	})

	if got, want := readReply(t, r, len("\x1B[27;3;56~")), "\x1B[27;3;56~"; got != want {
		t.Fatalf("modifyOtherKeys encoded digit = %q, want %q", got, want)
	}
}

func TestModifyOtherKeysStateTwoEncodesPlainASCIIText(t *testing.T) {
	vt, r := newReplyTestModel(t)
	vt.resize(80, 24)

	vt.update(testCSI('m', []uint32{4, 2}, '>'))
	vt.Update(vaxis.Key{Keycode: 'x', Text: "x", EventType: vaxis.EventPress})

	if got, want := readReply(t, r, len("\x1B[27;1;120~")), "\x1B[27;1;120~"; got != want {
		t.Fatalf("plain ASCII key with modifyOtherKeys = %q, want %q", got, want)
	}
}

func TestModifyOtherKeysStateTwoDoesNotEncodePasteText(t *testing.T) {
	vt, r := newReplyTestModel(t)
	vt.resize(80, 24)
	vt.mode.paste = true

	vt.update(testCSI('m', []uint32{4, 2}, '>'))
	vt.Update(vaxis.PasteStartEvent{})
	const pasted = "https://example.org/Linux-6"
	for _, ch := range pasted {
		key := vaxis.Key{Keycode: ch, Text: string(ch), EventType: vaxis.EventPaste}
		if unicode.IsUpper(ch) {
			key.Keycode = unicode.ToLower(ch)
			key.ShiftedCode = ch
			key.Modifiers = vaxis.ModShift
		}
		vt.Update(key)
	}
	vt.Update(vaxis.PasteEndEvent{})

	want := "\x1B[200~" + pasted + "\x1B[201~"
	if got := readReply(t, r, len(want)); got != want {
		t.Fatalf("paste with modifyOtherKeys = %q, want %q", got, want)
	}
}

func TestModifyOtherKeysStateTwoBackspaceUsesGhosttyTable(t *testing.T) {
	t.Run("plain", func(t *testing.T) {
		vt, r := newReplyTestModel(t)
		vt.resize(80, 24)

		vt.update(testCSI('m', []uint32{4, 2}, '>'))
		vt.Update(vaxis.Key{Keycode: vaxis.KeyBackspace, EventType: vaxis.EventPress})

		if got, want := readReply(t, r, len("\x7F")), "\x7F"; got != want {
			t.Fatalf("plain backspace with modifyOtherKeys = %q, want %q", got, want)
		}
	})

	t.Run("ctrl", func(t *testing.T) {
		vt, r := newReplyTestModel(t)
		vt.resize(80, 24)

		vt.update(testCSI('m', []uint32{4, 2}, '>'))
		vt.Update(vaxis.Key{Keycode: vaxis.KeyBackspace, Modifiers: vaxis.ModCtrl, EventType: vaxis.EventPress})

		if got, want := readReply(t, r, len("\x08")), "\x08"; got != want {
			t.Fatalf("ctrl backspace with modifyOtherKeys = %q, want %q", got, want)
		}
	})

	t.Run("decbkm", func(t *testing.T) {
		vt, r := newReplyTestModel(t)
		vt.resize(80, 24)

		vt.update(testCSI('m', []uint32{4, 2}, '>'))
		vt.update(testCSI('h', []uint32{67}, '?'))
		vt.Update(vaxis.Key{Keycode: vaxis.KeyBackspace, EventType: vaxis.EventPress})
		vt.Update(vaxis.Key{Keycode: vaxis.KeyBackspace, Modifiers: vaxis.ModCtrl, EventType: vaxis.EventPress})

		if got, want := readReply(t, r, len("\x08\x7F")), "\x08\x7F"; got != want {
			t.Fatalf("backspace with modifyOtherKeys and DECBKM = %q, want %q", got, want)
		}
	})

	t.Run("shift", func(t *testing.T) {
		vt, r := newReplyTestModel(t)
		vt.resize(80, 24)

		vt.update(testCSI('m', []uint32{4, 2}, '>'))
		vt.Update(vaxis.Key{Keycode: vaxis.KeyBackspace, Modifiers: vaxis.ModShift, EventType: vaxis.EventPress})

		if got, want := readReply(t, r, len("\x1B[27;2;127~")), "\x1B[27;2;127~"; got != want {
			t.Fatalf("shift backspace with modifyOtherKeys = %q, want %q", got, want)
		}
	})
}

func TestModifyOtherKeysResetFallsBackToLegacyEncoding(t *testing.T) {
	vt, r := newReplyTestModel(t)
	vt.resize(80, 24)

	vt.update(testCSI('m', []uint32{4, 2}, '>'))
	vt.update(testCSI('m', nil, '>'))
	vt.Update(vaxis.Key{Keycode: 'j', Modifiers: vaxis.ModCtrl, EventType: vaxis.EventPress})

	if got, want := readReply(t, r, len("\n")), "\n"; got != want {
		t.Fatalf("legacy key after modifyOtherKeys reset = %q, want %q", got, want)
	}
}

func TestModifyOtherKeysInvalidRequestIgnored(t *testing.T) {
	vt := New()

	vt.update(testCSI('m', []uint32{4, 2}, '>'))
	if !vt.mode.modifyOtherKeys2 {
		t.Fatal("modifyOtherKeys state 2 was not enabled")
	}

	vt.update(testCSI('m', []uint32{9}, '>'))
	vt.update(testCSI('m', []uint32{4, 2, 1}, '>'))
	if !vt.mode.modifyOtherKeys2 {
		t.Fatal("invalid modifyOtherKeys request changed state")
	}

	vt.update(testCSI('n', nil, '>'))
	if vt.mode.modifyOtherKeys2 {
		t.Fatal("CSI > n did not reset modifyOtherKeys state")
	}
}

func TestModifyOtherKeysCSIPrivateNResetsWithParameters(t *testing.T) {
	vt := New()

	vt.update(testCSI('m', []uint32{4, 2}, '>'))
	if !vt.mode.modifyOtherKeys2 {
		t.Fatal("modifyOtherKeys state 2 was not enabled")
	}

	vt.update(testCSI('n', []uint32{9, 1}, '>'))

	if vt.mode.modifyOtherKeys2 {
		t.Fatal("CSI > n with parameters did not reset modifyOtherKeys state")
	}
}
