package cql

import (
	"bytes"
	"strings"
	"testing"
)

func TestReadValueListPositional(t *testing.T) {
	var buf bytes.Buffer
	buf.Write(writeShort(2))
	buf.Write(writeBytes([]byte("one")))
	buf.Write(writeBytes(nil))

	parser := NewFrameBodyParser(buf.Bytes())
	values, named, err := parser.ReadValueList(QueryFlagValues)
	if err != nil {
		t.Fatalf("ReadValueList failed: %v", err)
	}
	if named {
		t.Fatalf("expected positional values")
	}
	if len(values) != 2 {
		t.Fatalf("expected 2 values, got %d", len(values))
	}
	if values[0].Name != "" || values[1].Name != "" {
		t.Fatalf("unexpected names for positional values")
	}
	if string(values[0].Value) != "one" {
		t.Fatalf("expected first value 'one', got %q", string(values[0].Value))
	}
	if values[1].Value != nil {
		t.Fatalf("expected second value nil, got %v", values[1].Value)
	}
	if values[1].Unset {
		t.Fatalf("expected second value unset=false")
	}
}

func TestReadValueListNamed(t *testing.T) {
	var buf bytes.Buffer
	buf.Write(writeShort(2))
	buf.Write(writeString("a"))
	buf.Write(writeBytes([]byte("1")))
	buf.Write(writeString("b"))
	buf.Write(writeBytes([]byte("2")))

	parser := NewFrameBodyParser(buf.Bytes())
	values, named, err := parser.ReadValueList(QueryFlagValues | QueryFlagWithNameValues)
	if err != nil {
		t.Fatalf("ReadValueList failed: %v", err)
	}
	if !named {
		t.Fatalf("expected named values")
	}
	if len(values) != 2 {
		t.Fatalf("expected 2 values, got %d", len(values))
	}
	if values[0].Name != "a" || string(values[0].Value) != "1" {
		t.Fatalf("unexpected first named value: %#v", values[0])
	}
	if values[1].Name != "b" || string(values[1].Value) != "2" {
		t.Fatalf("unexpected second named value: %#v", values[1])
	}
}

func TestReadValueListUnset(t *testing.T) {
	var buf bytes.Buffer
	buf.Write(writeShort(1))
	buf.Write(writeInt(int32(UnsetValue)))

	parser := NewFrameBodyParser(buf.Bytes())
	values, named, err := parser.ReadValueList(QueryFlagValues)
	if err != nil {
		t.Fatalf("ReadValueList failed: %v", err)
	}
	if named {
		t.Fatalf("expected positional values")
	}
	if len(values) != 1 {
		t.Fatalf("expected 1 value, got %d", len(values))
	}
	if values[0].Value != nil {
		t.Fatalf("expected unset value to be nil, got %v", values[0].Value)
	}
	if !values[0].Unset {
		t.Fatalf("expected unset value to be marked unset")
	}
}

func TestReadValueListNamedRequiresValuesFlag(t *testing.T) {
	parser := NewFrameBodyParser(nil)
	if _, _, err := parser.ReadValueList(QueryFlagWithNameValues); err == nil {
		t.Fatalf("expected error for named values without values flag")
	}
}

func TestErrorFrameBuilderSanitizesBackendMessage(t *testing.T) {
	builder := NewErrorFrameBuilder(ProtoVersion4)
	frame := builder.Build(7, ErrorCodeInvalid, "failed to ensure cql_meta.columns.default_expr: Error 8027 (HY000): Information schema is out of date: schema failed to update in 1 lease, please make sure TiDB can connect to TiKV")

	parser := NewFrameBodyParser(frame.Body)
	code, err := parser.ReadInt()
	if err != nil {
		t.Fatalf("ReadInt failed: %v", err)
	}
	if int(code) != ErrorCodeInvalid {
		t.Fatalf("error code = %d, want %d", code, ErrorCodeInvalid)
	}

	msg, err := parser.ReadString()
	if err != nil {
		t.Fatalf("ReadString failed: %v", err)
	}
	if strings.Contains(strings.ToLower(msg), "tidb") {
		t.Fatalf("expected TiDB to be sanitized, got %q", msg)
	}
	if strings.Contains(strings.ToLower(msg), "mysql") {
		t.Fatalf("expected MySQL to be sanitized, got %q", msg)
	}
	if strings.Contains(strings.ToLower(msg), "tikv") {
		t.Fatalf("expected TiKV to be sanitized, got %q", msg)
	}
	if strings.Contains(msg, "HY000") || strings.Contains(msg, "Error 8027") {
		t.Fatalf("expected backend error prefix/code to be stripped, got %q", msg)
	}
	if !strings.Contains(msg, "backend can connect to backend") {
		t.Fatalf("expected backend keywords to be rewritten, got %q", msg)
	}
}
