package checkpoint

import (
	"context"
	"testing"
)

func TestMemorySaver(t *testing.T) {
	ctx := context.Background()
	saver := NewMemorySaver()

	threadID := "test-thread-1"

	// Save a checkpoint
	checkpoint := map[string]interface{}{
		"messages": []string{"hello", "world"},
		"counter":  42,
	}

	config := map[string]interface{}{
		"thread_id": threadID,
	}

	err := saver.Put(ctx, config, checkpoint)
	if err != nil {
		t.Fatalf("Failed to save checkpoint: %v", err)
	}

	// Retrieve the checkpoint
	retrieved, err := saver.Get(ctx, config)
	if err != nil {
		t.Fatalf("Failed to get checkpoint: %v", err)
	}

	if retrieved == nil {
		t.Fatal("Expected non-nil checkpoint")
	}

	// Verify values
	msgs, ok := retrieved["messages"].([]interface{})
	if !ok || len(msgs) != 2 {
		t.Errorf("Expected 2 messages, got %v", msgs)
	}

	counter, ok := retrieved["counter"].(float64) // JSON unmarshals numbers as float64
	if !ok || counter != 42 {
		t.Errorf("Expected counter=42, got %v", retrieved["counter"])
	}
}

func TestMemorySaverMultipleVersions(t *testing.T) {
	ctx := context.Background()
	saver := NewMemorySaver()

	threadID := "test-thread-2"

	// Save multiple checkpoints
	for i := 0; i < 3; i++ {
		checkpoint := map[string]interface{}{
			"step": i,
		}
		config := map[string]interface{}{
			"thread_id": threadID,
		}
		err := saver.Put(ctx, config, checkpoint)
		if err != nil {
			t.Fatalf("Failed to save checkpoint %d: %v", i, err)
		}
	}

	// List checkpoints
	config := map[string]interface{}{
		"thread_id": threadID,
	}
	checkpoints, err := saver.List(ctx, config, 10)
	if err != nil {
		t.Fatalf("Failed to list checkpoints: %v", err)
	}

	if len(checkpoints) != 3 {
		t.Errorf("Expected 3 checkpoints, got %d", len(checkpoints))
	}

	// Get should return latest
	latest, err := saver.Get(ctx, config)
	if err != nil {
		t.Fatalf("Failed to get latest checkpoint: %v", err)
	}

	step := latest["step"].(float64)
	if step != 2 {
		t.Errorf("Expected latest step=2, got %v", step)
	}
}

func TestMemorySaverMultipleThreads(t *testing.T) {
	ctx := context.Background()
	saver := NewMemorySaver()

	// Save checkpoints for different threads
	threads := []string{"thread-a", "thread-b", "thread-c"}
	for i, threadID := range threads {
		checkpoint := map[string]interface{}{
			"thread_index": i,
		}
		config := map[string]interface{}{
			"thread_id": threadID,
		}
		err := saver.Put(ctx, config, checkpoint)
		if err != nil {
			t.Fatalf("Failed to save checkpoint for %s: %v", threadID, err)
		}
	}

	// Retrieve each thread's checkpoint
	for i, threadID := range threads {
		config := map[string]interface{}{
			"thread_id": threadID,
		}
		checkpoint, err := saver.Get(ctx, config)
		if err != nil {
			t.Fatalf("Failed to get checkpoint for %s: %v", threadID, err)
		}

		index := checkpoint["thread_index"].(float64)
		if int(index) != i {
			t.Errorf("For thread %s, expected index %d, got %v", threadID, i, index)
		}
	}
}

func TestDeepCopy(t *testing.T) {
	original := map[string]interface{}{
		"messages": []string{"hello", "world"},
		"nested": map[string]interface{}{
			"key": "value",
		},
	}

	copied := deepCopy(original)

	// Modify original
	original["messages"] = []string{"modified"}
	original["nested"].(map[string]interface{})["key"] = "modified"

	// Copy should be unchanged
	copiedMap := copied.(map[string]interface{})
	msgs := copiedMap["messages"].([]interface{})
	if len(msgs) != 2 || msgs[0] != "hello" {
		t.Error("Deep copy did not preserve original messages")
	}

	nested := copiedMap["nested"].(map[string]interface{})
	if nested["key"] != "value" {
		t.Error("Deep copy did not preserve nested value")
	}
}

func TestDeepCopySlice(t *testing.T) {
	original := []interface{}{"a", "b", "c"}
	copied := deepCopySlice(original)

	// Modify original
	original[0] = "modified"

	// Copy should be unchanged
	if copied[0] != "a" {
		t.Error("Deep copy slice did not preserve original")
	}
}

func TestMemorySaverWithMetadata(t *testing.T) {
	ctx := context.Background()
	saver := NewMemorySaver()

	threadID := "test-thread-meta"
	checkpoint := map[string]interface{}{
		"data": "value",
	}
	config := map[string]interface{}{
		"thread_id":  threadID,
		"metadata_1": "value_1",
		"metadata_2": 42,
	}

	err := saver.Put(ctx, config, checkpoint)
	if err != nil {
		t.Fatalf("Failed to save checkpoint: %v", err)
	}

	// List and verify metadata
	listConfig := map[string]interface{}{
		"thread_id": threadID,
	}
	checkpoints, err := saver.List(ctx, listConfig, 1)
	if err != nil {
		t.Fatalf("Failed to list checkpoints: %v", err)
	}

	if len(checkpoints) != 1 {
		t.Fatalf("Expected 1 checkpoint, got %d", len(checkpoints))
	}

	metadata := checkpoints[0]["metadata"].(map[string]interface{})
	if metadata["metadata_1"] != "value_1" {
		t.Error("Metadata not preserved correctly")
	}
}

func TestMemorySaverCheckpointID(t *testing.T) {
	ctx := context.Background()
	saver := NewMemorySaver()

	threadID := "test-thread-id"
	checkpointID := "custom-checkpoint-id"

	checkpoint := map[string]interface{}{
		"step": 1,
	}
	config := map[string]interface{}{
		"thread_id":     threadID,
		"checkpoint_id": checkpointID,
	}

	err := saver.Put(ctx, config, checkpoint)
	if err != nil {
		t.Fatalf("Failed to save checkpoint: %v", err)
	}

	// Retrieve by specific checkpoint ID
	getConfig := map[string]interface{}{
		"thread_id":     threadID,
		"checkpoint_id": checkpointID,
	}
	retrieved, err := saver.Get(ctx, getConfig)
	if err != nil {
		t.Fatalf("Failed to get checkpoint by ID: %v", err)
	}

	if retrieved == nil {
		t.Fatal("Expected non-nil checkpoint")
	}

	step := retrieved["step"].(float64)
	if step != 1 {
		t.Errorf("Expected step=1, got %v", step)
	}
}
