package automation

import (
	"context"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"

	"github.com/nexuscrm/shared/pkg/models"
)

func TestPreviewRecordCreateFields_StdConvertLeadCampaignAttributionUsesFlowMetadata(t *testing.T) {
	lead := models.SObject{
		"Id":        "lead-001",
		"FirstName": "Ada",
		"LastName":  "Lovelace",
		"Company":   "Acme",
		"Email":     "ada@example.com",
	}
	query := &seedQueryService{
		lead:    lead,
		account: models.SObject{"Id": "acct-default", "Name": "Acme"},
		records: map[string][]models.SObject{
			"CampaignMember|LeadId = 'lead-001'": {
				{"Id": "cm-new", "CampaignId": "camp-new"},
			},
		},
	}
	persist := &recordingPersistence{}
	executor := &FlowExecutor{
		persistence:   persist,
		query:         query,
		fieldMappings: copyFieldsApplier{},
		flowVersionFn: loadStandardFlowPackForTest,
	}

	result, err := executor.PreviewRecordCreateFields(
		stdConvertLeadActionContext(),
		"Std_ConvertLead",
		map[string]interface{}{
			"leadId":          "lead-001",
			"convertedStatus": "Converted",
			"accountId":       "acct-default",
			"opportunityName": "Acme Opportunity",
		},
		[]FlowRecordCreateFieldPreviewTarget{{
			ObjectAPIName: "Opportunity",
			FieldAPIName:  "CampaignId",
		}},
		nil,
	)

	require.NoError(t, err)
	require.Len(t, result.RecordCreates, 1)
	assert.Equal(t, "CreateOpportunity", result.RecordCreates[0].ElementName)
	assert.Equal(t, "Opportunity", result.RecordCreates[0].ObjectAPIName)
	assert.Equal(t, "camp-new", result.RecordCreates[0].Fields["CampaignId"])
	assert.Empty(t, persist.committedOps, "preview must not perform DML")
	assert.Empty(t, persist.stagedOps, "preview must not perform DML")
	assert.Contains(t, query.calls, seedQueryCall{
		object:    "CampaignMember",
		filter:    "LeadId = 'lead-001'",
		sortField: "CreatedDate",
		sortDir:   "DESC",
		limit:     1,
	}, "preview must reach Campaign attribution through the Flow recordLookup metadata")
}

func TestPreviewRecordCreateFields_StdConvertLeadNoCampaignReturnsEmptyField(t *testing.T) {
	lead := models.SObject{
		"Id":        "lead-001",
		"FirstName": "Ada",
		"LastName":  "Lovelace",
		"Company":   "Acme",
	}
	query := &seedQueryService{
		lead:    lead,
		account: models.SObject{"Id": "acct-default", "Name": "Acme"},
		records: map[string][]models.SObject{
			"CampaignMember|LeadId = 'lead-001'": {},
		},
	}
	persist := &recordingPersistence{}
	executor := &FlowExecutor{
		persistence:   persist,
		query:         query,
		fieldMappings: copyFieldsApplier{},
		flowVersionFn: loadStandardFlowPackForTest,
	}

	result, err := executor.PreviewRecordCreateFields(
		stdConvertLeadActionContext(),
		"Std_ConvertLead",
		map[string]interface{}{
			"leadId":          "lead-001",
			"convertedStatus": "Converted",
			"accountId":       "acct-default",
			"opportunityName": "Acme Opportunity",
		},
		[]FlowRecordCreateFieldPreviewTarget{{
			ObjectAPIName: "Opportunity",
			FieldAPIName:  "CampaignId",
		}},
		nil,
	)

	require.NoError(t, err)
	require.Len(t, result.RecordCreates, 1)
	assert.NotContains(t, result.RecordCreates[0].Fields, "CampaignId")
	assert.Empty(t, persist.committedOps, "preview must not perform DML")
	assert.Empty(t, persist.stagedOps, "preview must not perform DML")
}

func TestPreviewRecordCreateFields_RecordUpdateAssignmentsFailClosed(t *testing.T) {
	executor := &FlowExecutor{
		flowVersionFn: func(context.Context, string, *models.UserSession) (*models.FlowVersion, error) {
			return &models.FlowVersion{
				Id: "fv-preview-fail-closed",
				Metadata: models.FlowMetadata{
					Start: &models.FlowStart{Connector: &models.FlowConnector{TargetReference: "UpdateBeforePreview"}},
					RecordUpdates: []models.FlowRecordUpdate{{
						Name:           "UpdateBeforePreview",
						Object:         "Account",
						InputReference: "accountId",
						InputAssignments: []models.FlowInputAssignment{{
							Field: "Name",
							Value: "Changed",
						}},
						Connector: &models.FlowConnector{TargetReference: "CreateTarget"},
					}},
					RecordCreates: []models.FlowRecordCreate{{
						Name:   "CreateTarget",
						Object: "Opportunity",
					}},
				},
			}, nil
		},
	}

	_, err := executor.PreviewRecordCreateFields(
		context.Background(),
		"PreviewFailClosed",
		map[string]interface{}{"accountId": "acct-001"},
		[]FlowRecordCreateFieldPreviewTarget{{
			ObjectAPIName: "Opportunity",
			FieldAPIName:  "Name",
		}},
		nil,
	)

	require.Error(t, err)
	assert.Contains(t, err.Error(), `recordUpdate "UpdateBeforePreview" requires DML or assignment semantics`)
}

func TestPreviewRecordCreateFields_RecordUpdateCollectionInputFailsClosed(t *testing.T) {
	executor := &FlowExecutor{
		fieldMappings: copyFieldsApplier{},
		flowVersionFn: func(context.Context, string, *models.UserSession) (*models.FlowVersion, error) {
			return &models.FlowVersion{
				Id: "fv-preview-collection-fail-closed",
				Metadata: models.FlowMetadata{
					Start: &models.FlowStart{Connector: &models.FlowConnector{TargetReference: "UpdateCollectionBeforePreview"}},
					RecordUpdates: []models.FlowRecordUpdate{{
						Name:           "UpdateCollectionBeforePreview",
						Object:         "Account",
						InputReference: "accounts",
						FieldMapping: &models.FlowFieldMappingRef{
							SourceObject:    "Lead",
							SourceReference: "lead",
							TargetObject:    "Account",
							ActionType:      "convert",
						},
						Connector: &models.FlowConnector{TargetReference: "CreateTarget"},
					}},
					RecordCreates: []models.FlowRecordCreate{{
						Name:   "CreateTarget",
						Object: "Opportunity",
					}},
				},
			}, nil
		},
	}

	_, err := executor.PreviewRecordCreateFields(
		context.Background(),
		"PreviewCollectionFailClosed",
		map[string]interface{}{
			"lead":     models.SObject{},
			"accounts": []models.SObject{{"Id": "acct-001"}},
		},
		[]FlowRecordCreateFieldPreviewTarget{{
			ObjectAPIName: "Opportunity",
			FieldAPIName:  "Name",
		}},
		nil,
	)

	require.Error(t, err)
	assert.Contains(t, err.Error(), `recordUpdate "UpdateCollectionBeforePreview" requires DML or assignment semantics`)
}

func TestNewRecordCreatePreviewTargetSetValidation(t *testing.T) {
	_, err := newRecordCreatePreviewTargetSet(nil)
	require.Error(t, err)
	assert.Contains(t, err.Error(), "at least one recordCreate preview target is required")

	_, err = newRecordCreatePreviewTargetSet([]FlowRecordCreateFieldPreviewTarget{{
		ObjectAPIName: " ",
		FieldAPIName:  "Name",
	}})
	require.Error(t, err)
	assert.Contains(t, err.Error(), "objectApiName and fieldApiName")

	targetSet, err := newRecordCreatePreviewTargetSet([]FlowRecordCreateFieldPreviewTarget{
		{ObjectAPIName: " Opportunity ", FieldAPIName: " CampaignId "},
		{ObjectAPIName: "Opportunity", FieldAPIName: "CampaignId"},
	})
	require.NoError(t, err)
	assert.Equal(t, 1, targetSet.total)
	fields, ok := targetSet.fieldsForObject("Opportunity")
	require.True(t, ok)
	_, ok = fields["CampaignId"]
	assert.True(t, ok)
}

func TestPreviewRecordCreateFields_FlowNotFound(t *testing.T) {
	executor := &FlowExecutor{
		flowVersionFn: func(context.Context, string, *models.UserSession) (*models.FlowVersion, error) {
			return nil, nil
		},
	}

	_, err := executor.PreviewRecordCreateFields(
		context.Background(),
		"MissingFlow",
		map[string]interface{}{},
		[]FlowRecordCreateFieldPreviewTarget{{ObjectAPIName: "Opportunity", FieldAPIName: "Name"}},
		nil,
	)

	require.Error(t, err)
	assert.Contains(t, err.Error(), "MissingFlow")
}

func TestPreviewRecordCreateFields_EntryConditionsNotMetReturnsEmptyResult(t *testing.T) {
	executor := &FlowExecutor{
		flowVersionFn: func(context.Context, string, *models.UserSession) (*models.FlowVersion, error) {
			return &models.FlowVersion{
				Id: "fv-preview-entry-filter",
				Metadata: models.FlowMetadata{
					Start: &models.FlowStart{
						Object:      "Lead",
						FilterLogic: "and",
						Filters: []models.FlowRecordFilter{{
							Field:    "Status",
							Operator: "EqualTo",
							Value:    "Ready",
						}},
						Connector: &models.FlowConnector{TargetReference: "CreateTarget"},
					},
					RecordCreates: []models.FlowRecordCreate{{
						Name:   "CreateTarget",
						Object: "Opportunity",
						InputAssignments: []models.FlowInputAssignment{{
							Field: "Name",
							Value: "Should Not Preview",
						}},
					}},
				},
			}, nil
		},
	}

	result, err := executor.PreviewRecordCreateFields(
		context.Background(),
		"EntryFilteredFlow",
		map[string]interface{}{"$Record": models.SObject{"Status": "Not Ready"}},
		[]FlowRecordCreateFieldPreviewTarget{{ObjectAPIName: "Opportunity", FieldAPIName: "Name"}},
		nil,
	)

	require.NoError(t, err)
	require.NotNil(t, result)
	assert.Empty(t, result.RecordCreates)
}
