package executor

import (
	"bytes"
	"crypto/sha256"
	"encoding/binary"
	"testing"
)

func TestPagingStateRoundTrip(t *testing.T) {
	remaining := int64(12)
	state := pagingState{
		QueryHash:      sha256.Sum256([]byte("select * from ks.tbl|ks")),
		SchemaVersion:  "schema-1",
		Table:          "ks.tbl",
		OrderBy:        "id asc",
		LastPKValues:   []byte{0x01, 0x02, 0x03},
		RemainingLimit: &remaining,
	}

	encoded, err := encodePagingState(state)
	if err != nil {
		t.Fatalf("encodePagingState failed: %v", err)
	}

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

	if decoded.QueryHash != state.QueryHash {
		t.Errorf("query hash mismatch")
	}
	if decoded.SchemaVersion != state.SchemaVersion {
		t.Errorf("schema version = %q, want %q", decoded.SchemaVersion, state.SchemaVersion)
	}
	if decoded.Table != state.Table {
		t.Errorf("table = %q, want %q", decoded.Table, state.Table)
	}
	if decoded.OrderBy != state.OrderBy {
		t.Errorf("order by = %q, want %q", decoded.OrderBy, state.OrderBy)
	}
	if !bytes.Equal(decoded.LastPKValues, state.LastPKValues) {
		t.Errorf("last pk values mismatch")
	}
	if decoded.RemainingLimit == nil || *decoded.RemainingLimit != remaining {
		t.Errorf("remaining limit = %v, want %d", decoded.RemainingLimit, remaining)
	}
}

func TestPagingValuesRoundTrip(t *testing.T) {
	values := []interface{}{int32(42), "alpha"}
	types := []string{"int", "text"}

	encoded, err := encodePagingValues(values, types)
	if err != nil {
		t.Fatalf("encodePagingValues failed: %v", err)
	}

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

	if len(decoded) != len(values) {
		t.Fatalf("decoded len = %d, want %d", len(decoded), len(values))
	}
	if v, ok := decoded[0].(int32); !ok || v != 42 {
		t.Errorf("decoded[0] = %v, want int32(42)", decoded[0])
	}
	if v, ok := decoded[1].(string); !ok || v != "alpha" {
		t.Errorf("decoded[1] = %v, want \"alpha\"", decoded[1])
	}
}

func TestRowsResultHasMorePages(t *testing.T) {
	translator := NewQueryResultTranslator()
	result := &QueryResult{
		Columns: []ColumnInfo{
			{Keyspace: "ks", Table: "tbl", Name: "id", Type: "int"},
		},
		Rows: []Row{
			{Values: []interface{}{int32(1)}},
		},
		PagingState:  []byte{0x10, 0x11},
		HasMorePages: true,
	}

	frame, err := translator.TranslateToResultFrame(result, 0)
	if err != nil {
		t.Fatalf("TranslateToResultFrame failed: %v", err)
	}

	body := frame.Body
	pos := 0
	kind := binary.BigEndian.Uint32(body[pos : pos+4])
	pos += 4
	if kind != uint32(ResultKindRows) {
		t.Fatalf("result kind = 0x%04x, want 0x%04x", kind, ResultKindRows)
	}

	flags := binary.BigEndian.Uint32(body[pos : pos+4])
	pos += 4
	if flags&resultMetadataHasMore == 0 {
		t.Fatalf("expected has-more-pages flag set")
	}

	colCount := binary.BigEndian.Uint32(body[pos : pos+4])
	pos += 4
	if colCount != 1 {
		t.Fatalf("column count = %d, want 1", colCount)
	}

	if flags&resultMetadataHasMore != 0 {
		length := int32(binary.BigEndian.Uint32(body[pos : pos+4]))
		pos += 4
		if length != int32(len(result.PagingState)) {
			t.Fatalf("paging state length = %d, want %d", length, len(result.PagingState))
		}
		if !bytes.Equal(body[pos:pos+int(length)], result.PagingState) {
			t.Fatalf("paging state mismatch")
		}
	}
}
