package wal

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

func TestEntryRoundtrip(t *testing.T) {
	maxKey := bytes.Repeat([]byte("k"), int(MaxWalKeyBytes))

	cases := []struct {
		name string
		e    *WalEntry
	}{
		{"put_inline", &WalEntry{OpPut, VKInline, []byte("key1"), []byte("val1")}},
		{"put_inline_empty_value", &WalEntry{OpPut, VKInline, []byte("key2"), []byte{}}},
		{"delete", &WalEntry{OpDelete, VKNone, []byte("key3"), nil}},
		{"put_max_key", &WalEntry{OpPut, VKInline, maxKey, []byte("v")}},
	}

	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			encoded, err := EncodeEntry(tc.e)
			if err != nil {
				t.Fatalf("encode: %v", err)
			}

			got, consumed, err := DecodeEntry(encoded)
			if err != nil {
				t.Fatalf("decode: %v", err)
			}
			if consumed != len(encoded) {
				t.Fatalf("consumed %d != encoded len %d", consumed, len(encoded))
			}

			if got.OpType != tc.e.OpType {
				t.Errorf("OpType: got %d, want %d", got.OpType, tc.e.OpType)
			}
			if got.ValueKind != tc.e.ValueKind {
				t.Errorf("ValueKind: got %d, want %d", got.ValueKind, tc.e.ValueKind)
			}
			if !bytes.Equal(got.Key, tc.e.Key) {
				t.Errorf("Key: got %q, want %q", got.Key, tc.e.Key)
			}
			if !bytes.Equal(got.Value, tc.e.Value) {
				t.Errorf("Value: got %q, want %q", got.Value, tc.e.Value)
			}
		})
	}
}

func TestEntryValidation(t *testing.T) {
	bigKey := bytes.Repeat([]byte("k"), int(MaxWalKeyBytes)+1)
	bigVal := bytes.Repeat([]byte("v"), int(MaxWalInlineValueBytes)+1)

	cases := []struct {
		name    string
		e       *WalEntry
		wantErr bool
	}{
		{"op_invalid", &WalEntry{OpInvalid, VKNone, []byte("k"), nil}, true},
		{"put_vk_none", &WalEntry{OpPut, VKNone, []byte("k"), nil}, true},
		{"delete_vk_inline", &WalEntry{OpDelete, VKInline, []byte("k"), nil}, true},
		{"key_empty", &WalEntry{OpPut, VKInline, []byte{}, []byte("v")}, true},
		{"key_too_big", &WalEntry{OpPut, VKInline, bigKey, []byte("v")}, true},
		{"put_inline_val_too_big", &WalEntry{OpPut, VKInline, []byte("k"), bigVal}, true},
		{"put_inline_val_empty", &WalEntry{OpPut, VKInline, []byte("k"), []byte{}}, false},
	}

	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			_, err := EncodeEntry(tc.e)
			if (err != nil) != tc.wantErr {
				t.Errorf("EncodeEntry() error = %v, wantErr %v", err, tc.wantErr)
			}
		})
	}
}

func TestEntryDecodeTruncated(t *testing.T) {
	// Build a valid encoded entry, then truncate mid-varint.
	e := &WalEntry{OpPut, VKInline, []byte("key1"), []byte("value1")}
	full, err := EncodeEntry(e)
	if err != nil {
		t.Fatal(err)
	}

	// Truncate to just 1 byte — not enough for header.
	_, _, err = DecodeEntry(full[:1])
	if err == nil {
		t.Error("expected error for 1-byte data")
	}

	// Build data with an incomplete varint: opType + valueKind + start of varint (0xFF means more bytes follow).
	trunc := []byte{OpPut, VKInline, 0xFF}
	_, _, err = DecodeEntry(trunc)
	if err == nil {
		t.Error("expected error for truncated varint")
	}

	// Also test: varint specifies more bytes than available.
	// Encode a large key length varint but don't provide the key bytes.
	varintBuf := make([]byte, binary.MaxVarintLen64)
	n := binary.PutUvarint(varintBuf, 1000) // keyLen = 1000
	data := []byte{OpPut, VKInline}
	data = append(data, varintBuf[:n]...)
	data = append(data, varintBuf[:n]...) // valLen varint (also 1000)
	// Don't append any key/value bytes.
	_, _, err = DecodeEntry(data)
	if err == nil {
		t.Error("expected error for missing key/value bytes")
	}
}
