package agentpod

import (
	"fmt"
	"strings"
	"sync"
	"testing"
)

func TestMemoryBlock_AddString(t *testing.T) {
	mb := NewMemoryBlock()

	// Test adding string value
	mb.AddString("key1", "value1")

	// Verify the item was added correctly
	if !mb.Exists("key1") {
		t.Error("Key 'key1' doesn't exist after adding")
	}

	// Verify the value type is correct
	value := mb.Items["key1"]
	if !value.IsString() {
		t.Error("Value should be a string but isn't")
	}

	// Verify the value content
	if value.AsString() != "value1" {
		t.Errorf("Expected string value 'value1', got '%s'", value.AsString())
	}
}

func TestMemoryBlock_AddBlock(t *testing.T) {
	mb := NewMemoryBlock()
	nested := NewMemoryBlock()

	// Test adding memory block value
	mb.AddBlock("nested", nested)

	// Verify the item was added correctly
	if !mb.Exists("nested") {
		t.Error("Key 'nested' doesn't exist after adding")
	}

	// Verify the value type is correct
	value := mb.Items["nested"]
	if !value.IsBlock() {
		t.Error("Value should be a MemoryBlock but isn't")
	}

	// Verify the value content
	if value.AsBlock() != nested {
		t.Error("Block reference doesn't match the original")
	}
}

func TestMemoryBlock_Delete(t *testing.T) {
	mb := NewMemoryBlock()
	mb.AddString("key1", "value1")

	// Test deleting existing key
	deleted := mb.Delete("key1")
	if !deleted {
		t.Error("Delete reported false when key should exist")
	}

	// Test deleting non-existing key
	deleted = mb.Delete("nonexistent")
	if deleted {
		t.Error("Delete reported true when key shouldn't exist")
	}
}

func TestMemoryBlock_Exists(t *testing.T) {
	mb := NewMemoryBlock()
	mb.AddString("key1", "value1")

	// Test existing key
	exists := mb.Exists("key1")
	if !exists {
		t.Error("Exists reported false when key should exist")
	}

	// Test non-existing key
	exists = mb.Exists("nonexistent")
	if exists {
		t.Error("Exists reported true when key shouldn't exist")
	}
}

func TestMemoryBlock_Parse(t *testing.T) {
	// Create a complex memory block structure for testing
	root := NewMemoryBlock()
	root.AddString("key1", "value1")
	root.AddString("key2", "value2")

	nested1 := NewMemoryBlock()
	nested1.AddString("nestedKey1", "nestedValue1")
	nested1.AddString("nestedKey2", "nestedValue2")

	deeplyNested := NewMemoryBlock()
	deeplyNested.AddString("deepKey", "deepValue")
	nested1.AddBlock("deep", deeplyNested)

	root.AddBlock("nested", nested1)

	// Generate parsed string
	parsed := root.Parse()

	// Verify structure with expected output
	expected := `<Memory>
  key1: value1
  key2: value2
  <nested>
    nestedKey1: nestedValue1
    nestedKey2: nestedValue2
    <deep>
      deepKey: deepValue
    </deep>
  </nested>
</Memory>
`

	// Normalize line endings for comparison
	parsed = strings.ReplaceAll(parsed, "\r\n", "\n")
	expected = strings.ReplaceAll(expected, "\r\n", "\n")

	if parsed != expected {
		t.Errorf("Parse output doesn't match expected.\nGot:\n%s\nExpected:\n%s", parsed, expected)
	}
}

func TestMemoryBlock_ConcurrentAccess(t *testing.T) {
	mb := NewMemoryBlock()
	var wg sync.WaitGroup
	const numGoroutines = 100

	// Concurrent writes
	for i := 0; i < numGoroutines; i++ {
		wg.Add(1)
		go func(i int) {
			defer wg.Done()
			key := fmt.Sprintf("key-%d", i)
			value := fmt.Sprintf("value-%d", i)
			if i%2 == 0 {
				mb.AddString(key, value)
			} else {
				block := NewMemoryBlock()
				block.AddString("innerKey", "innerValue")
				mb.AddBlock(key, block)
			}
		}(i)
	}

	wg.Wait() // Allow writes to complete before concurrent reads

	// Concurrent reads
	for i := 0; i < numGoroutines; i++ {
		wg.Add(1)
		go func(i int) {
			defer wg.Done()
			_ = mb.Parse() // We just want to ensure this doesn't race
			mb.Exists(fmt.Sprintf("key-%d", i))
		}(i)
	}

	wg.Wait()

	// Final state check - we expect 100 items to have been added.
	mb.mu.RLock()
	defer mb.mu.RUnlock()
	if len(mb.keys) != numGoroutines {
		t.Errorf("Expected %d items in memory block after concurrent access, but got %d", numGoroutines, len(mb.keys))
	}
}
