package kv

import (
	"context"
	"io"
	"strings"
	"testing"

	badger "github.com/dgraph-io/badger/v4"

	"github.com/stretchr/testify/require"
)

func setupTestBadgerDB(t *testing.T) *badger.DB {
	// Create a temporary directory for the test database
	opts := badger.DefaultOptions("").WithInMemory(true).WithLogger(nil)
	db, err := badger.Open(opts)
	require.NoError(t, err)
	t.Cleanup(func() {
		err := db.Close()
		require.NoError(t, err)
	})
	return db
}

func setupTestKV(t *testing.T) KV {
	db := setupTestBadgerDB(t)
	return NewBadgerKV(db)
}

func TestPrefixRangeEnd(t *testing.T) {
	require.Equal(t, "b", PrefixRangeEnd("a"))
	require.Equal(t, "a/c", PrefixRangeEnd("a/b"))
	require.Equal(t, "a/b/d", PrefixRangeEnd("a/b/c"))
	require.Equal(t, "", PrefixRangeEnd(""))
}

func TestBadgerKVSmoke(t *testing.T) {
	// Simple smoke test to ensure the basic badger KV implementation works
	kv := setupTestKV(t)
	ctx := context.Background()

	// Test unix timestamp works
	timestamp, err := kv.UnixTimestamp(ctx)
	require.NoError(t, err)
	require.Greater(t, timestamp, int64(0))

	// Test get non-existent key returns proper error
	_, err = kv.Get(ctx, "test-section", "non-existent")
	require.Error(t, err)
	require.Equal(t, ErrNotFound, err)
}

func TestBadgerKV_UnderlyingStorage(t *testing.T) {
	// Test internal key storage format and structure
	db := setupTestBadgerDB(t)
	kv := NewBadgerKV(db)
	ctx := context.Background()

	t.Run("keys are stored with section prefix", func(t *testing.T) {
		section := "test-section"
		key := "test-key"
		value := "test-value"
		expectedInternalKey := section + "/" + key

		// Save through KV interface
		saveKVHelper(t, kv, ctx, section, key, strings.NewReader(value))

		// Verify the raw key exists in badger with correct format
		err := db.View(func(txn *badger.Txn) error {
			item, err := txn.Get([]byte(expectedInternalKey))
			require.NoError(t, err)

			// Verify the value is correct
			valueBytes, err := item.ValueCopy(nil)
			require.NoError(t, err)
			require.Equal(t, value, string(valueBytes))

			return nil
		})
		require.NoError(t, err)
	})

	t.Run("sections are properly isolated", func(t *testing.T) {
		section1 := "section1"
		section2 := "section2"
		key := "same-key"
		value1 := "value-from-section1"
		value2 := "value-from-section2"

		// Save same key in different sections
		saveKVHelper(t, kv, ctx, section1, key, strings.NewReader(value1))
		saveKVHelper(t, kv, ctx, section2, key, strings.NewReader(value2))

		// Verify both keys exist in badger with different internal keys
		err := db.View(func(txn *badger.Txn) error {
			// Check section1 key
			item1, err := txn.Get([]byte(section1 + "/" + key))
			require.NoError(t, err)
			value1Bytes, err := item1.ValueCopy(nil)
			require.NoError(t, err)
			require.Equal(t, value1, string(value1Bytes))

			// Check section2 key
			item2, err := txn.Get([]byte(section2 + "/" + key))
			require.NoError(t, err)
			value2Bytes, err := item2.ValueCopy(nil)
			require.NoError(t, err)
			require.Equal(t, value2, string(value2Bytes))

			return nil
		})
		require.NoError(t, err)

		// Verify KV interface returns correct values for each section
		reader1, err := kv.Get(ctx, section1, key)
		require.NoError(t, err)
		val1, err := io.ReadAll(reader1)
		require.NoError(t, err)
		require.Equal(t, value1, string(val1))
		err = reader1.Close()
		require.NoError(t, err)

		reader2, err := kv.Get(ctx, section2, key)
		require.NoError(t, err)
		val2, err := io.ReadAll(reader2)
		require.NoError(t, err)
		require.Equal(t, value2, string(val2))
		err = reader2.Close()
		require.NoError(t, err)
	})

	t.Run("delete removes correct internal key", func(t *testing.T) {
		section := "delete-section"
		key := "delete-key"
		value := "delete-value"
		internalKey := section + "/" + key

		// Save and verify it exists
		saveKVHelper(t, kv, ctx, section, key, strings.NewReader(value))

		// Verify it exists in badger
		err := db.View(func(txn *badger.Txn) error {
			_, err := txn.Get([]byte(internalKey))
			return err
		})
		require.NoError(t, err)

		// Delete through KV interface
		err = kv.Delete(ctx, section, key)
		require.NoError(t, err)

		// Verify it's gone from badger
		err = db.View(func(txn *badger.Txn) error {
			_, err := txn.Get([]byte(internalKey))
			return err
		})
		require.Error(t, err)
		require.Equal(t, badger.ErrKeyNotFound, err)
	})

	t.Run("keys iteration respects section boundaries", func(t *testing.T) {
		section1 := "alpha"
		section2 := "beta"

		// Add keys to both sections
		keys1 := []string{"a1", "a2", "a3"}
		keys2 := []string{"b1", "b2", "b3"}

		for _, k := range keys1 {
			saveKVHelper(t, kv, ctx, section1, k, strings.NewReader("value"+k))
		}
		for _, k := range keys2 {
			saveKVHelper(t, kv, ctx, section2, k, strings.NewReader("value"+k))
		}

		// List keys from section1 only
		var foundKeys1 []string //nolint:prealloc
		for k, err := range kv.Keys(ctx, section1, ListOptions{}) {
			require.NoError(t, err)
			foundKeys1 = append(foundKeys1, k)
		}
		require.Equal(t, keys1, foundKeys1)

		// List keys from section2 only
		var foundKeys2 []string //nolint:prealloc
		for k, err := range kv.Keys(ctx, section2, ListOptions{}) {
			require.NoError(t, err)
			foundKeys2 = append(foundKeys2, k)
		}
		require.Equal(t, keys2, foundKeys2)

		// Verify raw badger contains all keys with proper prefixes
		var allRawKeys []string
		err := db.View(func(txn *badger.Txn) error {
			opts := badger.DefaultIteratorOptions
			opts.PrefetchValues = false
			iter := txn.NewIterator(opts)
			defer iter.Close()

			for iter.Rewind(); iter.Valid(); iter.Next() {
				item := iter.Item()
				allRawKeys = append(allRawKeys, string(item.Key()))
			}
			return nil
		})
		require.NoError(t, err)

		// Check that all expected internal keys exist
		expectedInternalKeys := []string{
			"alpha/a1", "alpha/a2", "alpha/a3",
			"beta/b1", "beta/b2", "beta/b3",
		}
		for _, expectedKey := range expectedInternalKeys {
			require.Contains(t, allRawKeys, expectedKey, "Expected internal key %s should exist", expectedKey)
		}
	})
}

func TestIsValidKey(t *testing.T) {
	tests := []struct {
		name     string
		key      string
		expected bool
	}{
		// Valid keys
		{"simple key", "a", true},
		{"key with numbers", "a123", true},
		{"key with hyphens", "a-b-c", true},
		{"key with dots", "a.b.c", true},
		{"key with mixed", "a1-b2.c3", true},
		{"composite key with slash", "ns/group", true},
		{"composite key with tilde", "ns~action", true},
		{"complex composite key", "ns/group/resource/name", true},
		{"data key format", "ns/group/resource/name/123~created", true},
		{"metadata key format", "group/resource/ns/name/123~created~folder", true},
		{"metadata key format ending with a ~", "group/resource/ns/name/123~created~", true},
		{"uppercase letters", "Valid", true},
		{"underscores", "a_b", true},
		{"key with underscores and mixed chars", "4_D6mSh4z", true},
		{"complex key with underscores", "ns/group_name/resource-name/Name_123~action-type", true},

		// invalid keys
		{"empty key", "", false},
		{"special characters", "a@b", false},
		{"spaces", "a b", false},
		{"leading space", " key", false},
		{"trailing space", "key ", false},
		{"tab character", "a\tb", false},
		{"newline character", "a\nb", false},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := IsValidKey(tt.key)
			require.Equal(t, tt.expected, result,
				"IsValidKey(%q) = %v, expected %v", tt.key, result, tt.expected)
		})
	}
}

// saveKVHelper is a helper function to save data to KV store using the new WriteCloser interface
func saveKVHelper(t *testing.T, kv KV, ctx context.Context, section, key string, value io.Reader) {
	t.Helper()
	writer, err := kv.Save(ctx, section, key)
	require.NoError(t, err)
	_, err = io.Copy(writer, value)
	require.NoError(t, err)
	err = writer.Close()
	require.NoError(t, err)
}
