package automation

import (
	"context"
	"testing"

	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/nexuscrm/shared/pkg/models"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/mock"
)

// MockMetadataServiceForAction mocks metadata service for action tests
type MockMetadataServiceForAction struct {
	mock.Mock
	actions []*models.ActionMetadata
}

func (m *MockMetadataServiceForAction) GetActionByID(ctx context.Context, id string) *models.ActionMetadata {
	for _, action := range m.actions {
		if action.ID == id {
			return action
		}
	}
	return nil
}

// MockPersistenceForAction mocks persistence for action tests
type MockPersistenceForAction struct {
	mock.Mock
}

func (m *MockPersistenceForAction) Insert(ctx context.Context, objectName string, data models.SObject, user *models.UserSession) (models.SObject, error) {
	args := m.Called(ctx, objectName, data, user)
	if args.Get(0) == nil {
		return nil, args.Error(1)
	}
	return args.Get(0).(models.SObject), args.Error(1)
}

func (m *MockPersistenceForAction) Update(ctx context.Context, objectName, id string, data models.SObject, user *models.UserSession) error {
	args := m.Called(ctx, objectName, id, data, user)
	return args.Error(0)
}

func (m *MockPersistenceForAction) Delete(ctx context.Context, objectName, id string, user *models.UserSession) error {
	args := m.Called(ctx, objectName, id, user)
	return args.Error(0)
}

func (m *MockPersistenceForAction) GetRaw(ctx context.Context, objectName, id string) (models.SObject, error) {
	args := m.Called(ctx, objectName, id)
	if args.Get(0) == nil {
		return nil, args.Error(1)
	}
	return args.Get(0).(models.SObject), args.Error(1)
}

func (m *MockPersistenceForAction) GetLock(ctx context.Context, tableName, id string) (models.SObject, error) {
	args := m.Called(ctx, tableName, id)
	if args.Get(0) == nil {
		return nil, args.Error(1)
	}
	return args.Get(0).(models.SObject), args.Error(1)
}

func (m *MockPersistenceForAction) RunInTransaction(ctx context.Context, fn func(context.Context) error) error {
	return fn(ctx)
}
func (m *MockPersistenceForAction) DetachTx(ctx context.Context) context.Context { return ctx }

func TestActionService_ExecuteAction_NotFound(t *testing.T) {
	// Arrange
	mockMeta := &MockMetadataServiceForAction{
		actions: []*models.ActionMetadata{},
	}
	ctx := context.Background()
	user := &models.UserSession{ID: "user-1", Name: "Test User"}
	record := models.SObject{"Id": "record-1"}

	// Create a minimal ActionService for testing findAction behavior
	// Note: We can't create a full ActionService without all dependencies,
	// but we can test the error path by checking the behavior
	action := mockMeta.GetActionByID(ctx, "non-existent")

	// Assert
	assert.Nil(t, action, "Expected nil for non-existent action")
	_ = user
	_ = record
}

func TestActionService_ActionContext_Initialization(t *testing.T) {
	// Arrange
	user := &models.UserSession{ID: "user-1", Name: "Test User"}
	record := models.SObject{"Id": "record-1", "Name": "Test Record"}

	// Act
	actionCtx := &ActionContext{
		Record:  record,
		User:    user,
		Results: make(map[string]map[string]interface{}),
	}

	// Assert
	assert.NotNil(t, actionCtx)
	assert.Equal(t, "record-1", actionCtx.Record["Id"])
	assert.Equal(t, "user-1", actionCtx.User.ID)
	assert.Empty(t, actionCtx.Results)
}

func TestActionService_ActionContext_StoresResults(t *testing.T) {
	// Arrange
	actionCtx := &ActionContext{
		Record:  models.SObject{"Id": "record-1"},
		User:    &models.UserSession{ID: "user-1"},
		Results: make(map[string]map[string]interface{}),
	}

	// Act - simulate storing results from a step
	actionCtx.Results["step-1"] = map[string]interface{}{
		"id":     "new-record-123",
		"status": "Created",
	}

	// Assert
	assert.Len(t, actionCtx.Results, 1)
	assert.Equal(t, "new-record-123", actionCtx.Results["step-1"]["id"])
}

func TestActionService_ActionTypeSwitch(t *testing.T) {
	// Test that we recognize all supported action types
	supportedTypes := []string{
		constants.ActionTypeCreateRecord,
		constants.ActionTypeUpdateRecord,
		constants.ActionTypeSendEmail,
		constants.ActionTypeCallWebhook,
		constants.ActionTypeComposite,
	}

	for _, actionType := range supportedTypes {
		assert.NotEmpty(t, actionType, "Action type should not be empty")
	}
}

func TestMockPersistence_Insert(t *testing.T) {
	// Arrange
	mockPersistence := &MockPersistenceForAction{}
	ctx := context.Background()
	user := &models.UserSession{ID: "user-1"}
	record := models.SObject{"Name": "Test"}

	expectedResult := models.SObject{"Id": "new-123", "Name": "Test"}
	mockPersistence.On("Insert", ctx, "Account", record, user).Return(expectedResult, nil)

	// Act
	result, err := mockPersistence.Insert(ctx, "Account", record, user)

	// Assert
	assert.NoError(t, err)
	assert.Equal(t, "new-123", result["Id"])
	mockPersistence.AssertExpectations(t)
}

func TestMockPersistence_Update(t *testing.T) {
	// Arrange
	mockPersistence := &MockPersistenceForAction{}
	ctx := context.Background()
	user := &models.UserSession{ID: "user-1"}
	updates := models.SObject{"Status": "Closed"}

	mockPersistence.On("Update", ctx, "Account", "acc-123", updates, user).Return(nil)

	// Act
	err := mockPersistence.Update(ctx, "Account", "acc-123", updates, user)

	// Assert
	assert.NoError(t, err)
	mockPersistence.AssertExpectations(t)
}
