package ucxl

import (
	"reflect"
	"testing"
	"time"
)

func TestNewTemporalNavigator(t *testing.T) {
	tests := []struct {
		name          string
		maxVersion    int
		expectedMax   int
		expectedCurrent int
	}{
		{
			name:          "positive max version",
			maxVersion:    10,
			expectedMax:   10,
			expectedCurrent: 10,
		},
		{
			name:          "zero max version",
			maxVersion:    0,
			expectedMax:   0,
			expectedCurrent: 0,
		},
		{
			name:          "negative max version defaults to 0",
			maxVersion:    -5,
			expectedMax:   0,
			expectedCurrent: 0,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			nav := NewTemporalNavigator(tt.maxVersion)
			
			if nav.GetMaxVersion() != tt.expectedMax {
				t.Errorf("GetMaxVersion() = %d, want %d", nav.GetMaxVersion(), tt.expectedMax)
			}
			
			if nav.GetCurrentVersion() != tt.expectedCurrent {
				t.Errorf("GetCurrentVersion() = %d, want %d", nav.GetCurrentVersion(), tt.expectedCurrent)
			}
			
			if nav.GetHistory() == nil {
				t.Error("History should be initialized")
			}
			
			if len(nav.GetHistory()) != 0 {
				t.Error("History should be empty initially")
			}
		})
	}
}

func TestNavigateLatest(t *testing.T) {
	nav := NewTemporalNavigator(10)
	
	// Navigate to version 5 first
	nav.currentVersion = 5
	
	segment := TemporalSegment{Type: TemporalLatest}
	result, err := nav.Navigate(segment)
	
	if err != nil {
		t.Fatalf("Navigate() error = %v, want nil", err)
	}
	
	if !result.Success {
		t.Error("Navigation should be successful")
	}
	
	if result.TargetVersion != 10 {
		t.Errorf("TargetVersion = %d, want 10", result.TargetVersion)
	}
	
	if result.PreviousVersion != 5 {
		t.Errorf("PreviousVersion = %d, want 5", result.PreviousVersion)
	}
	
	if nav.GetCurrentVersion() != 10 {
		t.Errorf("Current version = %d, want 10", nav.GetCurrentVersion())
	}
}

func TestNavigateAny(t *testing.T) {
	nav := NewTemporalNavigator(10)
	nav.currentVersion = 5
	
	segment := TemporalSegment{Type: TemporalAny}
	result, err := nav.Navigate(segment)
	
	if err != nil {
		t.Fatalf("Navigate() error = %v, want nil", err)
	}
	
	if !result.Success {
		t.Error("Navigation should be successful")
	}
	
	if result.TargetVersion != 5 {
		t.Errorf("TargetVersion = %d, want 5 (should stay at current)", result.TargetVersion)
	}
	
	if nav.GetCurrentVersion() != 5 {
		t.Errorf("Current version = %d, want 5", nav.GetCurrentVersion())
	}
}

func TestNavigateSpecific(t *testing.T) {
	nav := NewTemporalNavigator(10)
	
	tests := []struct {
		name        string
		version     int
		shouldError bool
		expectedPos int
	}{
		{
			name:        "valid version",
			version:     7,
			shouldError: false,
			expectedPos: 7,
		},
		{
			name:        "version 0",
			version:     0,
			shouldError: false,
			expectedPos: 0,
		},
		{
			name:        "max version",
			version:     10,
			shouldError: false,
			expectedPos: 10,
		},
		{
			name:        "negative version",
			version:     -1,
			shouldError: true,
			expectedPos: 10, // Should stay at original position
		},
		{
			name:        "version beyond max",
			version:     15,
			shouldError: true,
			expectedPos: 10, // Should stay at original position
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			nav.Reset() // Reset to max version
			
			segment := TemporalSegment{
				Type:  TemporalSpecific,
				Count: tt.version,
			}
			
			result, err := nav.Navigate(segment)
			
			if tt.shouldError {
				if err == nil {
					t.Error("Expected error but got none")
				}
				if result.Success {
					t.Error("Result should indicate failure")
				}
			} else {
				if err != nil {
					t.Errorf("Unexpected error: %v", err)
				}
				if !result.Success {
					t.Error("Result should indicate success")
				}
			}
			
			if nav.GetCurrentVersion() != tt.expectedPos {
				t.Errorf("Current version = %d, want %d", nav.GetCurrentVersion(), tt.expectedPos)
			}
		})
	}
}

func TestNavigateBackward(t *testing.T) {
	nav := NewTemporalNavigator(10)
	nav.currentVersion = 5
	
	tests := []struct {
		name        string
		count       int
		shouldError bool
		expectedPos int
	}{
		{
			name:        "valid backward navigation",
			count:       2,
			shouldError: false,
			expectedPos: 3,
		},
		{
			name:        "backward to version 0",
			count:       5,
			shouldError: false,
			expectedPos: 0,
		},
		{
			name:        "backward beyond version 0",
			count:       10,
			shouldError: true,
			expectedPos: 5, // Should stay at original position
		},
		{
			name:        "negative count",
			count:       -1,
			shouldError: true,
			expectedPos: 5, // Should stay at original position
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			nav.currentVersion = 5 // Reset position
			
			segment := TemporalSegment{
				Type:      TemporalRelative,
				Direction: DirectionBackward,
				Count:     tt.count,
			}
			
			result, err := nav.Navigate(segment)
			
			if tt.shouldError {
				if err == nil {
					t.Error("Expected error but got none")
				}
				if result.Success {
					t.Error("Result should indicate failure")
				}
			} else {
				if err != nil {
					t.Errorf("Unexpected error: %v", err)
				}
				if !result.Success {
					t.Error("Result should indicate success")
				}
			}
			
			if nav.GetCurrentVersion() != tt.expectedPos {
				t.Errorf("Current version = %d, want %d", nav.GetCurrentVersion(), tt.expectedPos)
			}
		})
	}
}

func TestNavigateForward(t *testing.T) {
	nav := NewTemporalNavigator(10)
	nav.currentVersion = 5
	
	tests := []struct {
		name        string
		count       int
		shouldError bool
		expectedPos int
	}{
		{
			name:        "valid forward navigation",
			count:       3,
			shouldError: false,
			expectedPos: 8,
		},
		{
			name:        "forward to max version",
			count:       5,
			shouldError: false,
			expectedPos: 10,
		},
		{
			name:        "forward beyond max version",
			count:       10,
			shouldError: true,
			expectedPos: 5, // Should stay at original position
		},
		{
			name:        "negative count",
			count:       -1,
			shouldError: true,
			expectedPos: 5, // Should stay at original position
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			nav.currentVersion = 5 // Reset position
			
			segment := TemporalSegment{
				Type:      TemporalRelative,
				Direction: DirectionForward,
				Count:     tt.count,
			}
			
			result, err := nav.Navigate(segment)
			
			if tt.shouldError {
				if err == nil {
					t.Error("Expected error but got none")
				}
				if result.Success {
					t.Error("Result should indicate failure")
				}
			} else {
				if err != nil {
					t.Errorf("Unexpected error: %v", err)
				}
				if !result.Success {
					t.Error("Result should indicate success")
				}
			}
			
			if nav.GetCurrentVersion() != tt.expectedPos {
				t.Errorf("Current version = %d, want %d", nav.GetCurrentVersion(), tt.expectedPos)
			}
		})
	}
}

func TestNavigationHistory(t *testing.T) {
	nav := NewTemporalNavigator(10)
	
	// Perform several navigations
	segments := []TemporalSegment{
		{Type: TemporalSpecific, Count: 5},
		{Type: TemporalRelative, Direction: DirectionBackward, Count: 2},
		{Type: TemporalLatest},
	}
	
	for _, segment := range segments {
		nav.Navigate(segment)
	}
	
	history := nav.GetHistory()
	if len(history) != 3 {
		t.Errorf("History length = %d, want 3", len(history))
	}
	
	// Check that all steps are recorded
	for i, step := range history {
		if step.Operation == "" {
			t.Errorf("Step %d should have operation recorded", i)
		}
		if step.Timestamp.IsZero() {
			t.Errorf("Step %d should have timestamp", i)
		}
		if !step.Success {
			t.Errorf("Step %d should be successful", i)
		}
	}
	
	// Test clear history
	nav.ClearHistory()
	if len(nav.GetHistory()) != 0 {
		t.Error("History should be empty after ClearHistory()")
	}
}

func TestSetMaxVersion(t *testing.T) {
	nav := NewTemporalNavigator(10)
	nav.currentVersion = 5
	
	// Test increasing max version
	err := nav.SetMaxVersion(15)
	if err != nil {
		t.Errorf("SetMaxVersion(15) error = %v, want nil", err)
	}
	if nav.GetMaxVersion() != 15 {
		t.Errorf("Max version = %d, want 15", nav.GetMaxVersion())
	}
	if nav.GetCurrentVersion() != 5 {
		t.Errorf("Current version should remain at 5, got %d", nav.GetCurrentVersion())
	}
	
	// Test decreasing max version below current
	err = nav.SetMaxVersion(3)
	if err != nil {
		t.Errorf("SetMaxVersion(3) error = %v, want nil", err)
	}
	if nav.GetMaxVersion() != 3 {
		t.Errorf("Max version = %d, want 3", nav.GetMaxVersion())
	}
	if nav.GetCurrentVersion() != 3 {
		t.Errorf("Current version should be adjusted to 3, got %d", nav.GetCurrentVersion())
	}
	
	// Test negative max version
	err = nav.SetMaxVersion(-1)
	if err == nil {
		t.Error("SetMaxVersion(-1) should return error")
	}
}

func TestVersionInfo(t *testing.T) {
	nav := NewTemporalNavigator(10)
	
	info := VersionInfo{
		Version:     5,
		Created:     time.Now(),
		Author:      "test-author",
		Description: "test version",
		Tags:        []string{"stable", "release"},
	}
	
	// Set version info
	nav.SetVersionInfo(5, info)
	
	// Retrieve version info
	retrievedInfo, exists := nav.GetVersionInfo(5)
	if !exists {
		t.Error("Version info should exist")
	}
	if retrievedInfo.Author != info.Author {
		t.Errorf("Author = %s, want %s", retrievedInfo.Author, info.Author)
	}
	
	// Test non-existent version
	_, exists = nav.GetVersionInfo(99)
	if exists {
		t.Error("Version info should not exist for version 99")
	}
	
	// Test GetAllVersions
	allVersions := nav.GetAllVersions()
	if len(allVersions) != 1 {
		t.Errorf("All versions count = %d, want 1", len(allVersions))
	}
}

func TestCanNavigate(t *testing.T) {
	nav := NewTemporalNavigator(10)
	nav.currentVersion = 5
	
	tests := []struct {
		name      string
		direction string
		count     int
		expected  bool
	}{
		{"can go backward 3", "backward", 3, true},
		{"can go backward 5", "backward", 5, true},
		{"cannot go backward 6", "backward", 6, false},
		{"can go forward 3", "forward", 3, true},
		{"can go forward 5", "forward", 5, true},
		{"cannot go forward 6", "forward", 6, false},
	}
	
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			var result bool
			if tt.direction == "backward" {
				result = nav.CanNavigateBackward(tt.count)
			} else {
				result = nav.CanNavigateForward(tt.count)
			}
			
			if result != tt.expected {
				t.Errorf("Can navigate %s %d = %v, want %v", tt.direction, tt.count, result, tt.expected)
			}
		})
	}
}

func TestValidateTemporalSegment(t *testing.T) {
	nav := NewTemporalNavigator(10)
	nav.currentVersion = 5
	
	tests := []struct {
		name        string
		segment     TemporalSegment
		shouldError bool
	}{
		{
			name:        "latest is valid",
			segment:     TemporalSegment{Type: TemporalLatest},
			shouldError: false,
		},
		{
			name:        "any is valid",
			segment:     TemporalSegment{Type: TemporalAny},
			shouldError: false,
		},
		{
			name:        "valid specific version",
			segment:     TemporalSegment{Type: TemporalSpecific, Count: 7},
			shouldError: false,
		},
		{
			name:        "specific version out of range",
			segment:     TemporalSegment{Type: TemporalSpecific, Count: 15},
			shouldError: true,
		},
		{
			name:        "valid backward navigation",
			segment:     TemporalSegment{Type: TemporalRelative, Direction: DirectionBackward, Count: 3},
			shouldError: false,
		},
		{
			name:        "backward navigation out of range",
			segment:     TemporalSegment{Type: TemporalRelative, Direction: DirectionBackward, Count: 10},
			shouldError: true,
		},
		{
			name:        "valid forward navigation",
			segment:     TemporalSegment{Type: TemporalRelative, Direction: DirectionForward, Count: 3},
			shouldError: false,
		},
		{
			name:        "forward navigation out of range",
			segment:     TemporalSegment{Type: TemporalRelative, Direction: DirectionForward, Count: 10},
			shouldError: true,
		},
	}
	
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := nav.ValidateTemporalSegment(tt.segment)
			
			if tt.shouldError {
				if err == nil {
					t.Error("Expected error but got none")
				}
			} else {
				if err != nil {
					t.Errorf("Unexpected error: %v", err)
				}
			}
		})
	}
}

func TestNavigatorClone(t *testing.T) {
	nav := NewTemporalNavigator(10)
	nav.currentVersion = 5
	
	// Add some version info and history
	nav.SetVersionInfo(5, VersionInfo{Author: "test"})
	nav.Navigate(TemporalSegment{Type: TemporalLatest})
	
	cloned := nav.Clone()
	
	// Test that basic properties are cloned
	if cloned.GetCurrentVersion() != nav.GetCurrentVersion() {
		t.Error("Current version should be cloned")
	}
	if cloned.GetMaxVersion() != nav.GetMaxVersion() {
		t.Error("Max version should be cloned")
	}
	
	// Test that history is cloned
	originalHistory := nav.GetHistory()
	clonedHistory := cloned.GetHistory()
	if !reflect.DeepEqual(originalHistory, clonedHistory) {
		t.Error("History should be cloned")
	}
	
	// Test that version info is cloned
	originalVersions := nav.GetAllVersions()
	clonedVersions := cloned.GetAllVersions()
	if !reflect.DeepEqual(originalVersions, clonedVersions) {
		t.Error("Version info should be cloned")
	}
	
	// Test independence - modifying clone shouldn't affect original
	cloned.currentVersion = 0
	if nav.GetCurrentVersion() == cloned.GetCurrentVersion() {
		t.Error("Clone should be independent")
	}
}

func TestGetLastNavigation(t *testing.T) {
	nav := NewTemporalNavigator(10)
	
	// Initially should return nil
	last := nav.GetLastNavigation()
	if last != nil {
		t.Error("GetLastNavigation() should return nil when no navigation has occurred")
	}
	
	// After navigation should return the step
	segment := TemporalSegment{Type: TemporalSpecific, Count: 5}
	nav.Navigate(segment)
	
	last = nav.GetLastNavigation()
	if last == nil {
		t.Error("GetLastNavigation() should return the last navigation step")
	}
	if last.Operation != segment.String() {
		t.Errorf("Operation = %s, want %s", last.Operation, segment.String())
	}
}

// Benchmark tests
func BenchmarkNavigate(b *testing.B) {
	nav := NewTemporalNavigator(100)
	segment := TemporalSegment{Type: TemporalSpecific, Count: 50}
	
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		nav.Navigate(segment)
	}
}

func BenchmarkValidateTemporalSegment(b *testing.B) {
	nav := NewTemporalNavigator(100)
	nav.currentVersion = 50
	segment := TemporalSegment{Type: TemporalRelative, Direction: DirectionBackward, Count: 10}
	
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		nav.ValidateTemporalSegment(segment)
	}
}