package cql

import (
	"bytes"
	"context"
	"encoding/binary"
	"testing"

	"github.com/everything-on-mysql/cassandra-on-mysql/pkg/cql/executor"
	"go.uber.org/zap"
)

// TestCRC24 tests the CRC24 implementation
func TestCRC24(t *testing.T) {
	tests := []struct {
		name  string
		input []byte
	}{
		{"empty", []byte{}},
		{"single byte", []byte{0x01}},
		{"multiple bytes", []byte{0x01, 0x02, 0x03}},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := Crc24(tt.input)
			// Just verify it returns a valid value
			if result == 0 && len(tt.input) > 0 {
				t.Errorf("Crc24(%v) returned 0, expected non-zero", tt.input)
			}
		})
	}
}

// TestCRC32 tests the CRC32 implementation
func TestCRC32(t *testing.T) {
	tests := []struct {
		name  string
		input []byte
	}{
		{"empty", []byte{}},
		{"simple", []byte{0x01, 0x02, 0x03}},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := Crc32(tt.input)
			// Just verify it returns a valid value
			if result == 0 && len(tt.input) > 0 {
				t.Errorf("Crc32(%v) returned 0, expected non-zero", tt.input)
			}
		})
	}
}

// TestFrameParsing tests basic frame parsing
func TestFrameParsing(t *testing.T) {
	// Create a simple frame
	body := []byte{0x00, 0x01, 0x02, 0x03}
	header := make([]byte, 9)
	header[0] = ProtoVersion4                                  // Version
	header[1] = 0x00                                           // Flags
	binary.BigEndian.PutUint16(header[2:4], 0)                 // Stream
	header[4] = OpcodeReady                                    // Opcode
	binary.BigEndian.PutUint32(header[5:9], uint32(len(body))) // Length

	frameData := append(header, body...)

	frame, err := ParseFrame(frameData)
	if err != nil {
		t.Fatalf("ParseFrame failed: %v", err)
	}

	if frame.Version != ProtoVersion4 {
		t.Errorf("Version = %d, want %d", frame.Version, ProtoVersion4)
	}
	if frame.Opcode != OpcodeReady {
		t.Errorf("Opcode = %d, want %d", frame.Opcode, OpcodeReady)
	}
	if frame.Length != int32(len(body)) {
		t.Errorf("Length = %d, want %d", frame.Length, len(body))
	}
}

// TestBuildStartupFrame tests building a STARTUP frame
func TestBuildStartupFrame(t *testing.T) {
	frame := BuildStartupFrame("3.4.0", "test-driver", "1.0.0", "")

	if frame.Opcode != OpcodeStartup {
		t.Errorf("Opcode = %d, want %d", frame.Opcode, OpcodeStartup)
	}

	// Parse the body to verify it contains the options
	body := frame.Body
	if len(body) < 2 {
		t.Fatal("Body too short")
	}

	// Read number of entries
	numEntries := binary.BigEndian.Uint16(body[0:2])
	if numEntries < 1 {
		t.Errorf("Expected at least 1 option, got %d", numEntries)
	}
}

// TestBuildAuthResponseFrame tests building an AUTH_RESPONSE frame
func TestBuildAuthResponseFrame(t *testing.T) {
	frame := BuildAuthResponseFrame("testuser", "testpass")

	if frame.Opcode != OpcodeAuthResponse {
		t.Errorf("Opcode = %d, want %d", frame.Opcode, OpcodeAuthResponse)
	}

	// Verify body contains credentials
	if len(frame.Body) < 4 {
		t.Fatal("Body too short")
	}

	// Read length prefix
	length := binary.BigEndian.Uint32(frame.Body[0:4])
	if length == 0 {
		t.Error("Length should not be 0")
	}

	// Extract credentials
	credentials := frame.Body[4 : 4+length]
	if len(credentials) < 2 {
		t.Fatal("Credentials too short")
	}

	// Skip first byte (should be 0)
	credentials = credentials[1:]

	// Find null terminator
	nullIndex := -1
	for i, b := range credentials {
		if b == 0 {
			nullIndex = i
			break
		}
	}

	if nullIndex == -1 {
		t.Error("No null terminator found")
	}

	username := string(credentials[0:nullIndex])
	password := string(credentials[nullIndex+1:])

	if username != "testuser" {
		t.Errorf("Username = %s, want testuser", username)
	}
	if password != "testpass" {
		t.Errorf("Password = %s, want testpass", password)
	}
}

// TestSegmentFormat tests protocol v5+ segment formatting
func TestSegmentFormat(t *testing.T) {
	payload := []byte{0x01, 0x02, 0x03, 0x04, 0x05}

	// Test uncompressed segment
	segment := &Segment{
		Payload:       payload,
		SelfContained: true,
		Compressed:    false,
	}

	var buf bytes.Buffer
	err := segment.WriteSegment(&buf, nil)
	if err != nil {
		t.Fatalf("WriteSegment failed: %v", err)
	}

	// Read it back
	readSegment, err := ReadSegment(&buf, nil)
	if err != nil {
		t.Fatalf("ReadSegment failed: %v", err)
	}

	if !bytes.Equal(readSegment.Payload, payload) {
		t.Errorf("Payload mismatch")
	}
	if readSegment.SelfContained != true {
		t.Error("SelfContained should be true")
	}
}

// TestStringMapEncoding tests string map encoding/decoding
func TestStringMapEncoding(t *testing.T) {
	input := map[string]string{
		"CQL_VERSION": "3.4.0",
		"COMPRESSION": "",
	}

	encoded := writeStringMap(input)
	decoded, err := readStringMap(encoded)
	if err != nil {
		t.Fatalf("readStringMap failed: %v", err)
	}

	for k, v := range input {
		if decoded[k] != v {
			t.Errorf("Key %s: got %s, want %s", k, decoded[k], v)
		}
	}
}

// TestStringListEncoding tests string list encoding/decoding
func TestStringListEncoding(t *testing.T) {
	input := []string{"TOPOLOGY_CHANGE", "STATUS_CHANGE", "SCHEMA_CHANGE"}

	encoded := writeStringListFromBytes(input)
	decoded, err := readStringListFromBytes(encoded)
	if err != nil {
		t.Fatalf("readStringListFromBytes failed: %v", err)
	}

	if len(decoded) != len(input) {
		t.Errorf("Length mismatch: got %d, want %d", len(decoded), len(input))
	}

	for i, s := range input {
		if decoded[i] != s {
			t.Errorf("Index %d: got %s, want %s", i, decoded[i], s)
		}
	}
}

// TestTypeInfoEncoding tests type info encoding/decoding
func TestTypeInfoEncoding(t *testing.T) {
	tests := []struct {
		name string
		info TypeInfo
	}{
		{"native", &NativeTypeInfo{TypeCode: TypeInt}},
		{"list", &ListTypeInfo{Elem: &NativeTypeInfo{TypeCode: TypeVarchar}}},
		{"map", &MapTypeInfo{
			Key:   &NativeTypeInfo{TypeCode: TypeVarchar},
			Value: &NativeTypeInfo{TypeCode: TypeInt},
		}},
		{"set", &SetTypeInfo{Elem: &NativeTypeInfo{TypeCode: TypeUUID}}},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			encoded := writeTypeInfo(tt.info)
			decoded, n, err := readTypeInfo(encoded)
			if err != nil {
				t.Fatalf("readTypeInfo failed: %v", err)
			}
			if n != len(encoded) {
				t.Errorf("Read %d bytes, expected %d", n, len(encoded))
			}
			if decoded.Code() != tt.info.Code() {
				t.Errorf("Type code mismatch: got %d, want %d", decoded.Code(), tt.info.Code())
			}
		})
	}
}

func TestVectorTypeInfoEncodingOrder(t *testing.T) {
	info := &VectorTypeInfo{
		Size: 3,
		Elem: &NativeTypeInfo{TypeCode: TypeFloat},
	}
	encoded := writeTypeInfo(info)
	want := []byte{
		0x00, 0x32, // vector type code
		0x00, 0x08, // float element type
		0x00, 0x00, 0x00, 0x03, // size
	}
	if !bytes.Equal(encoded, want) {
		t.Fatalf("unexpected vector type info bytes: %x", encoded)
	}

	decoded, n, err := readTypeInfo(encoded)
	if err != nil {
		t.Fatalf("readTypeInfo failed: %v", err)
	}
	if n != len(encoded) {
		t.Fatalf("expected to read %d bytes, got %d", len(encoded), n)
	}
	vec, ok := decoded.(*VectorTypeInfo)
	if !ok {
		t.Fatalf("expected VectorTypeInfo, got %T", decoded)
	}
	if vec.Size != 3 {
		t.Fatalf("expected size 3, got %d", vec.Size)
	}
	if vec.Elem.Code() != TypeFloat {
		t.Fatalf("expected element type float, got %v", vec.Elem.Code())
	}
}

// TestQueryFrameBuilder tests building QUERY frames with various flags
func TestQueryFrameBuilder(t *testing.T) {
	tests := []struct {
		name        string
		query       string
		consistency uint16
		flags       uint32
		values      []QueryValue
	}{
		{
			name:        "simple query",
			query:       "SELECT * FROM test",
			consistency: ConsistencyOne,
			flags:       0,
			values:      nil,
		},
		{
			name:        "query with values",
			query:       "SELECT * FROM test WHERE id = ?",
			consistency: ConsistencyQuorum,
			flags:       QueryFlagValues,
			values:      []QueryValue{{Value: writeInt(123)}},
		},
		{
			name:        "query with page size",
			query:       "SELECT * FROM test",
			consistency: ConsistencyOne,
			flags:       QueryFlagPageSize,
			values:      nil,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			frame := BuildQueryFrame(
				tt.query,
				tt.consistency,
				tt.flags,
				tt.values,
				100, // pageSize
				nil, // pagingState
				0,   // serialConsistency
				0,   // timestamp
				"",  // keyspace
				nil, // nowInSeconds
			)

			if frame.Opcode != OpcodeQuery {
				t.Errorf("Opcode = %d, want %d", frame.Opcode, OpcodeQuery)
			}
			if frame.Length == 0 {
				t.Error("Frame body should not be empty")
			}
		})
	}
}

// TestBatchFrameBuilder tests building BATCH frames
func TestBatchFrameBuilder(t *testing.T) {
	statements := []BatchStatement{
		{
			Kind:  0,
			Query: "INSERT INTO test (id, value) VALUES (1, 'test')",
		},
		{
			Kind:  0,
			Query: "INSERT INTO test (id, value) VALUES (2, 'test2')",
		},
	}

	frame := BuildBatchFrame(
		BatchTypeLogged,
		statements,
		ConsistencyOne,
		0,
		0,
		0,
		"",
		nil,
	)

	if frame.Opcode != OpcodeBatch {
		t.Errorf("Opcode = %d, want %d", frame.Opcode, OpcodeBatch)
	}
}

// TestPrepareFrameBuilder tests building PREPARE frames
func TestPrepareFrameBuilder(t *testing.T) {
	frame := BuildPrepareFrame("SELECT * FROM test WHERE id = ?", "")

	if frame.Opcode != OpcodePrepare {
		t.Errorf("Opcode = %d, want %d", frame.Opcode, OpcodePrepare)
	}
}

// TestExecuteFrameBuilder tests building EXECUTE frames
func TestExecuteFrameBuilder(t *testing.T) {
	preparedID := []byte{0x01, 0x02, 0x03, 0x04}
	values := []QueryValue{{Value: writeInt(123)}}

	frame := BuildExecuteFrame(
		preparedID,
		nil,
		ConsistencyOne,
		QueryFlagValues,
		values,
		0,
		nil,
		0,
		0,
		"",
		nil,
	)

	if frame.Opcode != OpcodeExecute {
		t.Errorf("Opcode = %d, want %d", frame.Opcode, OpcodeExecute)
	}
}

// TestRegisterFrameBuilder tests building REGISTER frames
func TestRegisterFrameBuilder(t *testing.T) {
	events := []string{"TOPOLOGY_CHANGE", "STATUS_CHANGE", "SCHEMA_CHANGE"}

	frame := BuildRegisterFrame(events)

	if frame.Opcode != OpcodeRegister {
		t.Errorf("Opcode = %d, want %d", frame.Opcode, OpcodeRegister)
	}

	// Verify body contains the events
	decoded, err := readStringListFromBytes(frame.Body)
	if err != nil {
		t.Fatalf("Failed to decode events: %v", err)
	}

	if len(decoded) != len(events) {
		t.Errorf("Event count mismatch: got %d, want %d", len(decoded), len(events))
	}
}

// TestErrorFrame tests error frame creation
func TestErrorFrame(t *testing.T) {
	errorCode := ErrorCodeUnauthorized
	message := "Authentication required"

	var buf bytes.Buffer
	buf.Write(writeInt(int32(errorCode)))
	buf.Write(writeString(message))

	frame := &Frame{
		Version: ProtoVersion4,
		Flags:   0,
		Stream:  1,
		Opcode:  OpcodeError,
		Length:  int32(buf.Len()),
		Body:    buf.Bytes(),
	}

	if frame.Opcode != OpcodeError {
		t.Errorf("Opcode = %d, want %d", frame.Opcode, OpcodeError)
	}

	// Verify body structure
	if len(frame.Body) < 4 {
		t.Fatal("Error body too short")
	}

	readCode := int32(binary.BigEndian.Uint32(frame.Body[0:4]))
	if readCode != int32(errorCode) {
		t.Errorf("Error code = %d, want %d", readCode, errorCode)
	}
}

// TestFrameWithTracing tests frame with tracing flag
func TestFrameWithTracing(t *testing.T) {
	tracingID := [16]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10}
	body := []byte{0x00, 0x01}

	var buf bytes.Buffer
	// Header
	header := make([]byte, 9)
	header[0] = ProtoVersion4Response
	header[1] = FlagTracing // Set tracing flag
	binary.BigEndian.PutUint16(header[2:4], 0)
	header[4] = OpcodeReady
	binary.BigEndian.PutUint32(header[5:9], uint32(16+len(body)))
	buf.Write(header)

	// Tracing ID
	buf.Write(tracingID[:])

	// Body
	buf.Write(body)

	frame, err := ParseFrame(buf.Bytes())
	if err != nil {
		t.Fatalf("ParseFrame failed: %v", err)
	}

	if frame.TracingID == nil {
		t.Error("TracingID should not be nil")
	} else if *frame.TracingID != tracingID {
		t.Error("TracingID mismatch")
	}
}

// TestFrameWithWarnings tests frame with warnings
func TestFrameWithWarnings(t *testing.T) {
	warnings := []string{"Warning 1", "Warning 2"}
	body := []byte{0x00, 0x01}

	frame := &Frame{
		Version:  ProtoVersion4Response,
		Flags:    0,
		Stream:   0,
		Opcode:   OpcodeReady,
		Body:     body,
		Warnings: warnings,
	}

	var buf bytes.Buffer
	if err := frame.WriteFrame(&buf); err != nil {
		t.Fatalf("WriteFrame failed: %v", err)
	}

	frame, err := ParseFrame(buf.Bytes())
	if err != nil {
		t.Fatalf("ParseFrame failed: %v", err)
	}

	if len(frame.Warnings) != len(warnings) {
		t.Errorf("Warning count mismatch: got %d, want %d", len(frame.Warnings), len(warnings))
	}
	if !bytes.Equal(frame.Body, body) {
		t.Errorf("Body mismatch: got %v, want %v", frame.Body, body)
	}
}

// TestFrameWithCustomPayload tests frame with custom payload.
func TestFrameWithCustomPayload(t *testing.T) {
	payload := CustomPayload{
		"alpha": []byte("one"),
		"beta":  nil,
	}
	body := []byte{0x02, 0x03}

	frame := &Frame{
		Version:       ProtoVersion4Response,
		Flags:         0,
		Stream:        0,
		Opcode:        OpcodeReady,
		Body:          body,
		CustomPayload: payload,
	}

	var buf bytes.Buffer
	if err := frame.WriteFrame(&buf); err != nil {
		t.Fatalf("WriteFrame failed: %v", err)
	}

	frame, err := ParseFrame(buf.Bytes())
	if err != nil {
		t.Fatalf("ParseFrame failed: %v", err)
	}

	if len(frame.CustomPayload) != len(payload) {
		t.Fatalf("Custom payload length mismatch: got %d, want %d", len(frame.CustomPayload), len(payload))
	}
	if got := frame.CustomPayload["alpha"]; !bytes.Equal(got, payload["alpha"]) {
		t.Errorf("Custom payload alpha mismatch: got %v, want %v", got, payload["alpha"])
	}
	if got := frame.CustomPayload["beta"]; got != nil {
		t.Errorf("Custom payload beta mismatch: got %v, want nil", got)
	}
	if !bytes.Equal(frame.Body, body) {
		t.Errorf("Body mismatch: got %v, want %v", frame.Body, body)
	}
}

// TestFrameReaderWriter tests the FrameReader and FrameWriter
func TestFrameReaderWriter(t *testing.T) {
	// Create a frame
	frame := BuildStartupFrame("3.4.0", "", "", "")

	// Write it
	var buf bytes.Buffer
	writer := NewFrameWriter(&buf, nil, 4)
	err := writer.WriteFrame(frame)
	if err != nil {
		t.Fatalf("WriteFrame failed: %v", err)
	}

	// Read it back
	reader := NewFrameReader(&buf, nil, 4)
	readFrame, err := reader.ReadFrame()
	if err != nil {
		t.Fatalf("ReadFrame failed: %v", err)
	}

	if readFrame.Opcode != frame.Opcode {
		t.Errorf("Opcode mismatch: got %d, want %d", readFrame.Opcode, frame.Opcode)
	}
}

// TestProtocolV5Segment tests protocol v5 segment format
func TestProtocolV5Segment(t *testing.T) {
	// Create a frame that would be sent in v5
	frame := BuildOptionsFrame()
	frame.Version = ProtoVersion5 // Set to v5

	// Write frame data
	var frameBuf bytes.Buffer
	if err := frame.WriteFrame(&frameBuf); err != nil {
		t.Fatalf("WriteFrame failed: %v", err)
	}
	frameData := frameBuf.Bytes()

	// Wrap in segment
	segment := &Segment{
		Payload:       frameData,
		SelfContained: true,
		Compressed:    false,
	}

	var segmentBuf bytes.Buffer
	if err := segment.WriteSegment(&segmentBuf, nil); err != nil {
		t.Fatalf("WriteSegment failed: %v", err)
	}

	// Read segment back
	readSegment, err := ReadSegment(&segmentBuf, nil)
	if err != nil {
		t.Fatalf("ReadSegment failed: %v", err)
	}

	if !bytes.Equal(readSegment.Payload, frameData) {
		t.Error("Segment payload mismatch")
	}
}

// TestAuthenticationFlow tests the complete authentication flow
func TestAuthenticationFlow(t *testing.T) {
	// Test credential parsing
	username := "testuser"
	password := "testpass"

	// Build auth response
	frame := BuildAuthResponseFrame(username, password)

	// Verify body structure
	if len(frame.Body) < 4 {
		t.Fatal("Body too short")
	}

	// Parse credentials
	credentials, _, err := readBytes(frame.Body)
	if err != nil {
		t.Fatalf("readBytes failed: %v", err)
	}

	// Skip first byte
	credentials = credentials[1:]

	// Find null terminator
	nullIndex := -1
	for i, b := range credentials {
		if b == 0 {
			nullIndex = i
			break
		}
	}

	if nullIndex == -1 {
		t.Fatal("No null terminator found")
	}

	parsedUsername := string(credentials[0:nullIndex])
	parsedPassword := string(credentials[nullIndex+1:])

	if parsedUsername != username {
		t.Errorf("Username mismatch: got %s, want %s", parsedUsername, username)
	}
	if parsedPassword != password {
		t.Errorf("Password mismatch: got %s, want %s", parsedPassword, password)
	}
}

// TestAuthSuccessFrameFormat tests AUTH_SUCCESS frame format
func TestAuthSuccessFrameFormat(t *testing.T) {
	// AUTH_SUCCESS body should be [int length][bytes]
	// For empty data: [0x00, 0x00, 0x00, 0x00]

	body := writeBytes([]byte{})

	if len(body) != 4 {
		t.Errorf("Body length should be 4, got %d", len(body))
	}

	// Verify it's all zeros
	for _, b := range body {
		if b != 0 {
			t.Errorf("Body should be all zeros, got %x", body)
		}
	}

	// Verify parsing
	length := int32(binary.BigEndian.Uint32(body[0:4]))
	if length != 0 {
		t.Errorf("Parsed length should be 0, got %d", length)
	}
}

// TestReadyFrameFormat tests READY frame format
func TestReadyFrameFormat(t *testing.T) {
	// READY should have no body (length = 0, body = nil)
	frame := &Frame{
		Version: ProtoVersion4,
		Flags:   0,
		Stream:  0,
		Opcode:  OpcodeReady,
		Length:  0,
		Body:    nil,
	}

	if frame.Length != 0 {
		t.Errorf("READY length should be 0, got %d", frame.Length)
	}

	if frame.Body != nil {
		t.Errorf("READY body should be nil, got %v", frame.Body)
	}
}

// TestPreparedResultBytes tests the exact bytes produced for PREPARED result
// This helps debug the "unknown type release_version" error
func TestPreparedResultBytes(t *testing.T) {
	// Create a handler to access the internal methods
	h := &MessageHandler{}

	// Create a prepared statement like what handlePrepare would create
	// For SELECT release_version FROM system.local, there are no bind markers
	prepared := &executor.PreparedStatement{
		ID:         "test_stmt",
		ParamCount: 0,
		Parameters: []executor.ColumnInfo{}, // No bind markers
		Columns: []executor.ColumnInfo{
			{Keyspace: "system", Table: "local", Name: "release_version", Type: "text"},
		},
	}

	// Test protocol v4
	result := h.createPreparedResult(prepared, ProtoVersion4)

	t.Logf("Protocol v4 PREPARED result bytes (hex): %x", result)
	t.Logf("Total length: %d bytes", len(result))

	// Parse and verify the structure
	pos := 0

	// 1. Result kind (4 bytes)
	kind := binary.BigEndian.Uint32(result[pos : pos+4])
	pos += 4
	if kind != 0x0004 {
		t.Errorf("Expected kind 0x0004 (PREPARED), got 0x%08x", kind)
	}
	t.Logf("  [%d-%d] Kind: 0x%08x (PREPARED)", pos-4, pos-1, kind)

	// 2. Prepared ID (short_bytes)
	idLen := binary.BigEndian.Uint16(result[pos : pos+2])
	pos += 2
	stmtID := string(result[pos : pos+int(idLen)])
	pos += int(idLen)
	t.Logf("  [%d-%d] ID length: %d, ID: '%s'", pos-2-int(idLen), pos-1, idLen, stmtID)

	// 3. Prepared metadata flags (4 bytes)
	prepFlags := binary.BigEndian.Uint32(result[pos : pos+4])
	pos += 4
	t.Logf("  [%d-%d] Prepared flags: %d", pos-4, pos-1, prepFlags)

	// 4. Prepared metadata column count (4 bytes)
	prepColCount := binary.BigEndian.Uint32(result[pos : pos+4])
	pos += 4
	t.Logf("  [%d-%d] Prepared column count: %d", pos-4, pos-1, prepColCount)

	// 5. Prepared metadata pk_count (v4+, 4 bytes)
	// pk_count indicates which bind markers correspond to partition key columns
	prepPkCount := binary.BigEndian.Uint32(result[pos : pos+4])
	pos += 4
	t.Logf("  [%d-%d] Prepared pk_count: %d", pos-4, pos-1, prepPkCount)

	// 6. Prepared metadata pk_indices (if pk_count > 0)
	// For this test, pk_count should be 0, so no indices follow
	if prepPkCount > 0 {
		pos += int(prepPkCount) * 2 // Each pk_index is a short (2 bytes)
	}

	// 7. Prepared metadata column specs (parameters/bind markers)
	// For SELECT release_version FROM system.local, there are no bind markers
	for i := uint32(0); i < prepColCount; i++ {
		// This loop should not execute since prepColCount should be 0
		// If it does, skip the specs
		ksLen := binary.BigEndian.Uint16(result[pos : pos+2])
		pos += 2 + int(ksLen)
		tbLen := binary.BigEndian.Uint16(result[pos : pos+2])
		pos += 2 + int(tbLen)
		colLen := binary.BigEndian.Uint16(result[pos : pos+2])
		pos += 2 + int(colLen)
		pos += 2 // type code
	}

	// 8. Result metadata flags (4 bytes)
	resFlags := binary.BigEndian.Uint32(result[pos : pos+4])
	pos += 4
	t.Logf("  [%d-%d] Result flags: %d", pos-4, pos-1, resFlags)

	// 9. Result metadata column count (4 bytes)
	resColCount := binary.BigEndian.Uint32(result[pos : pos+4])
	pos += 4
	t.Logf("  [%d-%d] Result column count: %d", pos-4, pos-1, resColCount)

	// 10. Result metadata column specs
	// Note: result metadata does NOT have pkey_count (unlike prepared metadata)
	for i := uint32(0); i < resColCount; i++ {
		// Keyspace
		ksLen := binary.BigEndian.Uint16(result[pos : pos+2])
		pos += 2
		keyspace := string(result[pos : pos+int(ksLen)])
		pos += int(ksLen)

		// Table
		tbLen := binary.BigEndian.Uint16(result[pos : pos+2])
		pos += 2
		table := string(result[pos : pos+int(tbLen)])
		pos += int(tbLen)

		// Column name
		colLen := binary.BigEndian.Uint16(result[pos : pos+2])
		pos += 2
		colName := string(result[pos : pos+int(colLen)])
		pos += int(colLen)

		// Type code
		typeCode := binary.BigEndian.Uint16(result[pos : pos+2])
		pos += 2
		t.Logf("  [%d-%d] Result col %d: keyspace='%s' table='%s' name='%s', type=0x%04x", pos-2, pos-1, i, keyspace, table, colName, typeCode)

		// Verify against prepared.Columns
		if i < uint32(len(prepared.Columns)) {
			if keyspace != prepared.Columns[i].Keyspace {
				t.Errorf("Result col %d keyspace mismatch: expected '%s', got '%s'", i, prepared.Columns[i].Keyspace, keyspace)
			}
			if table != prepared.Columns[i].Table {
				t.Errorf("Result col %d table mismatch: expected '%s', got '%s'", i, prepared.Columns[i].Table, table)
			}
			if colName != prepared.Columns[i].Name {
				t.Errorf("Result col %d name mismatch: expected '%s', got '%s'", i, prepared.Columns[i].Name, colName)
			}
		}
	}

	t.Logf("  Total bytes parsed: %d", pos)
	t.Logf("  Remaining bytes: %d", len(result)-pos)
}

// TestFullFrameWithPrepared tests the complete frame including header
// This simulates exactly what the client receives
func TestFullFrameWithPrepared(t *testing.T) {
	// Create a handler
	h := &MessageHandler{}

	// Create a prepared statement
	// For SELECT release_version FROM system.local, there are no bind markers
	prepared := &executor.PreparedStatement{
		ID:         "test_stmt",
		ParamCount: 0,
		Parameters: []executor.ColumnInfo{}, // No bind markers
		Columns: []executor.ColumnInfo{
			{Keyspace: "system", Table: "local", Name: "release_version", Type: "text"},
		},
	}

	// Create the result body
	body := h.createPreparedResult(prepared, ProtoVersion4)

	// Create a complete frame
	frame := &Frame{
		Version: ProtoVersion4 | 0x80, // Response version
		Flags:   0,
		Stream:  0,
		Opcode:  OpcodeResult,
		Length:  int32(len(body)),
		Body:    body,
	}

	// Serialize the frame
	var buf bytes.Buffer
	if err := frame.WriteFrame(&buf); err != nil {
		t.Fatalf("Failed to write frame: %v", err)
	}
	frameBytes := buf.Bytes()

	t.Logf("Complete frame bytes (hex): %x", frameBytes)
	t.Logf("Frame length: %d bytes", len(frameBytes))
	t.Logf("Header: %x", frameBytes[0:9])
	t.Logf("Body: %x", frameBytes[9:])

	// Now parse it back to verify
	reader := NewFrameReader(&buf, nil, 4)
	parsedFrame, err := reader.ReadFrame()
	if err != nil {
		t.Fatalf("Failed to read frame: %v", err)
	}

	if parsedFrame.Opcode != OpcodeResult {
		t.Errorf("Opcode mismatch: got %d, want %d", parsedFrame.Opcode, OpcodeResult)
	}

	// Parse the body to verify it's correct
	bodyParsed := parsedFrame.Body
	pos := 0

	// Result kind
	kind := binary.BigEndian.Uint32(bodyParsed[pos : pos+4])
	pos += 4
	if kind != 0x0004 {
		t.Errorf("Kind mismatch: got 0x%08x, want 0x0004", kind)
	}

	// Prepared ID
	idLen := binary.BigEndian.Uint16(bodyParsed[pos : pos+2])
	pos += 2
	stmtID := string(bodyParsed[pos : pos+int(idLen)])
	pos += int(idLen)
	if stmtID != "test_stmt" {
		t.Errorf("ID mismatch: got %s, want test_stmt", stmtID)
	}

	// Prepared flags
	prepFlags := binary.BigEndian.Uint32(bodyParsed[pos : pos+4])
	pos += 4
	if prepFlags != 0 {
		t.Errorf("Prepared flags mismatch: got %d, want 0", prepFlags)
	}

	// Prepared column count (should be 0 - no bind markers)
	prepColCount := binary.BigEndian.Uint32(bodyParsed[pos : pos+4])
	pos += 4
	if prepColCount != 0 {
		t.Errorf("Prepared column count mismatch: got %d, want 0 (no bind markers)", prepColCount)
	}

	// Prepared pkey_count
	prepPkeyCount := binary.BigEndian.Uint32(bodyParsed[pos : pos+4])
	pos += 4
	if prepPkeyCount != 0 {
		t.Errorf("Prepared pkey_count mismatch: got %d, want 0", prepPkeyCount)
	}

	// Result metadata flags
	resFlags := binary.BigEndian.Uint32(bodyParsed[pos : pos+4])
	pos += 4
	if resFlags != 0 {
		t.Errorf("Result flags mismatch: got %d, want 0", resFlags)
	}

	// Result column count (should be 1 - release_version)
	resColCount := binary.BigEndian.Uint32(bodyParsed[pos : pos+4])
	pos += 4
	if resColCount != 1 {
		t.Errorf("Result column count mismatch: got %d, want 1", resColCount)
	}

	// Result column spec
	// Note: result metadata does NOT have pkey_count (unlike prepared metadata)
	// Keyspace
	ksLen := binary.BigEndian.Uint16(bodyParsed[pos : pos+2])
	pos += 2
	keyspace := string(bodyParsed[pos : pos+int(ksLen)])
	pos += int(ksLen)
	if keyspace != "system" {
		t.Errorf("Keyspace mismatch: got '%s', want 'system'", keyspace)
	}

	// Table
	tbLen := binary.BigEndian.Uint16(bodyParsed[pos : pos+2])
	pos += 2
	table := string(bodyParsed[pos : pos+int(tbLen)])
	pos += int(tbLen)
	if table != "local" {
		t.Errorf("Table mismatch: got '%s', want 'local'", table)
	}

	// Column name
	colLen := binary.BigEndian.Uint16(bodyParsed[pos : pos+2])
	pos += 2
	colName := string(bodyParsed[pos : pos+int(colLen)])
	pos += int(colLen)
	if colName != "release_version" {
		t.Errorf("Column name mismatch: got '%s', want 'release_version'", colName)
	}

	// Type code
	typeCode := binary.BigEndian.Uint16(bodyParsed[pos : pos+2])
	pos += 2
	if typeCode != 0x000D {
		t.Errorf("Type code mismatch: got 0x%04x, want 0x000D (varchar)", typeCode)
	}

	t.Logf("Successfully verified all fields up to position %d", pos)
}

// TestStartupWithAuth tests that STARTUP returns AUTHENTICATE when authenticator requires auth
func TestStartupWithAuth(t *testing.T) {
	// Create a password authenticator (requires auth)
	authenticator := NewPasswordAuthenticator()

	// Create a no-op logger for testing
	logger := zap.NewNop()

	// Create handler with authenticator
	handler := &MessageHandler{
		logger:        logger,
		handlers:      make(map[byte]MessageHandlerFunc),
		authenticator: authenticator,
	}

	// Build STARTUP frame
	startupFrame := &Frame{
		Version: ProtoVersion4,
		Flags:   0,
		Stream:  1,
		Opcode:  OpcodeStartup,
		Length:  0,
		Body:    []byte{0x00, 0x00}, // Empty map
	}

	// Handle STARTUP directly
	response, err := handler.handleStartup(context.Background(), startupFrame)
	if err != nil {
		t.Fatalf("handleStartup failed: %v", err)
	}

	// Should get AUTHENTICATE response
	if response.Opcode != OpcodeAuthenticate {
		t.Errorf("Expected AUTHENTICATE (0x03), got 0x%02x", response.Opcode)
	}

	t.Logf("SUCCESS: STARTUP with PasswordAuthenticator returns AUTHENTICATE")
}

// TestStartupWithoutAuth tests that STARTUP returns READY when no authenticator
func TestStartupWithoutAuth(t *testing.T) {
	// Create a no-op logger for testing
	logger := zap.NewNop()

	// Create handler with AllowAllAuthenticator
	handler := &MessageHandler{
		logger:        logger,
		handlers:      make(map[byte]MessageHandlerFunc),
		authenticator: &AllowAllAuthenticator{},
	}

	// Build STARTUP frame
	startupFrame := &Frame{
		Version: ProtoVersion4,
		Flags:   0,
		Stream:  1,
		Opcode:  OpcodeStartup,
		Length:  0,
		Body:    []byte{0x00, 0x00}, // Empty map
	}

	// Handle STARTUP directly
	response, err := handler.handleStartup(context.Background(), startupFrame)
	if err != nil {
		t.Fatalf("handleStartup failed: %v", err)
	}

	// Should get READY response
	if response.Opcode != OpcodeReady {
		t.Errorf("Expected READY (0x02), got 0x%02x", response.Opcode)
	}

	t.Logf("SUCCESS: STARTUP with AllowAllAuthenticator returns READY")
}
