// ABOUTME: Tests for EdgeConnect deletion planner with mock scenarios
// ABOUTME: Tests deletion planning logic and resource discovery
package v2

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

	"edp.buildth.ing/DevFW-CICD/edge-connect-client/v2/internal/config"
	v2 "edp.buildth.ing/DevFW-CICD/edge-connect-client/v2/sdk/edgeconnect/v2"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/mock"
	"github.com/stretchr/testify/require"
)

// MockEdgeConnectClient is a mock implementation of the EdgeConnect client
type MockEdgeConnectClient struct {
	mock.Mock
}

func (m *MockEdgeConnectClient) ShowApp(ctx context.Context, appKey v2.AppKey, region string) (v2.App, error) {
	args := m.Called(ctx, appKey, region)
	if args.Get(0) == nil {
		return v2.App{}, args.Error(1)
	}
	return args.Get(0).(v2.App), args.Error(1)
}

func (m *MockEdgeConnectClient) ShowAppInstances(ctx context.Context, instanceKey v2.AppInstanceKey, appKey v2.AppKey, region string) ([]v2.AppInstance, error) {
	args := m.Called(ctx, instanceKey, region)
	if args.Get(0) == nil {
		return nil, args.Error(1)
	}
	return args.Get(0).([]v2.AppInstance), args.Error(1)
}

func (m *MockEdgeConnectClient) DeleteApp(ctx context.Context, appKey v2.AppKey, region string) error {
	args := m.Called(ctx, appKey, region)
	return args.Error(0)
}

func (m *MockEdgeConnectClient) DeleteAppInstance(ctx context.Context, instanceKey v2.AppInstanceKey, region string) error {
	args := m.Called(ctx, instanceKey, region)
	return args.Error(0)
}

func createTestConfig(t *testing.T) *config.EdgeConnectConfig {
	// Create temporary manifest file
	tempDir := t.TempDir()
	manifestFile := filepath.Join(tempDir, "test-manifest.yaml")
	manifestContent := "apiVersion: v1\nkind: Pod\nmetadata:\n  name: test\n"
	err := os.WriteFile(manifestFile, []byte(manifestContent), 0644)
	require.NoError(t, err)

	return &config.EdgeConnectConfig{
		Kind: "edgeconnect-deployment",
		Metadata: config.Metadata{
			Name:         "test-app",
			AppVersion:   "1.0.0",
			Organization: "testorg",
		},
		Spec: config.Spec{
			K8sApp: &config.K8sApp{
				ManifestFile: manifestFile,
			},
			InfraTemplate: []config.InfraTemplate{
				{
					Region:       "US",
					CloudletOrg:  "TestCloudletOrg",
					CloudletName: "TestCloudlet",
					FlavorName:   "small",
				},
			},
		},
	}
}

func TestNewPlanner(t *testing.T) {
	mockClient := &MockEdgeConnectClient{}
	planner := NewPlanner(mockClient)

	assert.NotNil(t, planner)
}

func TestPlanDeletion_WithExistingResources(t *testing.T) {
	mockClient := &MockEdgeConnectClient{}
	planner := NewPlanner(mockClient)
	testConfig := createTestConfig(t)

	// Mock existing app
	existingApp := v2.App{
		Key: v2.AppKey{
			Organization: "testorg",
			Name:         "test-app",
			Version:      "1.0.0",
		},
		Deployment: "kubernetes",
	}

	// Mock existing instances
	existingInstances := []v2.AppInstance{
		{
			Key: v2.AppInstanceKey{
				Organization: "testorg",
				Name:         "test-app-1.0.0-instance",
				CloudletKey: v2.CloudletKey{
					Organization: "TestCloudletOrg",
					Name:         "TestCloudlet",
				},
			},
			AppKey: v2.AppKey{
				Organization: "testorg",
				Name:         "test-app",
				Version:      "1.0.0",
			},
		},
	}

	mockClient.On("ShowApp", mock.Anything, mock.AnythingOfType("v2.AppKey"), "US").
		Return(existingApp, nil)

	mockClient.On("ShowAppInstances", mock.Anything, mock.AnythingOfType("v2.AppInstanceKey"), "US").
		Return(existingInstances, nil)

	ctx := context.Background()
	result, err := planner.Plan(ctx, testConfig)

	require.NoError(t, err)
	require.NotNil(t, result)
	require.NotNil(t, result.Plan)

	plan := result.Plan
	assert.Equal(t, "test-app", plan.ConfigName)
	assert.NotNil(t, plan.AppToDelete)
	assert.Equal(t, "test-app", plan.AppToDelete.Name)
	assert.Equal(t, "1.0.0", plan.AppToDelete.Version)
	assert.Equal(t, "testorg", plan.AppToDelete.Organization)

	require.Len(t, plan.InstancesToDelete, 1)
	assert.Equal(t, "test-app-1.0.0-instance", plan.InstancesToDelete[0].Name)
	assert.Equal(t, "testorg", plan.InstancesToDelete[0].Organization)

	assert.Equal(t, 2, plan.TotalActions) // 1 app + 1 instance
	assert.False(t, plan.IsEmpty())

	mockClient.AssertExpectations(t)
}

func TestPlanDeletion_NoResourcesExist(t *testing.T) {
	mockClient := &MockEdgeConnectClient{}
	planner := NewPlanner(mockClient)
	testConfig := createTestConfig(t)

	// Mock API calls to return "not found" errors
	mockClient.On("ShowApp", mock.Anything, mock.AnythingOfType("v2.AppKey"), "US").
		Return(v2.App{}, &v2.APIError{StatusCode: 404, Messages: []string{"App not found"}})

	mockClient.On("ShowAppInstances", mock.Anything, mock.AnythingOfType("v2.AppInstanceKey"), "US").
		Return([]v2.AppInstance{}, nil)

	ctx := context.Background()
	result, err := planner.Plan(ctx, testConfig)

	require.NoError(t, err)
	require.NotNil(t, result)
	require.NotNil(t, result.Plan)

	plan := result.Plan
	assert.Equal(t, "test-app", plan.ConfigName)
	assert.Nil(t, plan.AppToDelete)
	assert.Len(t, plan.InstancesToDelete, 0)
	assert.Equal(t, 0, plan.TotalActions)
	assert.True(t, plan.IsEmpty())

	mockClient.AssertExpectations(t)
}

func TestPlanDeletion_OnlyInstancesExist(t *testing.T) {
	mockClient := &MockEdgeConnectClient{}
	planner := NewPlanner(mockClient)
	testConfig := createTestConfig(t)

	// Mock existing instances but no app
	existingInstances := []v2.AppInstance{
		{
			Key: v2.AppInstanceKey{
				Organization: "testorg",
				Name:         "test-app-1.0.0-instance",
				CloudletKey: v2.CloudletKey{
					Organization: "TestCloudletOrg",
					Name:         "TestCloudlet",
				},
			},
		},
	}

	mockClient.On("ShowApp", mock.Anything, mock.AnythingOfType("v2.AppKey"), "US").
		Return(v2.App{}, &v2.APIError{StatusCode: 404, Messages: []string{"App not found"}})

	mockClient.On("ShowAppInstances", mock.Anything, mock.AnythingOfType("v2.AppInstanceKey"), "US").
		Return(existingInstances, nil)

	ctx := context.Background()
	result, err := planner.Plan(ctx, testConfig)

	require.NoError(t, err)
	require.NotNil(t, result)
	require.NotNil(t, result.Plan)

	plan := result.Plan
	assert.Nil(t, plan.AppToDelete)
	assert.Len(t, plan.InstancesToDelete, 1)
	assert.Equal(t, 1, plan.TotalActions)
	assert.False(t, plan.IsEmpty())

	mockClient.AssertExpectations(t)
}
