package rest

import (
	"context"
	"encoding/json"
	"testing"

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

func TestRelationshipDeleteSemantics_PrefersRelationshipMetadata(t *testing.T) {
	t.Parallel()

	field := models.FieldMetadata{
		APIName:        "AccountId",
		Type:           constants.FieldTypeReference,
		IsMasterDetail: false,
	}
	rel := &models.Relationship{
		FieldAPIName:     "AccountId",
		IsSharingParent:  true,
		CascadeDelete:    false,
		RestrictedDelete: true,
	}

	cascadeDelete, restrictedDelete := relationshipDeleteSemantics(field, rel)
	if cascadeDelete {
		t.Fatalf("expected relationship metadata to keep cascadeDelete=false")
	}
	if !restrictedDelete {
		t.Fatalf("expected relationship metadata to propagate restrictedDelete=true")
	}
}

func TestRelationshipDeleteSemantics_FallsBackToFieldMetadata(t *testing.T) {
	t.Parallel()

	restrict := constants.DeleteRuleRestrict
	field := models.FieldMetadata{
		APIName:        "ParentId",
		Type:           constants.FieldTypeMasterDetail,
		IsMasterDetail: true,
		DeleteRule:     &restrict,
	}

	cascadeDelete, restrictedDelete := relationshipDeleteSemantics(field, nil)
	if !cascadeDelete {
		t.Fatalf("expected master-detail fallback to set cascadeDelete=true")
	}
	if !restrictedDelete {
		t.Fatalf("expected DeleteRule fallback to set restrictedDelete=true")
	}
}

// TestCampaignMemberStatusRuntimePicklist verifies that the CampaignMember Status field
// correctly returns FilteredLookupInfo in the describe response for runtime picklist filtering
func TestCampaignMemberStatusRuntimePicklist(t *testing.T) {
	// Create a mock schema with CampaignMember and CampaignMemberStatus objects
	validator := runtimeScopedPicklistValidator
	controllerName := "CampaignId"

	campaignMemberStatusSchema := &models.ObjectMetadata{
		APIName: "CampaignMemberStatus",
		Fields: []models.FieldMetadata{
			{APIName: "Name", Type: constants.FieldTypeString, IsNameField: true},
			{APIName: "Label", Type: constants.FieldTypeString},
			{APIName: "CampaignId", Type: constants.FieldTypeReference},
			{APIName: "SortOrder", Type: constants.FieldTypeDouble},
			{APIName: "IsDefault", Type: constants.FieldTypeBoolean},
		},
	}

	campaignMemberSchema := &models.ObjectMetadata{
		APIName: "CampaignMember",
		Fields: []models.FieldMetadata{
			{APIName: "Id", Type: constants.FieldTypeString},
			{APIName: "CampaignId", Type: constants.FieldTypeReference, Label: "Campaign"},
			{APIName: "Status", Type: constants.FieldTypePicklist, Label: "Status",
				Validator: &validator, ControllingField: &controllerName,
				ReferenceTo: []string{"CampaignMemberStatus"}},
			{APIName: "LeadId", Type: constants.FieldTypeReference, Label: "Lead"},
			{APIName: "ContactId", Type: constants.FieldTypeReference, Label: "Contact"},
		},
	}

	// Mock schema lookup
	mockSchemaLookup := &mockSchemaLookup{
		schemas: map[string]*models.ObjectMetadata{
			"CampaignMember":       campaignMemberSchema,
			"CampaignMemberStatus": campaignMemberStatusSchema,
		},
	}

	// Create the runtime picklist info as would be done in the describe handler
	field := campaignMemberSchema.Fields[2] // Status field
	runtimePicklist := buildRuntimePicklistInfo(context.Background(), mockSchemaLookup, field)

	// Verify that runtime picklist info was built correctly
	require.NotNil(t, runtimePicklist)
	assert.Equal(t, "CampaignMemberStatus", runtimePicklist.SourceObject)
	assert.Equal(t, "Label", runtimePicklist.ValueField) // Should find Label as the label field
	assert.Equal(t, "Label", runtimePicklist.LabelField)
	assert.Equal(t, "SortOrder", runtimePicklist.SortField) // Should find SortOrder as sort field
	assert.Equal(t, "ASC", runtimePicklist.SortOrder)
	assert.Len(t, runtimePicklist.FilterMappings, 1)
	assert.Equal(t, "CampaignId", runtimePicklist.FilterMappings[0].SourceField)
	assert.Equal(t, "CampaignId", runtimePicklist.FilterMappings[0].RecordField)

	// Now verify that FilteredLookupInfo can be serialized to JSON properly
	filteredLookupInfo := FilteredLookupInfo{RuntimePicklist: runtimePicklist}
	jsonBytes, err := json.Marshal(filteredLookupInfo)
	require.NoError(t, err)

	// Verify the JSON contains the expected structure
	var result map[string]interface{}
	err = json.Unmarshal(jsonBytes, &result)
	require.NoError(t, err)

	// Check that RuntimePicklist exists and has the expected properties
	runtimePicklistData, ok := result["runtimePicklist"].(map[string]interface{})
	require.True(t, ok)
	assert.Equal(t, "CampaignMemberStatus", runtimePicklistData["sourceObject"])
	assert.Equal(t, "Label", runtimePicklistData["valueField"])
	assert.Equal(t, "Label", runtimePicklistData["labelField"])
	assert.Equal(t, "SortOrder", runtimePicklistData["sortField"])

	// Verify filter mappings
	filterMappings, ok := runtimePicklistData["filterMappings"].([]interface{})
	require.True(t, ok)
	require.Len(t, filterMappings, 1)

	mapping := filterMappings[0].(map[string]interface{})
	assert.Equal(t, "CampaignId", mapping["sourceField"])
	assert.Equal(t, "CampaignId", mapping["recordField"])
}

// mockSchemaLookup implements the schemaLookup interface for testing
type mockSchemaLookup struct {
	schemas map[string]*models.ObjectMetadata
}

func (m *mockSchemaLookup) GetSchema(_ context.Context, apiName string) *models.ObjectMetadata {
	return m.schemas[apiName]
}
