package records

import (
	"context"
	"strings"
	"testing"

	eventFeature "github.com/nexuscrm/events"
	"github.com/nexuscrm/kernel/pkg/formula"
	"github.com/nexuscrm/kernel/pkg/validation"
	metadataFeature "github.com/nexuscrm/metadata"
	"github.com/nexuscrm/permissions"
	"github.com/nexuscrm/shared/pkg/constants"
	sharedEvents "github.com/nexuscrm/shared/pkg/events"
	"github.com/nexuscrm/shared/pkg/models"
)

type finalLookupValidationRepo struct {
	captureRecordRepo
	records  map[string]map[string]models.SObject
	maxCalls int
}

func (r *finalLookupValidationRepo) FindOne(ctx context.Context, tableName string, id string) (models.SObject, error) {
	if r.records == nil || r.records[tableName] == nil {
		return nil, nil
	}
	return cloneSObject(r.records[tableName][id]), nil
}

func (r *finalLookupValidationRepo) MaxNumericFieldForParent(ctx context.Context, schema *models.ObjectMetadata, targetField, parentField string, parentValue interface{}) (float64, error) {
	r.maxCalls++
	return 0, nil
}

func TestInsertRevalidatesNonPolymorphicLookupsAfterBeforeSaveFlow(t *testing.T) {
	parentField := "ParentId"
	childSchema := &models.ObjectMetadata{
		ID:           "obj_child",
		APIName:      "ChildObject",
		SharingModel: constants.SharingModelPublicReadWrite,
		Fields: []models.FieldMetadata{
			{APIName: constants.FieldID, Type: constants.FieldTypeString, Createable: false, Updateable: false},
			{APIName: constants.FieldName, Type: constants.FieldTypeString, Createable: true, Updateable: true},
			{APIName: "ParentId", Type: constants.FieldTypeReference, ReferenceTo: []string{"ParentObject"}, Createable: true, Updateable: true},
			{APIName: "SortOrder__c", Type: models.FieldType("integer"), Createable: true, Updateable: true, SiblingMaxParentField: &parentField},
		},
	}
	parentSchema := &models.ObjectMetadata{
		ID:           "obj_parent",
		APIName:      "ParentObject",
		SharingModel: constants.SharingModelPublicReadWrite,
		Fields: []models.FieldMetadata{
			{APIName: constants.FieldID, Type: constants.FieldTypeString, Createable: false, Updateable: false},
			{APIName: constants.FieldName, Type: constants.FieldTypeString, Createable: true, Updateable: true},
		},
	}
	makeFieldsMap(childSchema)
	makeFieldsMap(parentSchema)

	repo := &finalLookupValidationRepo{
		records: map[string]map[string]models.SObject{
			"ParentObject": {
				"parent-ok": {constants.FieldID: "parent-ok", constants.FieldName: "Visible parent"},
			},
		},
	}
	metaSvc := metadataFeature.NewMetadataService(&stubUpsertMetaRepo{schemas: []*models.ObjectMetadata{childSchema, parentSchema}}, nil, nil)
	permSvc := permissions.NewPermissionService(&stubUpsertPermRepo{grantCreate: true, grantEdit: true}, metaSvc, nil)
	ps := &PersistenceService{
		repo:                repo,
		rollup:              noopRollupProcessor{},
		metadata:            metaSvc,
		permissions:         permSvc,
		eventBus:            &eventFeature.NoOpEventBus{},
		validator:           metadataFeature.NewValidationService(formula.NewEngine()),
		schemaValidator:     validation.NewSchemaValidator(),
		txManager:           inlineTxManager{},
		parentLaneAdmission: &fakeParentLaneAdmissionController{},
	}
	ps.eventBus = mutatingRecordEventBus{mutate: func(eventType sharedEvents.EventType, record models.SObject) {
		if eventType == sharedEvents.RecordBeforeCreate {
			record["ParentId"] = "missing-parent"
		}
	}}

	_, err := ps.Insert(context.Background(), childSchema.APIName, models.SObject{
		constants.FieldName: "line",
		"ParentId":          "parent-ok",
	}, recordEntrypointUser())
	if err == nil {
		t.Fatal("Insert accepted before-save lookup mutation to a missing parent")
	}
	if !strings.Contains(err.Error(), "referenced ParentObject record") {
		t.Fatalf("Insert error = %v; want final parent lookup validation failure", err)
	}
	if repo.maxCalls != 0 {
		t.Fatalf("MaxNumericFieldForParent calls = %d, want 0 before validating final parent", repo.maxCalls)
	}
	if repo.inserted != nil {
		t.Fatalf("Insert persisted after invalid final parent: %#v", repo.inserted)
	}
}
