package metadata

import (
	"context"
	"testing"

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

func TestMetadataService_ConvertedLookups_ReturnsStandardLeadAnchors(t *testing.T) {
	ms, metaRepo, _, _ := newTestService()
	ctx := context.Background()

	metaRepo.schemas["lead"] = &models.ObjectMetadata{
		APIName: "Lead",
		Fields: []models.FieldMetadata{
			{APIName: "Id", Type: constants.FieldTypeString},
			{APIName: "Company", Type: constants.FieldTypeString},
			{APIName: "ConvertedAccountId", Type: constants.FieldTypeReference, ReferenceTo: []string{"Account"}},
			{APIName: "ConvertedContactId", Type: constants.FieldTypeReference, ReferenceTo: []string{"Contact"}},
			{APIName: "ConvertedOpportunityId", Type: constants.FieldTypeReference, ReferenceTo: []string{"Opportunity"}},
		},
	}

	anchors := ms.ConvertedLookups(ctx, "Lead")
	require.Len(t, anchors, 3)
	assert.Equal(t, "ConvertedAccountId", anchors[0].APIName)
	assert.Equal(t, []string{"Account"}, anchors[0].ReferenceTo)
	assert.Equal(t, "ConvertedContactId", anchors[1].APIName)
	assert.Equal(t, "ConvertedOpportunityId", anchors[2].APIName)
}

func TestMetadataService_ConvertedLookups_PicksUpTenantCustomAnchor(t *testing.T) {
	// Load-bearing claim: a tenant adding a custom Converted*__c lookup on
	// Lead must be picked up by the flow's anchor iteration with zero code
	// change.
	ms, metaRepo, _, _ := newTestService()
	ctx := context.Background()

	metaRepo.schemas["lead"] = &models.ObjectMetadata{
		APIName: "Lead",
		Fields: []models.FieldMetadata{
			{APIName: "ConvertedAccountId", Type: constants.FieldTypeReference, ReferenceTo: []string{"Account"}},
			{APIName: "ConvertedPartnerOrg__c", Type: constants.FieldTypeReference, ReferenceTo: []string{"PartnerOrg__c"}},
		},
	}

	anchors := ms.ConvertedLookups(ctx, "Lead")
	require.Len(t, anchors, 2)
	assert.Equal(t, "ConvertedPartnerOrg__c", anchors[1].APIName)
	assert.Equal(t, []string{"PartnerOrg__c"}, anchors[1].ReferenceTo)
}

func TestMetadataService_ConvertedLookups_SkipsFieldsWithoutReferenceTo(t *testing.T) {
	// A field named ConvertedFooId but not a lookup (ReferenceTo empty) is
	// not an anchor — exclude it so the flow doesn't try to relink records.
	ms, metaRepo, _, _ := newTestService()
	ctx := context.Background()

	metaRepo.schemas["lead"] = &models.ObjectMetadata{
		APIName: "Lead",
		Fields: []models.FieldMetadata{
			{APIName: "ConvertedAccountId", Type: constants.FieldTypeReference, ReferenceTo: []string{"Account"}},
			{APIName: "ConvertedNotesId", Type: constants.FieldTypeString},
		},
	}

	anchors := ms.ConvertedLookups(ctx, "Lead")
	require.Len(t, anchors, 1)
	assert.Equal(t, "ConvertedAccountId", anchors[0].APIName)
}

func TestMetadataService_ConvertedLookups_RejectsShortNames(t *testing.T) {
	// "ConvertedId" (no target segment) and "Converted" alone must not be
	// mistaken for anchors.
	ms, metaRepo, _, _ := newTestService()
	ctx := context.Background()

	metaRepo.schemas["lead"] = &models.ObjectMetadata{
		APIName: "Lead",
		Fields: []models.FieldMetadata{
			{APIName: "ConvertedId", Type: constants.FieldTypeReference, ReferenceTo: []string{"Anything"}},
			{APIName: "Converted__c", Type: constants.FieldTypeReference, ReferenceTo: []string{"Anything__c"}},
			{APIName: "Converted", Type: constants.FieldTypeReference, ReferenceTo: []string{"Anything"}},
			{APIName: "ConvertedAccountId", Type: constants.FieldTypeReference, ReferenceTo: []string{"Account"}},
			{APIName: "ConvertedPartnerOrg__c", Type: constants.FieldTypeReference, ReferenceTo: []string{"PartnerOrg__c"}},
		},
	}

	anchors := ms.ConvertedLookups(ctx, "Lead")
	require.Len(t, anchors, 2)
	assert.Equal(t, "ConvertedAccountId", anchors[0].APIName)
	assert.Equal(t, "ConvertedPartnerOrg__c", anchors[1].APIName)
}

func TestMetadataService_ConvertedLookups_EmptyForNonConvertObject(t *testing.T) {
	ms, metaRepo, _, _ := newTestService()
	ctx := context.Background()

	metaRepo.schemas["account"] = &models.ObjectMetadata{
		APIName: "Account",
		Fields: []models.FieldMetadata{
			{APIName: "Id", Type: constants.FieldTypeString},
			{APIName: "Name", Type: constants.FieldTypeString},
		},
	}

	assert.Empty(t, ms.ConvertedLookups(ctx, "Account"))
}

func TestMetadataService_ConvertedLookups_NilForUnknownObject(t *testing.T) {
	ms, _, _, _ := newTestService()
	assert.Nil(t, ms.ConvertedLookups(context.Background(), "NoSuchObject"))
}

func TestMetadataService_ConvertedStateMetadata_UsesConvertedShape(t *testing.T) {
	ms, metaRepo, _, _ := newTestService()
	ctx := context.Background()

	metaRepo.schemas["lead"] = &models.ObjectMetadata{
		APIName: "Lead",
		Fields: []models.FieldMetadata{
			{APIName: "IsConverted", Type: constants.FieldTypeBoolean},
			{APIName: "ConvertedDate", Type: constants.FieldTypeDate},
			{APIName: "ConvertedAccountId", Type: constants.FieldTypeReference, ReferenceTo: []string{"Account"}},
			{APIName: "ConvertedPartnerOrg__c", Type: constants.FieldTypeReference, ReferenceTo: []string{"PartnerOrg__c"}},
			{APIName: constants.FieldLastModifiedByID, Type: constants.FieldTypeReference, ReferenceTo: []string{constants.TableUser}},
		},
	}

	descriptor := ms.ConvertedStateMetadata(ctx, "Lead")
	require.NotNil(t, descriptor)
	assert.Equal(t, "IsConverted", descriptor.StateField.APIName)
	require.NotNil(t, descriptor.DateField)
	assert.Equal(t, "ConvertedDate", descriptor.DateField.APIName)
	require.NotNil(t, descriptor.ActorField)
	assert.Equal(t, constants.FieldLastModifiedByID, descriptor.ActorField.APIName)
	require.Len(t, descriptor.LookupFields, 2)
	assert.Equal(t, "ConvertedAccountId", descriptor.LookupFields[0].APIName)
	assert.Equal(t, "ConvertedPartnerOrg__c", descriptor.LookupFields[1].APIName)
}

func TestMetadataService_ConvertedStateMetadata_RequiresStateAndLookup(t *testing.T) {
	ms, metaRepo, _, _ := newTestService()
	ctx := context.Background()

	metaRepo.schemas["nostate"] = &models.ObjectMetadata{
		APIName: "NoState",
		Fields: []models.FieldMetadata{
			{APIName: "ConvertedAccountId", Type: constants.FieldTypeReference, ReferenceTo: []string{"Account"}},
		},
	}
	metaRepo.schemas["nolookup"] = &models.ObjectMetadata{
		APIName: "NoLookup",
		Fields: []models.FieldMetadata{
			{APIName: "IsConverted", Type: constants.FieldTypeBoolean},
		},
	}

	assert.Nil(t, ms.ConvertedStateMetadata(ctx, "NoState"))
	assert.Nil(t, ms.ConvertedStateMetadata(ctx, "NoLookup"))
}
