package termcolor

import (
	"maps"
	"testing"
)

func TestParseReplies(t *testing.T) {
	for _, tc := range []struct {
		name string
		in   string
		want map[int]Color
	}{
		{"four digit channels with st", "\x1b]10;rgb:cccc/cccc/cccc\x1b\\\x1b]11;rgb:1c1c/2a2a/3c3c\x1b\\", map[int]Color{
			Foreground: {0xcccc, 0xcccc, 0xcccc}, Background: {0x1c1c, 0x2a2a, 0x3c3c}}},
		{"two digit channels with bel", "\x1b]4;5;rgb:ff/00/80\a\x1b]12;rgb:11/22/33\a", map[int]Color{
			5: {0xffff, 0, 0x8080}, Cursor: {0x1111, 0x2222, 0x3333}}},
		{"one digit channels", "\x1b]4;0;rgb:f/0/a\x1b\\", map[int]Color{0: {0xffff, 0, 0xaaaa}}},
		{"three digit channels", "\x1b]4;1;rgb:fff/000/fff\x1b\\", map[int]Color{1: {0xffff, 0, 0xffff}}},
		{"sentinel and junk around replies", "\x1b[?62;1c\x1b]4;1;rgb:8888/8888/8888\x1b\\junk", map[int]Color{
			1: {0x8888, 0x8888, 0x8888}}},
		{"incomplete reply ignored", "\x1b]10;rgb:cccc/c", nil},
		{"unrelated osc ignored", "\x1b]7;file:///tmp\x1b\\\x1b[?1;2c", nil},
	} {
		got := ParseReplies([]byte(tc.in))
		if len(got) != len(tc.want) {
			t.Fatalf("%s: ParseReplies(%q) = %v, want %v", tc.name, tc.in, got, tc.want)
		}
		for k, v := range tc.want {
			if got[k] != v {
				t.Fatalf("%s: ParseReplies(%q)[%d] = %v, want %v", tc.name, tc.in, k, got[k], v)
			}
		}
	}
}

func TestDA1End(t *testing.T) {
	in := "\x1b]10;?x\x1b[?62;22c rest"
	if got, want := DA1End([]byte(in)), len("\x1b]10;?x\x1b[?62;22c"); got != want {
		t.Fatalf("DA1End = %d, want %d", got, want)
	}
	if DA1End([]byte("\x1b]10;rgb:0000/0000/0000\x1b\\")) != -1 {
		t.Fatal("a color reply is not the sentinel")
	}
	if DA1End([]byte("\x1b[?62;")) != -1 {
		t.Fatal("an incomplete sentinel must not match")
	}
}

func TestEncodeColorsRoundTrip(t *testing.T) {
	in := map[int]Color{Foreground: {0xffff, 0xffff, 0xffff}, 5: {0x8000, 0, 0x8000}, Background: {0, 0, 0}}
	out, ok := DecodeColors(EncodeColors(in))
	if !ok || !maps.Equal(out, in) {
		t.Fatalf("round trip = %v %v, want %v", out, ok, in)
	}
	if EncodeColors(nil) != nil {
		t.Fatal("no colors must encode to nothing so the payload stays legacy")
	}
	if _, ok := DecodeColors(nil); ok {
		t.Fatal("a legacy payload must decode as unknown")
	}
	if _, ok := DecodeColors([]byte{colorsVersion + 1, 1}); ok {
		t.Fatal("an unknown version must decode as unknown")
	}
	if _, ok := DecodeColors([]byte{colorsVersion, 2, 0, 0, 0, 0, 0, 0, 0}); ok {
		t.Fatal("a truncated section must decode as unknown")
	}
}

func TestAgreed(t *testing.T) {
	a := map[int]Color{Foreground: {1, 2, 3}, Background: {4, 5, 6}}
	b := map[int]Color{Foreground: {1, 2, 3}, Background: {7, 8, 9}}
	if c, ok := Agreed(Foreground, []map[int]Color{a, b}); !ok || c != (Color{1, 2, 3}) {
		t.Fatalf("agreed foreground = %v %v", c, ok)
	}
	if _, ok := Agreed(Background, []map[int]Color{a, b}); ok {
		t.Fatal("a disagreement must stay silent")
	}
	if _, ok := Agreed(Cursor, []map[int]Color{a, b}); ok {
		t.Fatal("a color nobody reported must stay silent")
	}
	if _, ok := Agreed(Foreground, []map[int]Color{a, nil}); ok {
		t.Fatal("a viewer without colors must stay silent")
	}
	if _, ok := Agreed(Foreground, nil); ok {
		t.Fatal("zero viewers must stay silent")
	}
}

func TestReply(t *testing.T) {
	c := Color{0x1234, 0x5678, 0x9abc}
	if got, want := Reply(5, c), "\x1b]4;5;rgb:1234/5678/9abc\x1b\\"; got != want {
		t.Fatalf("Reply(5) = %q, want %q", got, want)
	}
	if got, want := Reply(Background, c), "\x1b]11;rgb:1234/5678/9abc\x1b\\"; got != want {
		t.Fatalf("Reply(Background) = %q, want %q", got, want)
	}
}
