package apply

import (
	"context"
	"fmt"
	"os"
	"path/filepath"
	"testing"

	"edp.buildth.ing/DevFW-CICD/edge-connect-client/internal/config"
	"edp.buildth.ing/DevFW-CICD/edge-connect-client/internal/core/domain"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/mock"
)

func TestPlanner_Plan_CreateApp(t *testing.T) {
	mockAppRepo := new(MockAppRepository)
	mockAppInstRepo := new(MockAppInstanceRepository)
	planner := NewTestPlanner(mockAppRepo, mockAppInstRepo)

	testConfig := &config.EdgeConnectConfig{
		Metadata: config.Metadata{
			Name:         "test-app",
			AppVersion:   "1.0.0",
			Organization: "test-org",
		},
		Spec: config.Spec{
			K8sApp: &config.K8sApp{},
			InfraTemplate: []config.InfraTemplate{
				{
					Region:       "us-west",
					CloudletOrg:  "cloudlet-org",
					CloudletName: "cloudlet-name",
					FlavorName:   "m4.small",
				},
			},
		},
	}

	// Mock app not found
	mockAppRepo.On("ShowApp", mock.Anything, "us-west", domain.AppKey{
		Organization: "test-org",
		Name:         "test-app",
		Version:      "1.0.0",
	}).Return(nil, fmt.Errorf("resource not found"))

	// Mock app instance not found
	mockAppInstRepo.On("ShowAppInstance", mock.Anything, mock.AnythingOfType("string"), mock.AnythingOfType("domain.AppInstanceKey")).
		Return(nil, fmt.Errorf("resource not found"))

	result, err := planner.Plan(context.Background(), testConfig)
	assert.NoError(t, err)
	assert.NotNil(t, result)
	assert.NotNil(t, result.Plan)
	assert.Equal(t, ActionCreate, result.Plan.AppAction.Type)
	assert.Equal(t, "Application does not exist", result.Plan.AppAction.Reason)
	assert.Len(t, result.Plan.InstanceActions, 1)
	assert.Equal(t, ActionCreate, result.Plan.InstanceActions[0].Type)
	assert.Equal(t, "Instance does not exist", result.Plan.InstanceActions[0].Reason)
	assert.Equal(t, 2, result.Plan.TotalActions)

	mockAppRepo.AssertExpectations(t)
	mockAppInstRepo.AssertExpectations(t)
}

func TestPlanner_Plan_UpdateApp(t *testing.T) {
	mockAppRepo := new(MockAppRepository)
	mockAppInstRepo := new(MockAppInstanceRepository)
	planner := NewTestPlanner(mockAppRepo, mockAppInstRepo)

	testConfig := &config.EdgeConnectConfig{
		Metadata: config.Metadata{
			Name:         "test-app",
			AppVersion:   "1.0.0",
			Organization: "test-org",
		},
		Spec: config.Spec{
			K8sApp: &config.K8sApp{},
			InfraTemplate: []config.InfraTemplate{
				{
					Region:       "us-west",
					CloudletOrg:  "cloudlet-org",
					CloudletName: "cloudlet-name",
					FlavorName:   "m4.small",
				},
			},
			Network: &config.NetworkConfig{
				OutboundConnections: []config.OutboundConnection{
					{
						Protocol:     "tcp",
						PortRangeMin: 80,
						PortRangeMax: 80,
						RemoteCIDR:   "0.0.0.0/0",
					},
				},
			},
		},
	}

	existingApp := &domain.App{
		Key: domain.AppKey{
			Organization: "test-org",
			Name:         "test-app",
			Version:      "1.0.0",
		},
		Deployment: "kubernetes",
		RequiredOutboundConnections: []domain.SecurityRule{
			{
				Protocol:     "tcp",
				PortRangeMin: 8080,
				PortRangeMax: 8080,
				RemoteCIDR:   "0.0.0.0/0",
			},
		},
	}

	// Mock app found
	mockAppRepo.On("ShowApp", mock.Anything, "us-west", domain.AppKey{
		Organization: "test-org",
		Name:         "test-app",
		Version:      "1.0.0",
	}).Return(existingApp, nil)

	// Mock instance not found
	mockAppInstRepo.On("ShowAppInstance", mock.Anything, "us-west", domain.AppInstanceKey{
		Organization: "test-org",
		Name:         "test-app-1.0.0-instance",
		CloudletKey: domain.CloudletKey{
			Organization: "cloudlet-org",
			Name:         "cloudlet-name",
		},
	}).Return(nil, fmt.Errorf("resource not found"))

	result, err := planner.Plan(context.Background(), testConfig)
	assert.NoError(t, err)
	assert.NotNil(t, result)
	assert.NotNil(t, result.Plan)
	assert.Equal(t, ActionUpdate, result.Plan.AppAction.Type)
	assert.Contains(t, result.Plan.AppAction.Reason, "Application configuration has changed")
	assert.Len(t, result.Plan.InstanceActions, 1)
	assert.Equal(t, ActionCreate, result.Plan.InstanceActions[0].Type)
	assert.Equal(t, 2, result.Plan.TotalActions)

	mockAppRepo.AssertExpectations(t)
	mockAppInstRepo.AssertExpectations(t)
}

func TestPlanner_Plan_NoChange(t *testing.T) {
	mockAppRepo := new(MockAppRepository)
	mockAppInstRepo := new(MockAppInstanceRepository)
	planner := NewTestPlanner(mockAppRepo, mockAppInstRepo)

	testConfig := &config.EdgeConnectConfig{
		Metadata: config.Metadata{
			Name:         "test-app",
			AppVersion:   "1.0.0",
			Organization: "test-org",
		},
		Spec: config.Spec{
			K8sApp: &config.K8sApp{},
			InfraTemplate: []config.InfraTemplate{
				{
					Region:       "us-west",
					CloudletOrg:  "cloudlet-org",
					CloudletName: "cloudlet-name",
					FlavorName:   "m4.small",
				},
			},
			Network: &config.NetworkConfig{
				OutboundConnections: []config.OutboundConnection{
					{
						Protocol:     "tcp",
						PortRangeMin: 80,
						PortRangeMax: 80,
						RemoteCIDR:   "0.0.0.0/0",
					},
				},
			},
		},
	}

	existingApp := &domain.App{
		Key: domain.AppKey{
			Organization: "test-org",
			Name:         "test-app",
			Version:      "1.0.0",
		},
		Deployment: "kubernetes",
		RequiredOutboundConnections: []domain.SecurityRule{
			{
				Protocol:     "tcp",
				PortRangeMin: 80,
				PortRangeMax: 80,
				RemoteCIDR:   "0.0.0.0/0",
			},
		},
	}

	existingInstance := &domain.AppInstance{
		Key: domain.AppInstanceKey{
			Organization: "test-org",
			Name:         "test-app-1.0.0-instance",
			CloudletKey: domain.CloudletKey{
				Organization: "cloudlet-org",
				Name:         "cloudlet-name",
			},
		},
		AppKey: domain.AppKey{
			Organization: "test-org",
			Name:         "test-app",
			Version:      "1.0.0",
		},
		Flavor: domain.Flavor{Name: "m4.small"},
	}

	// Mock app found
	mockAppRepo.On("ShowApp", mock.Anything, "us-west", domain.AppKey{
		Organization: "test-org",
		Name:         "test-app",
		Version:      "1.0.0",
	}).Return(existingApp, nil)

	// Mock instance found
	mockAppInstRepo.On("ShowAppInstance", mock.Anything, "us-west", domain.AppInstanceKey{
		Organization: "test-org",		Name:         "test-app-1.0.0-instance",
		CloudletKey: domain.CloudletKey{
			Organization: "cloudlet-org",
			Name:         "cloudlet-name",
		},
	}).Return(existingInstance, nil)

	result, err := planner.Plan(context.Background(), testConfig)
	assert.NoError(t, err)
	assert.NotNil(t, result)
	assert.NotNil(t, result.Plan)
	assert.Equal(t, ActionNone, result.Plan.AppAction.Type)
	assert.Equal(t, ActionNone, result.Plan.InstanceActions[0].Type)
	assert.Equal(t, 0, result.Plan.TotalActions)

	mockAppRepo.AssertExpectations(t)
	mockAppInstRepo.AssertExpectations(t)
}

func TestPlanner_Plan_SkipStateCheck(t *testing.T) {
	mockAppRepo := new(MockAppRepository)
	mockAppInstRepo := new(MockAppInstanceRepository)
	planner := NewTestPlanner(mockAppRepo, mockAppInstRepo)

	testConfig := &config.EdgeConnectConfig{
		Metadata: config.Metadata{
			Name:         "test-app",
			AppVersion:   "1.0.0",
			Organization: "test-org",
		},
		Spec: config.Spec{
			K8sApp: &config.K8sApp{},
			InfraTemplate: []config.InfraTemplate{
				{
					Region:       "us-west",
					CloudletOrg:  "cloudlet-org",
					CloudletName: "cloudlet-name",
					FlavorName:   "m4.small",
				},
			},
		},
	}

	opts := DefaultPlanOptions()
	opts.SkipStateCheck = true

	result, err := planner.PlanWithOptions(context.Background(), testConfig, opts)
	assert.NoError(t, err)
	assert.NotNil(t, result)
	assert.NotNil(t, result.Plan)
	assert.Equal(t, ActionCreate, result.Plan.AppAction.Type)
	assert.Equal(t, "Creating app (state check skipped)", result.Plan.AppAction.Reason)
	assert.Len(t, result.Plan.InstanceActions, 1)
	assert.Equal(t, ActionCreate, result.Plan.InstanceActions[0].Type)
	assert.Equal(t, "Creating instance (state check skipped)", result.Plan.InstanceActions[0].Reason)
	assert.Equal(t, 2, result.Plan.TotalActions)

	mockAppRepo.AssertNotCalled(t, "ShowApp", mock.Anything, mock.Anything, mock.Anything)
	mockAppInstRepo.AssertNotCalled(t, "ShowAppInstance", mock.Anything, mock.Anything, mock.Anything)
}

func TestPlanner_Plan_ManifestHashChange(t *testing.T) {
	mockAppRepo := new(MockAppRepository)
	mockAppInstRepo := new(MockAppInstanceRepository)
	planner := NewTestPlanner(mockAppRepo, mockAppInstRepo)

	// Create a temporary manifest file
	tempDir := t.TempDir()
	manifestPath := filepath.Join(tempDir, "manifest.yaml")
	err := os.WriteFile(manifestPath, []byte("new manifest content"), 0644)
	assert.NoError(t, err)

	testConfig := &config.EdgeConnectConfig{
		Metadata: config.Metadata{
			Name:         "test-app",
			AppVersion:   "1.0.0",
			Organization: "test-org",
		},
		Spec: config.Spec{
			K8sApp: &config.K8sApp{
				ManifestFile: manifestPath,
			},
			InfraTemplate: []config.InfraTemplate{
				{
					Region:       "us-west",
					CloudletOrg:  "cloudlet-org",
					CloudletName: "cloudlet-name",
					FlavorName:   "m4.small",
				},
			},
		},
	}

	existingApp := &domain.App{
		Key: domain.AppKey{
			Organization: "test-org",
			Name:         "test-app",
			Version:      "1.0.0",
		},
		Deployment:         "kubernetes",
		DeploymentManifest: "old manifest content", // Different manifest
	}

	// Mock app found
	mockAppRepo.On("ShowApp", mock.Anything, "us-west", domain.AppKey{
		Organization: "test-org",
		Name:         "test-app",
		Version:      "1.0.0",
	}).Return(existingApp, nil)

	// Mock instance found (no change)
	mockAppInstRepo.On("ShowAppInstance", mock.Anything, "us-west", domain.AppInstanceKey{
		Organization: "test-org",
		Name:         "test-app-1.0.0-instance",
		CloudletKey: domain.CloudletKey{
			Organization: "cloudlet-org",
			Name:         "cloudlet-name",
		},
	}).Return(&domain.AppInstance{
		Key: domain.AppInstanceKey{
			Organization: "test-org",
			Name:         "test-app-1.0.0-instance",
			CloudletKey: domain.CloudletKey{
				Organization: "cloudlet-org",
				Name:         "cloudlet-name",
			},
		},
		AppKey: domain.AppKey{
			Organization: "test-org",
			Name:         "test-app",
			Version:      "1.0.0",
		},
		Flavor: domain.Flavor{Name: "m4.small"},
	}, nil)

	result, err := planner.Plan(context.Background(), testConfig)
	assert.NoError(t, err)
	assert.NotNil(t, result)
	assert.NotNil(t, result.Plan)
	assert.Equal(t, ActionUpdate, result.Plan.AppAction.Type)
	assert.True(t, result.Plan.AppAction.ManifestChanged)
	assert.Contains(t, result.Plan.AppAction.Reason, "Application configuration has changed")
	assert.Contains(t, result.Warnings, "Manifest file has changed - instances may need to be recreated")
	assert.Equal(t, 1, result.Plan.TotalActions) // Only app update, instance is ActionNone

	mockAppRepo.AssertExpectations(t)
	mockAppInstRepo.AssertExpectations(t)
}

func TestPlanner_Plan_InstanceFlavorChange(t *testing.T) {
	mockAppRepo := new(MockAppRepository)
	mockAppInstRepo := new(MockAppInstanceRepository)
	planner := NewTestPlanner(mockAppRepo, mockAppInstRepo)

	testConfig := &config.EdgeConnectConfig{
		Metadata: config.Metadata{
			Name:         "test-app",
			AppVersion:   "1.0.0",
			Organization: "test-org",
		},
		Spec: config.Spec{
			K8sApp: &config.K8sApp{},
			InfraTemplate: []config.InfraTemplate{
				{
					Region:       "us-west",
					CloudletOrg:  "cloudlet-org",
					CloudletName: "cloudlet-name",
					FlavorName:   "m4.large", // Changed flavor
				},
			},
		},
	}

	existingApp := &domain.App{
		Key: domain.AppKey{
			Organization: "test-org",
			Name:         "test-app",
			Version:      "1.0.0",
		},
		Deployment: "kubernetes",
	}

	existingInstance := &domain.AppInstance{
		Key: domain.AppInstanceKey{
			Organization: "test-org",
			Name:         "test-app-1.0.0-instance",
			CloudletKey: domain.CloudletKey{
				Organization: "cloudlet-org",
				Name:         "cloudlet-name",
			},
		},
		AppKey: domain.AppKey{
			Organization: "test-org",
			Name:         "test-app",
			Version:      "1.0.0",
		},
		Flavor: domain.Flavor{Name: "m4.small"}, // Old flavor
	}

	// Mock app found
	mockAppRepo.On("ShowApp", mock.Anything, "us-west", domain.AppKey{
		Organization: "test-org",
		Name:         "test-app",
		Version:      "1.0.0",
	}).Return(existingApp, nil)

	// Mock instance found
	mockAppInstRepo.On("ShowAppInstance", mock.Anything, "us-west", domain.AppInstanceKey{
		Organization: "test-org",
		Name:         "test-app-1.0.0-instance",
		CloudletKey: domain.CloudletKey{
			Organization: "cloudlet-org",
			Name:         "cloudlet-name",
		},
	}).Return(existingInstance, nil)

	result, err := planner.Plan(context.Background(), testConfig)
	assert.NoError(t, err)
	assert.NotNil(t, result)
	assert.NotNil(t, result.Plan)
	assert.Equal(t, ActionNone, result.Plan.AppAction.Type)
	assert.Len(t, result.Plan.InstanceActions, 1)
	assert.Equal(t, ActionUpdate, result.Plan.InstanceActions[0].Type)
	assert.Contains(t, result.Plan.InstanceActions[0].Reason, "Instance configuration has changed")
	assert.Contains(t, result.Plan.InstanceActions[0].Changes, "Flavor changed: m4.small -> m4.large")
	assert.Equal(t, 1, result.Plan.TotalActions)

	mockAppRepo.AssertExpectations(t)
	mockAppInstRepo.AssertExpectations(t)
}

func TestPlanner_Plan_MultipleInstances(t *testing.T) {
	mockAppRepo := new(MockAppRepository)
	mockAppInstRepo := new(MockAppInstanceRepository)
	planner := NewTestPlanner(mockAppRepo, mockAppInstRepo)

	testConfig := &config.EdgeConnectConfig{
		Metadata: config.Metadata{
			Name:         "multi-app",
			AppVersion:   "1.0.0",
			Organization: "test-org",
		},
		Spec: config.Spec{
			K8sApp: &config.K8sApp{},
			InfraTemplate: []config.InfraTemplate{
				{
					Region:       "us-west",
					CloudletOrg:  "cloudlet-org-1",
					CloudletName: "cloudlet-name-1",
					FlavorName:   "m4.small",
				},
				{
					Region:       "us-east",
					CloudletOrg:  "cloudlet-org-2",
					CloudletName: "cloudlet-name-2",
					FlavorName:   "m4.medium",
				},
			},
		},
	}

	existingApp := &domain.App{
		Key: domain.AppKey{
			Organization: "test-org",
			Name:         "multi-app",
			Version:      "1.0.0",
		},
		Deployment: "kubernetes",
	}

	// Mock app found
	mockAppRepo.On("ShowApp", mock.Anything, mock.AnythingOfType("string"), domain.AppKey{
		Organization: "test-org",
		Name:         "multi-app",
		Version:      "1.0.0",
	}).Return(existingApp, nil)

	// Mock instance 1 not found
	mockAppInstRepo.On("ShowAppInstance", mock.Anything, "us-west", domain.AppInstanceKey{
		Organization: "test-org",
		Name:         "multi-app-1.0.0-instance",
		CloudletKey: domain.CloudletKey{
			Organization: "cloudlet-org-1",
			Name:         "cloudlet-name-1",
		},
	}).Return(nil, fmt.Errorf("resource not found"))

	// Mock instance 2 found with different flavor
	mockAppInstRepo.On("ShowAppInstance", mock.Anything, "us-east", domain.AppInstanceKey{
		Organization: "test-org",
		Name:         "multi-app-1.0.0-instance",
		CloudletKey: domain.CloudletKey{
			Organization: "cloudlet-org-2",
			Name:         "cloudlet-name-2",
		},
	}).Return(&domain.AppInstance{
		Key: domain.AppInstanceKey{
			Organization: "test-org",
			Name:         "multi-app-1.0.0-instance",
			CloudletKey: domain.CloudletKey{
				Organization: "cloudlet-org-2",
				Name:         "cloudlet-name-2",
			},
		},
		AppKey: domain.AppKey{
			Organization: "test-org",
			Name:         "multi-app",
			Version:      "1.0.0",
		},
		Flavor: domain.Flavor{Name: "m4.small"}, // Different flavor
	}, nil)

	result, err := planner.Plan(context.Background(), testConfig)
	assert.NoError(t, err)
	assert.NotNil(t, result)
	assert.NotNil(t, result.Plan)
	assert.Equal(t, ActionNone, result.Plan.AppAction.Type)
	assert.Len(t, result.Plan.InstanceActions, 2)
	assert.Equal(t, ActionCreate, result.Plan.InstanceActions[0].Type) // Instance 1 is new
	assert.Equal(t, ActionUpdate, result.Plan.InstanceActions[1].Type) // Instance 2 has flavor change
	assert.Contains(t, result.Plan.InstanceActions[1].Changes, "Flavor changed: m4.small -> m4.medium")
	assert.Equal(t, 2, result.Plan.TotalActions)

	mockAppRepo.AssertExpectations(t)
	mockAppInstRepo.AssertExpectations(t)
}

func TestPlanner_Plan_AppQueryError(t *testing.T) {
	mockAppRepo := new(MockAppRepository)
	mockAppInstRepo := new(MockAppInstanceRepository)
	planner := NewTestPlanner(mockAppRepo, mockAppInstRepo)

	testConfig := &config.EdgeConnectConfig{
		Metadata: config.Metadata{
			Name:         "test-app",
			AppVersion:   "1.0.0",
			Organization: "test-org",
		},
		Spec: config.Spec{
			K8sApp: &config.K8sApp{},
			InfraTemplate: []config.InfraTemplate{
				{
					Region:       "us-west",
					CloudletOrg:  "cloudlet-org",
					CloudletName: "cloudlet-name",
					FlavorName:   "m4.small",
				},
			},
		},
	}

	// Mock app query error
	mockAppRepo.On("ShowApp", mock.Anything, "us-west", domain.AppKey{
		Organization: "test-org",
		Name:         "test-app",
		Version:      "1.0.0",
	}).Return(nil, fmt.Errorf("network error"))

	result, err := planner.Plan(context.Background(), testConfig)
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "failed to query current app state: network error")
	assert.NotNil(t, result)

	mockAppRepo.AssertExpectations(t)
	mockAppInstRepo.AssertExpectations(t)
}

func TestPlanner_Plan_InstanceQueryError(t *testing.T) {
	mockAppRepo := new(MockAppRepository)
	mockAppInstRepo := new(MockAppInstanceRepository)
	planner := NewTestPlanner(mockAppRepo, mockAppInstRepo)

	testConfig := &config.EdgeConnectConfig{
		Metadata: config.Metadata{
			Name:         "test-app",
			AppVersion:   "1.0.0",
			Organization: "test-org",
		},
		Spec: config.Spec{
			K8sApp: &config.K8sApp{},
			InfraTemplate: []config.InfraTemplate{
				{
					Region:       "us-west",
					CloudletOrg:  "cloudlet-org",
					CloudletName: "cloudlet-name",
					FlavorName:   "m4.small",
				},
			},
		},
	}

	existingApp := &domain.App{
		Key: domain.AppKey{
			Organization: "test-org",
			Name:         "test-app",
			Version:      "1.0.0",
		},
		Deployment: "kubernetes",
	}

	// Mock app found
	mockAppRepo.On("ShowApp", mock.Anything, "us-west", domain.AppKey{
		Organization: "test-org",
		Name:         "test-app",
		Version:      "1.0.0",
	}).Return(existingApp, nil)

	// Mock instance query error
	mockAppInstRepo.On("ShowAppInstance", mock.Anything, "us-west", domain.AppInstanceKey{
		Organization: "test-org",
		Name:         "test-app-1.0.0-instance",
		CloudletKey: domain.CloudletKey{
			Organization: "cloudlet-org",
			Name:         "cloudlet-name",
		},
	}).Return(nil, fmt.Errorf("database error"))

	result, err := planner.Plan(context.Background(), testConfig)
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "failed to query current instance state: database error")
	assert.NotNil(t, result)

	mockAppRepo.AssertExpectations(t)
	mockAppInstRepo.AssertExpectations(t)
}

func TestPlanner_Plan_ManifestFileError(t *testing.T) {
	mockAppRepo := new(MockAppRepository)
	mockAppInstRepo := new(MockAppInstanceRepository)
	planner := NewTestPlanner(mockAppRepo, mockAppInstRepo)

	testConfig := &config.EdgeConnectConfig{
		Metadata: config.Metadata{
			Name:         "test-app",
			AppVersion:   "1.0.0",
			Organization: "test-org",
		},
		Spec: config.Spec{
			K8sApp: &config.K8sApp{
				ManifestFile: "/non/existent/path/manifest.yaml", // Invalid path
			},
			InfraTemplate: []config.InfraTemplate{
				{
					Region:       "us-west",
					CloudletOrg:  "cloudlet-org",
					CloudletName: "cloudlet-name",
					FlavorName:   "m4.small",
				},
			},
		},
	}

	result, err := planner.Plan(context.Background(), testConfig)
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "failed to calculate manifest hash: failed to open manifest file")
	assert.NotNil(t, result)

	mockAppRepo.AssertNotCalled(t, "ShowApp", mock.Anything, mock.Anything, mock.Anything)
	mockAppInstRepo.AssertNotCalled(t, "ShowAppInstance", mock.Anything, mock.Anything, mock.Anything)
}

func TestPlanner_Plan_AppTypeChange(t *testing.T) {
	mockAppRepo := new(MockAppRepository)
	mockAppInstRepo := new(MockAppInstanceRepository)
	planner := NewTestPlanner(mockAppRepo, mockAppInstRepo)

	testConfig := &config.EdgeConnectConfig{
		Metadata: config.Metadata{
			Name:         "test-app",
			AppVersion:   "1.0.0",
			Organization: "test-org",
		},
		        Spec: config.Spec{
		            DockerApp: &config.DockerApp{},
		            InfraTemplate: []config.InfraTemplate{
		                {
		                    Region:       "us-west",
		                    CloudletOrg:  "cloudlet-org",
		                    CloudletName: "cloudlet-name",
		                    FlavorName:   "m4.small",
		                },
		            },
		        },
		    }
		
		    existingApp := &domain.App{
		        Key: domain.AppKey{
		            Organization: "test-org",
		            Name:         "test-app",
		            Version:      "1.0.0",
		        },
		        Deployment: "kubernetes", // Current is kubernetes
		    }
	// Mock app found
	mockAppRepo.On("ShowApp", mock.Anything, "us-west", domain.AppKey{
		Organization: "test-org",
		Name:         "test-app",
		Version:      "1.0.0",
	}).Return(existingApp, nil)

	// Mock instance found (no change)
	mockAppInstRepo.On("ShowAppInstance", mock.Anything, "us-west", domain.AppInstanceKey{
		Organization: "test-org",
		Name:         "test-app-1.0.0-instance",
		CloudletKey: domain.CloudletKey{
			Organization: "cloudlet-org",
			Name:         "cloudlet-name",
		},
	}).Return(&domain.AppInstance{
		Key: domain.AppInstanceKey{
			Organization: "test-org",
			Name:         "test-app-1.0.0-instance",
			CloudletKey: domain.CloudletKey{
				Organization: "cloudlet-org",
				Name:         "cloudlet-name",
			},
		},
		AppKey: domain.AppKey{
			Organization: "test-org",
			Name:         "test-app",
			Version:      "1.0.0",
		},
		Flavor: domain.Flavor{Name: "m4.small"},
	}, nil)

	result, err := planner.Plan(context.Background(), testConfig)
	assert.NoError(t, err)
	assert.NotNil(t, result)
	assert.NotNil(t, result.Plan)
	assert.Equal(t, ActionUpdate, result.Plan.AppAction.Type)
	assert.Contains(t, result.Plan.AppAction.Changes, "App type changed: KUBERNETES -> DOCKER")
	assert.Equal(t, 1, result.Plan.TotalActions) // Only app update, instance is ActionNone

	mockAppRepo.AssertExpectations(t)
	mockAppInstRepo.AssertExpectations(t)
}