package metadata

import (
	"context"
	"testing"

	"github.com/nexuscrm/kernel/pkg/ports"
	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/nexuscrm/shared/pkg/models"
)

// metadata_sharing_test exercises GetParentControlledObject — the single SSoT
// predicate consumed by every layer that asks "is this object parent-
// controlled?". Tests inject a metadataSnapshot directly into ms.coreCache so
// ensureCoreCacheInitialized's fast path returns without hitting the DB.

func makeServiceWithRelationships(rels map[string][]*models.Relationship) *MetadataService {
	snap := &metadataSnapshot{relationshipsMap: rels}
	ms := &MetadataService{}
	ms.coreCache.Store(snap)
	return ms
}

func TestGetParentControlledObjectReturnsParentForFullyConfiguredSchema(t *testing.T) {
	ms := makeServiceWithRelationships(map[string][]*models.Relationship{
		"opportunitylineitem": {
			{
				ChildObjectAPIName:  "OpportunityLineItem",
				ParentObjectAPIName: "Opportunity",
				FieldAPIName:        "OpportunityId",
				IsSharingParent:     true,
			},
		},
	})

	schema := &models.ObjectMetadata{APIName: "OpportunityLineItem", SharingModel: constants.SharingModelControlledByParent}
	if got := ms.GetParentControlledObject(t.Context(), schema); got != "Opportunity" {
		t.Fatalf("GetParentControlledObject = %q; want Opportunity", got)
	}
}

func TestGetParentControlledObjectFallsThroughWithoutRelationship(t *testing.T) {
	ms := makeServiceWithRelationships(map[string][]*models.Relationship{}) // no rels at all

	schema := &models.ObjectMetadata{APIName: "Lonely", SharingModel: constants.SharingModelControlledByParent}
	if got := ms.GetParentControlledObject(t.Context(), schema); got != "" {
		t.Fatalf("malformed PC schema (no rel) should return \"\"; got %q", got)
	}
}

func TestGetParentControlledObjectFallsThroughWithoutSharingModel(t *testing.T) {
	ms := makeServiceWithRelationships(map[string][]*models.Relationship{
		"detail": {
			{ChildObjectAPIName: "Detail", ParentObjectAPIName: "Master", IsSharingParent: true, FieldAPIName: "MasterId"},
		},
	})

	// Relationship present but SharingModel is Private — predicate must fail.
	schema := &models.ObjectMetadata{APIName: "Detail", SharingModel: constants.SharingModelPrivate}
	if got := ms.GetParentControlledObject(t.Context(), schema); got != "" {
		t.Fatalf("schema with relationship but Private sharing should return \"\"; got %q", got)
	}
}

func TestGetParentControlledObjectDerivesSharingParentFromFieldMetadata(t *testing.T) {
	ms := NewMetadataService(&metadataSharingRepo{
		schemas: []*models.ObjectMetadata{
			{
				APIName:      "Detail",
				SharingModel: constants.SharingModelControlledByParent,
				Fields: []models.FieldMetadata{
					{
						APIName:          "MasterId",
						Type:             constants.FieldTypeMasterDetail,
						IsMasterDetail:   true,
						ReferenceTo:      []string{"Master"},
						RelationshipName: identityTestStringPtr("Master"),
					},
				},
			},
			{APIName: "Master"},
		},
	}, nil, nil)

	schema := ms.GetSchema(t.Context(), "Detail")
	if schema == nil {
		t.Fatal("GetSchema(Detail) = nil")
	}
	if got := ms.GetParentControlledObject(t.Context(), schema); got != "Master" {
		t.Fatalf("GetParentControlledObject derived parent = %q, want Master", got)
	}
}

type metadataSharingRepo struct {
	ports.MetadataRepository
	schemas []*models.ObjectMetadata
}

func (r *metadataSharingRepo) GetAllSchemas(context.Context) ([]*models.ObjectMetadata, error) {
	return r.schemas, nil
}

func (r *metadataSharingRepo) GetAllRelationships(context.Context) ([]*models.Relationship, error) {
	return nil, nil
}
