// ABOUTME: Tests for EdgeConnect deletion manager with mock scenarios
// ABOUTME: Tests deletion execution and error handling with mock clients
package v2

import (
	"context"
	"fmt"
	"testing"
	"time"

	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"
)

// MockResourceClient for testing deletion manager
type MockResourceClient struct {
	mock.Mock
}

func (m *MockResourceClient) 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 *MockResourceClient) 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 *MockResourceClient) DeleteApp(ctx context.Context, appKey v2.AppKey, region string) error {
	args := m.Called(ctx, appKey, region)
	return args.Error(0)
}

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

// TestLogger implements Logger interface for testing
type TestLogger struct {
	messages []string
}

func (l *TestLogger) Printf(format string, v ...interface{}) {
	l.messages = append(l.messages, fmt.Sprintf(format, v...))
}

func TestNewResourceManager(t *testing.T) {
	mockClient := &MockResourceClient{}
	manager := NewResourceManager(mockClient)

	assert.NotNil(t, manager)
}

func TestWithLogger(t *testing.T) {
	mockClient := &MockResourceClient{}
	logger := &TestLogger{}

	manager := NewResourceManager(mockClient, WithLogger(logger))

	// Cast to implementation to check logger was set
	impl := manager.(*EdgeConnectResourceManager)
	assert.Equal(t, logger, impl.logger)
}

func createTestDeletionPlan() *DeletionPlan {
	return &DeletionPlan{
		ConfigName: "test-deletion",
		AppToDelete: &AppDeletion{
			Name:         "test-app",
			Version:      "1.0.0",
			Organization: "testorg",
			Region:       "US",
		},
		InstancesToDelete: []InstanceDeletion{
			{
				Name:         "test-app-1.0.0-instance",
				Organization: "testorg",
				Region:       "US",
				CloudletOrg:  "cloudletorg",
				CloudletName: "cloudlet1",
			},
		},
		TotalActions:      2,
		EstimatedDuration: 10 * time.Second,
	}
}

func TestExecuteDeletion_Success(t *testing.T) {
	mockClient := &MockResourceClient{}
	logger := &TestLogger{}
	manager := NewResourceManager(mockClient, WithLogger(logger))

	plan := createTestDeletionPlan()

	// Mock successful deletion operations
	mockClient.On("DeleteAppInstance", mock.Anything, mock.AnythingOfType("v2.AppInstanceKey"), "US").
		Return(nil)
	mockClient.On("DeleteApp", mock.Anything, mock.AnythingOfType("v2.AppKey"), "US").
		Return(nil)

	ctx := context.Background()
	result, err := manager.ExecuteDeletion(ctx, plan)

	require.NoError(t, err)
	require.NotNil(t, result)
	assert.True(t, result.Success)
	assert.Len(t, result.CompletedActions, 2) // 1 instance + 1 app
	assert.Len(t, result.FailedActions, 0)

	mockClient.AssertExpectations(t)
}

func TestExecuteDeletion_InstanceDeleteFails(t *testing.T) {
	mockClient := &MockResourceClient{}
	logger := &TestLogger{}
	manager := NewResourceManager(mockClient, WithLogger(logger))

	plan := createTestDeletionPlan()

	// Mock instance deletion failure
	mockClient.On("DeleteAppInstance", mock.Anything, mock.AnythingOfType("v2.AppInstanceKey"), "US").
		Return(fmt.Errorf("instance deletion failed"))

	ctx := context.Background()
	result, err := manager.ExecuteDeletion(ctx, plan)

	require.Error(t, err)
	require.NotNil(t, result)
	assert.False(t, result.Success)
	assert.Len(t, result.FailedActions, 1)

	mockClient.AssertExpectations(t)
}

func TestExecuteDeletion_OnlyInstances(t *testing.T) {
	mockClient := &MockResourceClient{}
	logger := &TestLogger{}
	manager := NewResourceManager(mockClient, WithLogger(logger))

	plan := &DeletionPlan{
		ConfigName:  "test-deletion",
		AppToDelete: nil, // No app to delete
		InstancesToDelete: []InstanceDeletion{
			{
				Name:         "test-app-1.0.0-instance",
				Organization: "testorg",
				Region:       "US",
				CloudletOrg:  "cloudletorg",
				CloudletName: "cloudlet1",
			},
		},
		TotalActions:      1,
		EstimatedDuration: 5 * time.Second,
	}

	// Mock successful instance deletion
	mockClient.On("DeleteAppInstance", mock.Anything, mock.AnythingOfType("v2.AppInstanceKey"), "US").
		Return(nil)

	ctx := context.Background()
	result, err := manager.ExecuteDeletion(ctx, plan)

	require.NoError(t, err)
	require.NotNil(t, result)
	assert.True(t, result.Success)
	assert.Len(t, result.CompletedActions, 1)

	mockClient.AssertExpectations(t)
}

func TestExecuteDeletion_EmptyPlan(t *testing.T) {
	mockClient := &MockResourceClient{}
	manager := NewResourceManager(mockClient)

	plan := &DeletionPlan{
		ConfigName:        "test-deletion",
		AppToDelete:       nil,
		InstancesToDelete: []InstanceDeletion{},
		TotalActions:      0,
	}

	ctx := context.Background()
	result, err := manager.ExecuteDeletion(ctx, plan)

	require.NoError(t, err)
	require.NotNil(t, result)
	assert.True(t, result.Success)
	assert.Len(t, result.CompletedActions, 0)
	assert.Len(t, result.FailedActions, 0)
}
