package reconciler

import (
	"context"
	"fmt"
	"testing"

	apierrors "k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/apimachinery/pkg/runtime/schema"

	musterv1alpha1 "github.com/giantswarm/muster/pkg/apis/muster/v1alpha1"

	"github.com/giantswarm/muster/internal/api"
)

func TestMCPServerReconciler_GetResourceType(t *testing.T) {
	mgr := NewMockMCPServerManager()
	orchAPI := NewMockOrchestratorAPI()
	registry := NewMockServiceRegistry()
	reconciler := NewMCPServerReconciler(orchAPI, mgr, registry)

	if reconciler.GetResourceType() != ResourceTypeMCPServer {
		t.Errorf("expected ResourceTypeMCPServer, got %s", reconciler.GetResourceType())
	}
}

func TestMCPServerReconciler_ReconcileCreate(t *testing.T) {
	mgr := NewMockMCPServerManager()
	orchAPI := NewMockOrchestratorAPI()
	registry := NewMockServiceRegistry()
	reconciler := NewMCPServerReconciler(orchAPI, mgr, registry)

	// Add a valid MCPServer with AutoStart enabled
	mgr.AddMCPServer(&api.MCPServerInfo{
		Name:      "test-server",
		Type:      "stdio",
		Command:   "test-command",
		AutoStart: true,
	})

	req := ReconcileRequest{
		Type:    ResourceTypeMCPServer,
		Name:    "test-server",
		Attempt: 1,
	}

	ctx := context.Background()
	result := reconciler.Reconcile(ctx, req)

	if result.Error != nil {
		t.Errorf("unexpected error: %v", result.Error)
	}

	// Verify service was started
	if !orchAPI.StartedServices["test-server"] {
		t.Error("expected service to be started")
	}
}

func TestMCPServerReconciler_ReconcileCreateNoAutoStart(t *testing.T) {
	mgr := NewMockMCPServerManager()
	orchAPI := NewMockOrchestratorAPI()
	registry := NewMockServiceRegistry()
	reconciler := NewMCPServerReconciler(orchAPI, mgr, registry)

	// Add a valid MCPServer with AutoStart disabled
	mgr.AddMCPServer(&api.MCPServerInfo{
		Name:      "test-server",
		Type:      "stdio",
		Command:   "test-command",
		AutoStart: false,
	})

	req := ReconcileRequest{
		Type:    ResourceTypeMCPServer,
		Name:    "test-server",
		Attempt: 1,
	}

	ctx := context.Background()
	result := reconciler.Reconcile(ctx, req)

	if result.Error != nil {
		t.Errorf("unexpected error: %v", result.Error)
	}

	// Verify service was NOT started (AutoStart is false)
	if orchAPI.StartedServices["test-server"] {
		t.Error("service should not be started when AutoStart is false")
	}
}

func TestMCPServerReconciler_ReconcileDelete(t *testing.T) {
	mgr := NewMockMCPServerManager()
	orchAPI := NewMockOrchestratorAPI()
	registry := NewMockServiceRegistry()

	// Add service to registry to simulate it exists
	registry.AddService("deleted-server", &MockServiceInfo{
		Name:        "deleted-server",
		ServiceType: api.TypeMCPServer,
		State:       api.StateRunning,
		Health:      api.HealthHealthy,
	})

	reconciler := NewMCPServerReconciler(orchAPI, mgr, registry)

	// Do not add the MCPServer to manager - simulate a delete scenario
	req := ReconcileRequest{
		Type:    ResourceTypeMCPServer,
		Name:    "deleted-server",
		Attempt: 1,
	}

	ctx := context.Background()
	result := reconciler.Reconcile(ctx, req)

	if result.Error != nil {
		t.Errorf("unexpected error for delete: %v", result.Error)
	}

	// Verify service was stopped
	if !orchAPI.StoppedServices["deleted-server"] {
		t.Error("expected service to be stopped on delete")
	}
}

func TestMCPServerReconciler_ReconcileDeleteNotFound(t *testing.T) {
	mgr := NewMockMCPServerManager()
	orchAPI := NewMockOrchestratorAPI()
	registry := NewMockServiceRegistry()
	reconciler := NewMCPServerReconciler(orchAPI, mgr, registry)

	// Do not add the MCPServer to manager or registry - nothing to delete
	req := ReconcileRequest{
		Type:    ResourceTypeMCPServer,
		Name:    "nonexistent-server",
		Attempt: 1,
	}

	ctx := context.Background()
	result := reconciler.Reconcile(ctx, req)

	if result.Error != nil {
		t.Errorf("unexpected error for nonexistent delete: %v", result.Error)
	}
	if result.Requeue {
		t.Error("expected no requeue for nonexistent resource")
	}
}

func TestMCPServerReconciler_ReconcileUpdate(t *testing.T) {
	mgr := NewMockMCPServerManager()
	orchAPI := NewMockOrchestratorAPI()
	registry := NewMockServiceRegistry()

	// Add existing service whose configuration has changed
	registry.AddService("test-server", &MockServiceInfo{
		Name:          "test-server",
		ServiceType:   api.TypeMCPServer,
		State:         api.StateRunning,
		Health:        api.HealthHealthy,
		ConfigChanged: true,
	})

	reconciler := NewMCPServerReconciler(orchAPI, mgr, registry)

	// Add MCPServer with new configuration
	mgr.AddMCPServer(&api.MCPServerInfo{
		Name:      "test-server",
		Type:      "stdio",
		Command:   "new-command", // Changed
		AutoStart: true,
	})

	req := ReconcileRequest{
		Type:    ResourceTypeMCPServer,
		Name:    "test-server",
		Attempt: 1,
	}

	ctx := context.Background()
	result := reconciler.Reconcile(ctx, req)

	if result.Error != nil {
		t.Errorf("unexpected error: %v", result.Error)
	}

	// Verify service was restarted due to config change
	if !orchAPI.RestartedServices["test-server"] {
		t.Error("expected service to be restarted on config change")
	}
}

func TestMCPServerReconciler_ReconcileUpdateNoChange(t *testing.T) {
	mgr := NewMockMCPServerManager()
	orchAPI := NewMockOrchestratorAPI()
	registry := NewMockServiceRegistry()

	// Add existing service with unchanged configuration (ConfigChanged defaults to false)
	registry.AddService("test-server", &MockServiceInfo{
		Name:        "test-server",
		ServiceType: api.TypeMCPServer,
		State:       api.StateRunning,
		Health:      api.HealthHealthy,
	})

	reconciler := NewMCPServerReconciler(orchAPI, mgr, registry)

	// Add MCPServer with same configuration
	mgr.AddMCPServer(&api.MCPServerInfo{
		Name:      "test-server",
		Type:      "stdio",
		Command:   "test-command",
		AutoStart: true,
	})

	req := ReconcileRequest{
		Type:    ResourceTypeMCPServer,
		Name:    "test-server",
		Attempt: 1,
	}

	ctx := context.Background()
	result := reconciler.Reconcile(ctx, req)

	if result.Error != nil {
		t.Errorf("unexpected error: %v", result.Error)
	}

	// Verify service was NOT restarted (no config change)
	if orchAPI.RestartedServices["test-server"] {
		t.Error("service should not be restarted when config is unchanged")
	}
}

func TestMCPServerReconciler_ReconcileCreateAuthRequired(t *testing.T) {
	mgr := NewMockMCPServerManager()
	orchAPI := NewMockOrchestratorAPI()
	registry := NewMockServiceRegistry()

	// Simulate AuthRequiredError from StartService
	orchAPI.StartError = fmt.Errorf("failed to start service: %w", &mockAuthRequiredError{msg: "401 Unauthorized"})

	reconciler := NewMCPServerReconciler(orchAPI, mgr, registry)

	mgr.AddMCPServer(&api.MCPServerInfo{
		Name:      "remote-server",
		Type:      "streamable-http",
		URL:       "https://example.com/mcp",
		AutoStart: true,
	})

	req := ReconcileRequest{
		Type:    ResourceTypeMCPServer,
		Name:    "remote-server",
		Attempt: 1,
	}

	ctx := context.Background()
	result := reconciler.Reconcile(ctx, req)

	// Auth Required should be treated as success, not an error
	if result.Error != nil {
		t.Errorf("expected no error for auth required state, got: %v", result.Error)
	}
	if result.Requeue {
		t.Error("expected no requeue for auth required state")
	}
}

func TestMCPServerReconciler_ReconcileStartError(t *testing.T) {
	mgr := NewMockMCPServerManager()
	orchAPI := NewMockOrchestratorAPI()
	registry := NewMockServiceRegistry()

	// Simulate start error
	orchAPI.StartError = fmt.Errorf("service not found in orchestrator")

	reconciler := NewMCPServerReconciler(orchAPI, mgr, registry)

	mgr.AddMCPServer(&api.MCPServerInfo{
		Name:      "test-server",
		Type:      "stdio",
		Command:   "test-command",
		AutoStart: true,
	})

	req := ReconcileRequest{
		Type:    ResourceTypeMCPServer,
		Name:    "test-server",
		Attempt: 1,
	}

	ctx := context.Background()
	result := reconciler.Reconcile(ctx, req)

	if result.Error == nil {
		t.Error("expected error when start fails")
	}
	if !result.Requeue {
		t.Error("expected requeue on start error")
	}
}

func TestMCPServerReconciler_ReconcileStopError(t *testing.T) {
	mgr := NewMockMCPServerManager()
	orchAPI := NewMockOrchestratorAPI()
	registry := NewMockServiceRegistry()

	// Add service to registry to simulate it exists
	registry.AddService("deleted-server", &MockServiceInfo{
		Name:        "deleted-server",
		ServiceType: api.TypeMCPServer,
		State:       api.StateRunning,
		Health:      api.HealthHealthy,
	})

	// Simulate stop error
	orchAPI.StopError = fmt.Errorf("failed to stop service")

	reconciler := NewMCPServerReconciler(orchAPI, mgr, registry)

	// Do not add the MCPServer to manager - simulate a delete scenario
	req := ReconcileRequest{
		Type:    ResourceTypeMCPServer,
		Name:    "deleted-server",
		Attempt: 1,
	}

	ctx := context.Background()
	result := reconciler.Reconcile(ctx, req)

	if result.Error == nil {
		t.Error("expected error when stop fails")
	}
	if !result.Requeue {
		t.Error("expected requeue on stop error")
	}
}

func TestMCPServerReconciler_ReconcileUpdateAuthRequired(t *testing.T) {
	mgr := NewMockMCPServerManager()
	orchAPI := NewMockOrchestratorAPI()
	registry := NewMockServiceRegistry()

	// Add existing service whose configuration has changed
	registry.AddService("remote-server", &MockServiceInfo{
		Name:          "remote-server",
		ServiceType:   api.TypeMCPServer,
		State:         api.StateRunning,
		Health:        api.HealthHealthy,
		ConfigChanged: true,
	})

	// Simulate AuthRequiredError from RestartService
	orchAPI.RestartError = fmt.Errorf("failed to restart: %w", &mockAuthRequiredError{msg: "401 Unauthorized"})

	reconciler := NewMCPServerReconciler(orchAPI, mgr, registry)

	mgr.AddMCPServer(&api.MCPServerInfo{
		Name:      "remote-server",
		Type:      "streamable-http",
		URL:       "https://new-url.com/mcp", // Changed URL to trigger restart
		AutoStart: true,
	})

	req := ReconcileRequest{
		Type:    ResourceTypeMCPServer,
		Name:    "remote-server",
		Attempt: 1,
	}

	ctx := context.Background()
	result := reconciler.Reconcile(ctx, req)

	// Auth Required after config update should be treated as success
	if result.Error != nil {
		t.Errorf("expected no error for auth required after restart, got: %v", result.Error)
	}
	if result.Requeue {
		t.Error("expected no requeue for auth required after restart")
	}
}

func TestMCPServerReconciler_ReconcileRestartError(t *testing.T) {
	mgr := NewMockMCPServerManager()
	orchAPI := NewMockOrchestratorAPI()
	registry := NewMockServiceRegistry()

	// Add existing service whose configuration has changed
	registry.AddService("test-server", &MockServiceInfo{
		Name:          "test-server",
		ServiceType:   api.TypeMCPServer,
		State:         api.StateRunning,
		Health:        api.HealthHealthy,
		ConfigChanged: true,
	})

	// Simulate restart error
	orchAPI.RestartError = fmt.Errorf("failed to restart service")

	reconciler := NewMCPServerReconciler(orchAPI, mgr, registry)

	// Add MCPServer with new configuration to trigger restart
	mgr.AddMCPServer(&api.MCPServerInfo{
		Name:      "test-server",
		Type:      "stdio",
		Command:   "new-command", // Changed
		AutoStart: true,
	})

	req := ReconcileRequest{
		Type:    ResourceTypeMCPServer,
		Name:    "test-server",
		Attempt: 1,
	}

	ctx := context.Background()
	result := reconciler.Reconcile(ctx, req)

	if result.Error == nil {
		t.Error("expected error when restart fails")
	}
	if !result.Requeue {
		t.Error("expected requeue on restart error")
	}
}

func TestMCPServerReconciler_PeriodicRequeue(t *testing.T) {
	mgr := NewMockMCPServerManager()
	orchAPI := NewMockOrchestratorAPI()
	registry := NewMockServiceRegistry()

	// Add existing service (no config change, ConfigChanged defaults to false)
	registry.AddService("test-server", &MockServiceInfo{
		Name:        "test-server",
		ServiceType: api.TypeMCPServer,
		State:       api.StateRunning,
		Health:      api.HealthHealthy,
	})

	reconciler := NewMCPServerReconciler(orchAPI, mgr, registry)

	mgr.AddMCPServer(&api.MCPServerInfo{
		Name:      "test-server",
		Type:      "stdio",
		Command:   "test-command",
		AutoStart: true,
	})

	req := ReconcileRequest{
		Type:    ResourceTypeMCPServer,
		Name:    "test-server",
		Attempt: 1,
	}

	ctx := context.Background()
	result := reconciler.Reconcile(ctx, req)

	if result.Error != nil {
		t.Errorf("unexpected error: %v", result.Error)
	}

	// Verify RequeueAfter is set for periodic status sync
	if result.RequeueAfter == 0 {
		t.Error("expected RequeueAfter to be set for periodic status sync")
	}

	if result.RequeueAfter != DefaultStatusSyncInterval {
		t.Errorf("expected RequeueAfter = %v, got %v", DefaultStatusSyncInterval, result.RequeueAfter)
	}
}

func TestMCPServerReconciler_ArgsChange(t *testing.T) {
	mgr := NewMockMCPServerManager()
	orchAPI := NewMockOrchestratorAPI()
	registry := NewMockServiceRegistry()

	// Add existing service whose args have changed
	registry.AddService("test-server", &MockServiceInfo{
		Name:          "test-server",
		ServiceType:   api.TypeMCPServer,
		State:         api.StateRunning,
		Health:        api.HealthHealthy,
		ConfigChanged: true,
	})

	reconciler := NewMCPServerReconciler(orchAPI, mgr, registry)

	// Add MCPServer with different args
	mgr.AddMCPServer(&api.MCPServerInfo{
		Name:      "test-server",
		Type:      "stdio",
		Command:   "test-command",
		Args:      []string{"new-arg1", "new-arg2"},
		AutoStart: true,
	})

	req := ReconcileRequest{
		Type:    ResourceTypeMCPServer,
		Name:    "test-server",
		Attempt: 1,
	}

	ctx := context.Background()
	result := reconciler.Reconcile(ctx, req)

	if result.Error != nil {
		t.Errorf("unexpected error: %v", result.Error)
	}

	// Verify service was restarted due to args change
	if !orchAPI.RestartedServices["test-server"] {
		t.Error("expected service to be restarted when args change")
	}
}

// =============================================================================
// Status Sync Tests - Verify syncStatus functionality
// =============================================================================

func TestMCPServerReconciler_SyncStatus_RunningService(t *testing.T) {
	mgr := NewMockMCPServerManager()
	orchAPI := NewMockOrchestratorAPI()
	registry := NewMockServiceRegistry()
	statusUpdater := NewMockStatusUpdater()

	// Add existing running service
	registry.AddService("test-server", &MockServiceInfo{
		Name:        "test-server",
		ServiceType: api.TypeMCPServer,
		State:       api.StateRunning,
		Health:      api.HealthHealthy,
	})

	reconciler := NewMCPServerReconciler(orchAPI, mgr, registry).
		WithStatusUpdater(statusUpdater, "default")

	// Add MCPServer with same config (no restart needed)
	mgr.AddMCPServer(&api.MCPServerInfo{
		Name:      "test-server",
		Type:      "stdio",
		Command:   "test-command",
		AutoStart: true,
	})

	req := ReconcileRequest{
		Type:      ResourceTypeMCPServer,
		Name:      "test-server",
		Namespace: "default",
		Attempt:   1,
	}

	ctx := context.Background()
	result := reconciler.Reconcile(ctx, req)

	if result.Error != nil {
		t.Errorf("unexpected error: %v", result.Error)
	}

	// Verify status was synced
	if !statusUpdater.GetMCPServerCalled {
		t.Error("expected GetMCPServer to be called for status sync")
	}
	if !statusUpdater.UpdateMCPServerStatusCalled {
		t.Error("expected UpdateMCPServerStatus to be called")
	}

	// Verify status values
	if statusUpdater.LastUpdatedMCPServer == nil {
		t.Fatal("expected LastUpdatedMCPServer to be set")
	}
	// For stdio servers, Running state maps to MCPServerStateRunning
	if statusUpdater.LastUpdatedMCPServer.Status.State != musterv1alpha1.MCPServerStateRunning {
		t.Errorf("expected state 'Running', got '%s'", statusUpdater.LastUpdatedMCPServer.Status.State)
	}
	if statusUpdater.LastUpdatedMCPServer.Status.LastConnected == nil {
		t.Error("expected LastConnected to be set for running service")
	}
}

func TestMCPServerReconciler_SyncStatus_ServiceNotFound(t *testing.T) {
	mgr := NewMockMCPServerManager()
	orchAPI := NewMockOrchestratorAPI()
	registry := NewMockServiceRegistry()
	statusUpdater := NewMockStatusUpdater()

	// No service in registry - simulate deleted service

	reconciler := NewMCPServerReconciler(orchAPI, mgr, registry).
		WithStatusUpdater(statusUpdater, "default")

	// MCPServer exists but service doesn't - will be created
	mgr.AddMCPServer(&api.MCPServerInfo{
		Name:      "test-server",
		Type:      "stdio",
		Command:   "test-command",
		AutoStart: true,
	})

	req := ReconcileRequest{
		Type:      ResourceTypeMCPServer,
		Name:      "test-server",
		Namespace: "default",
		Attempt:   1,
	}

	ctx := context.Background()
	_ = reconciler.Reconcile(ctx, req)

	// Verify status was synced (even if service is not yet running)
	if !statusUpdater.UpdateMCPServerStatusCalled {
		t.Error("expected UpdateMCPServerStatus to be called")
	}
}

func TestMCPServerReconciler_SyncStatus_WithError(t *testing.T) {
	mgr := NewMockMCPServerManager()
	orchAPI := NewMockOrchestratorAPI()
	registry := NewMockServiceRegistry()
	statusUpdater := NewMockStatusUpdater()

	// Add service with error
	registry.AddService("test-server", &MockServiceInfo{
		Name:        "test-server",
		ServiceType: api.TypeMCPServer,
		State:       api.StateError,
		Health:      api.HealthUnhealthy,
		LastError:   fmt.Errorf("connection failed"),
	})

	reconciler := NewMCPServerReconciler(orchAPI, mgr, registry).
		WithStatusUpdater(statusUpdater, "default")

	mgr.AddMCPServer(&api.MCPServerInfo{
		Name:      "test-server",
		Type:      "stdio",
		Command:   "test-command",
		AutoStart: true,
	})

	req := ReconcileRequest{
		Type:      ResourceTypeMCPServer,
		Name:      "test-server",
		Namespace: "default",
		Attempt:   1,
	}

	ctx := context.Background()
	_ = reconciler.Reconcile(ctx, req)

	// Verify status was synced with error
	if !statusUpdater.UpdateMCPServerStatusCalled {
		t.Error("expected UpdateMCPServerStatus to be called")
	}

	if statusUpdater.LastUpdatedMCPServer == nil {
		t.Fatal("expected LastUpdatedMCPServer to be set")
	}
	if statusUpdater.LastUpdatedMCPServer.Status.State != musterv1alpha1.MCPServerStateFailed {
		t.Errorf("expected state 'Failed', got '%s'", statusUpdater.LastUpdatedMCPServer.Status.State)
	}
	if statusUpdater.LastUpdatedMCPServer.Status.LastError == "" {
		t.Error("expected LastError to be set")
	}
}

func TestMCPServerReconciler_SyncStatus_NoUpdaterConfigured(t *testing.T) {
	mgr := NewMockMCPServerManager()
	orchAPI := NewMockOrchestratorAPI()
	registry := NewMockServiceRegistry()

	// No status updater configured - should not panic
	reconciler := NewMCPServerReconciler(orchAPI, mgr, registry)

	mgr.AddMCPServer(&api.MCPServerInfo{
		Name:      "test-server",
		Type:      "stdio",
		Command:   "test-command",
		AutoStart: true,
	})

	req := ReconcileRequest{
		Type:    ResourceTypeMCPServer,
		Name:    "test-server",
		Attempt: 1,
	}

	ctx := context.Background()
	result := reconciler.Reconcile(ctx, req)

	// Should complete without error even without status updater
	if result.Error != nil {
		t.Errorf("unexpected error: %v", result.Error)
	}
}

func TestMCPServerReconciler_SyncStatus_GetMCPServerError(t *testing.T) {
	mgr := NewMockMCPServerManager()
	orchAPI := NewMockOrchestratorAPI()
	registry := NewMockServiceRegistry()
	statusUpdater := NewMockStatusUpdater()

	// Simulate error when getting MCPServer CRD
	statusUpdater.GetMCPServerError = fmt.Errorf("CRD not found")

	registry.AddService("test-server", &MockServiceInfo{
		Name:        "test-server",
		ServiceType: api.TypeMCPServer,
		State:       api.StateRunning,
		Health:      api.HealthHealthy,
	})

	reconciler := NewMCPServerReconciler(orchAPI, mgr, registry).
		WithStatusUpdater(statusUpdater, "default")

	mgr.AddMCPServer(&api.MCPServerInfo{
		Name:      "test-server",
		Type:      "stdio",
		Command:   "test-command",
		AutoStart: true,
	})

	req := ReconcileRequest{
		Type:      ResourceTypeMCPServer,
		Name:      "test-server",
		Namespace: "default",
		Attempt:   1,
	}

	ctx := context.Background()
	result := reconciler.Reconcile(ctx, req)

	// Reconciliation should still succeed even if status sync fails
	if result.Error != nil {
		t.Errorf("unexpected error: %v", result.Error)
	}

	// GetMCPServer was called but UpdateMCPServerStatus should not be called
	if !statusUpdater.GetMCPServerCalled {
		t.Error("expected GetMCPServer to be called")
	}
	if statusUpdater.UpdateMCPServerStatusCalled {
		t.Error("expected UpdateMCPServerStatus NOT to be called when GetMCPServer fails")
	}
}

func TestMCPServerReconciler_SyncStatus_UpdateError(t *testing.T) {
	mgr := NewMockMCPServerManager()
	orchAPI := NewMockOrchestratorAPI()
	registry := NewMockServiceRegistry()
	statusUpdater := NewMockStatusUpdater()

	// Simulate error when updating status
	statusUpdater.UpdateMCPServerStatusError = fmt.Errorf("update failed")

	registry.AddService("test-server", &MockServiceInfo{
		Name:        "test-server",
		ServiceType: api.TypeMCPServer,
		State:       api.StateRunning,
		Health:      api.HealthHealthy,
	})

	reconciler := NewMCPServerReconciler(orchAPI, mgr, registry).
		WithStatusUpdater(statusUpdater, "default")

	mgr.AddMCPServer(&api.MCPServerInfo{
		Name:      "test-server",
		Type:      "stdio",
		Command:   "test-command",
		AutoStart: true,
	})

	req := ReconcileRequest{
		Type:      ResourceTypeMCPServer,
		Name:      "test-server",
		Namespace: "default",
		Attempt:   1,
	}

	ctx := context.Background()
	result := reconciler.Reconcile(ctx, req)

	// Reconciliation should still succeed even if status update fails
	// (status sync is best-effort)
	if result.Error != nil {
		t.Errorf("unexpected error: %v", result.Error)
	}

	// Both methods should be called
	if !statusUpdater.GetMCPServerCalled {
		t.Error("expected GetMCPServer to be called")
	}
	if !statusUpdater.UpdateMCPServerStatusCalled {
		t.Error("expected UpdateMCPServerStatus to be called")
	}
}

func TestCategorizeStatusSyncError(t *testing.T) {
	tests := []struct {
		name     string
		err      error
		expected string
	}{
		{
			name:     "nil error",
			err:      nil,
			expected: "unknown",
		},
		{
			name:     "not found error",
			err:      fmt.Errorf("resource not found"),
			expected: "crd_not_found",
		},
		{
			name:     "connection refused",
			err:      fmt.Errorf("connection refused"),
			expected: "api_server_unreachable",
		},
		{
			name:     "no route to host",
			err:      fmt.Errorf("no route to host"),
			expected: "api_server_unreachable",
		},
		{
			name:     "network unreachable",
			err:      fmt.Errorf("network is unreachable"),
			expected: "api_server_unreachable",
		},
		{
			name:     "timeout error",
			err:      fmt.Errorf("request timeout"),
			expected: "timeout",
		},
		{
			name:     "deadline exceeded",
			err:      fmt.Errorf("context deadline exceeded"),
			expected: "timeout",
		},
		{
			name:     "forbidden lowercase",
			err:      fmt.Errorf("forbidden: user does not have access"),
			expected: "permission_denied",
		},
		{
			name:     "Forbidden uppercase",
			err:      fmt.Errorf("Forbidden: RBAC denied"),
			expected: "permission_denied",
		},
		{
			name:     "unauthorized",
			err:      fmt.Errorf("unauthorized: invalid token"),
			expected: "authentication_failed",
		},
		{
			name:     "generic error",
			err:      fmt.Errorf("some random error"),
			expected: "update_status_failed",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := CategorizeStatusSyncError(tt.err)
			if result != tt.expected {
				t.Errorf("CategorizeStatusSyncError(%v) = %s, want %s", tt.err, result, tt.expected)
			}
		})
	}
}

func TestIsConflictError(t *testing.T) {
	tests := []struct {
		name     string
		err      error
		expected bool
	}{
		{
			name:     "nil error",
			err:      nil,
			expected: false,
		},
		{
			name:     "regular error",
			err:      fmt.Errorf("some error"),
			expected: false,
		},
		{
			name:     "Kubernetes conflict error",
			err:      apierrors.NewConflict(schema.GroupResource{Group: "muster.giantswarm.io", Resource: "mcpservers"}, "test", fmt.Errorf("the object has been modified")),
			expected: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := IsConflictError(tt.err)
			if result != tt.expected {
				t.Errorf("IsConflictError(%v) = %v, want %v", tt.err, result, tt.expected)
			}
		})
	}
}

func TestMCPServerReconciler_SyncStatus_RetriesOnConflict(t *testing.T) {
	// This test verifies that the retry-on-conflict logic works correctly:
	// 1. First update attempt returns a conflict error
	// 2. Retry loop should re-fetch the CRD and try again
	// 3. Second attempt should succeed

	mgr := NewMockMCPServerManager()
	orchAPI := NewMockOrchestratorAPI()
	registry := NewMockServiceRegistry()
	statusUpdater := NewMockStatusUpdater()

	// Configure to fail with conflict on first attempt, succeed on second
	conflictErr := apierrors.NewConflict(
		schema.GroupResource{Group: "muster.giantswarm.io", Resource: "mcpservers"},
		"test-server",
		fmt.Errorf("the object has been modified"),
	)
	statusUpdater.UpdateMCPServerStatusError = conflictErr
	statusUpdater.UpdateMCPServerStatusFailCount = 1 // Fail only the first attempt

	// Add running service (no config change)
	registry.AddService("test-server", &MockServiceInfo{
		Name:        "test-server",
		ServiceType: api.TypeMCPServer,
		State:       api.StateRunning,
		Health:      api.HealthHealthy,
	})

	reconciler := NewMCPServerReconciler(orchAPI, mgr, registry).
		WithStatusUpdater(statusUpdater, "default")

	// Add MCPServer with same config (no restart needed)
	mgr.AddMCPServer(&api.MCPServerInfo{
		Name:      "test-server",
		Type:      "stdio",
		Command:   "test-command",
		AutoStart: true,
	})

	req := ReconcileRequest{
		Type:      ResourceTypeMCPServer,
		Name:      "test-server",
		Namespace: "default",
		Attempt:   1,
	}

	ctx := context.Background()
	result := reconciler.Reconcile(ctx, req)

	// Reconciliation should succeed (status sync retried and succeeded)
	if result.Error != nil {
		t.Errorf("unexpected error: %v", result.Error)
	}

	// Verify UpdateMCPServerStatus was called multiple times (retry happened)
	callCount := statusUpdater.GetUpdateMCPServerStatusCallCount()
	if callCount < 2 {
		t.Errorf("expected UpdateMCPServerStatus to be called at least 2 times (retry), got %d", callCount)
	}

	// Verify the status was eventually synced correctly
	if statusUpdater.LastUpdatedMCPServer == nil {
		t.Fatal("expected LastUpdatedMCPServer to be set")
	}
	// For stdio servers, Running state maps to MCPServerStateRunning
	if statusUpdater.LastUpdatedMCPServer.Status.State != musterv1alpha1.MCPServerStateRunning {
		t.Errorf("expected state 'Running', got '%s'", statusUpdater.LastUpdatedMCPServer.Status.State)
	}
}
