package metadata

import (
	"context"
	"strings"
	"testing"

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

type batchRollupMetadataRepo struct {
	ports.MetadataRepository
	schemas map[string]*models.ObjectMetadata
}

func (r *batchRollupMetadataRepo) GetSchemaByAPIName(_ context.Context, apiName string) (*models.ObjectMetadata, error) {
	if r == nil {
		return nil, nil
	}
	return r.schemas[strings.ToLower(strings.TrimSpace(apiName))], nil
}

func (r *batchRollupMetadataRepo) GetAllSchemas(_ context.Context) ([]*models.ObjectMetadata, error) {
	out := make([]*models.ObjectMetadata, 0, len(r.schemas))
	for _, schema := range r.schemas {
		out = append(out, schema)
	}
	return out, nil
}

func (r *batchRollupMetadataRepo) GetLayouts(context.Context, string) ([]*models.PageLayout, error) {
	return nil, nil
}

type batchRollupSchemaRepo struct {
	ports.SchemaRepository
	batchCalls int
}

func (r *batchRollupSchemaRepo) BatchAddColumns(string, []models.FieldMetadata) error {
	r.batchCalls++
	return nil
}

func TestBatchCreateFieldsRollupCycleRejected(t *testing.T) {
	objectA := &models.ObjectMetadata{
		ID:      "obj_a",
		APIName: "ObjectA",
		Fields: []models.FieldMetadata{
			{
				APIName: "Total",
				Type:    constants.FieldTypeRollupSummary,
				RollupConfig: &models.RollupConfig{
					CalcType:          "SUM",
					SummaryObject:     "ObjectB",
					SummaryField:      "Amount",
					RelationshipField: "ObjectAId",
				},
			},
			{
				APIName:       "ObjectBId",
				Type:          constants.FieldTypeReference,
				ReferenceTo:   []string{"ObjectB"},
				Createable:    true,
				Updateable:    true,
				IsPolymorphic: false,
			},
		},
	}
	objectB := &models.ObjectMetadata{
		ID:      "obj_b",
		APIName: "ObjectB",
		Fields: []models.FieldMetadata{
			{APIName: "Amount", Type: constants.FieldTypeCurrency},
			{APIName: "ObjectAId", Type: constants.FieldTypeReference, ReferenceTo: []string{"ObjectA"}},
		},
	}

	metaRepo := &batchRollupMetadataRepo{
		schemas: map[string]*models.ObjectMetadata{
			"objecta": objectA,
			"objectb": objectB,
		},
	}
	schemaRepo := &batchRollupSchemaRepo{}
	ms := NewMetadataService(metaRepo, NewSchemaManager(schemaRepo), nil)

	_, err := ms.BatchCreateFields(context.Background(), "ObjectB", []models.FieldMetadata{{
		APIName: "BackRef",
		Label:   "Back Ref",
		Type:    constants.FieldTypeRollupSummary,
		RollupConfig: &models.RollupConfig{
			CalcType:          "SUM",
			SummaryObject:     "ObjectA",
			SummaryField:      "Total",
			RelationshipField: "ObjectBId",
		},
	}})
	if err == nil {
		t.Fatal("BatchCreateFields accepted rollup cycle, want rejection")
	}
	if !strings.Contains(strings.ToLower(err.Error()), "cycle") {
		t.Fatalf("error = %v, want cycle diagnostic", err)
	}
	if schemaRepo.batchCalls != 0 {
		t.Fatalf("BatchAddColumns calls = %d, want 0 on rejected cycle", schemaRepo.batchCalls)
	}
}

func TestBatchCreateFieldsNonRollupSkipsExistingRollupGraphDrift(t *testing.T) {
	objectA := &models.ObjectMetadata{
		ID:      "obj_a",
		APIName: "ObjectA",
		Fields: []models.FieldMetadata{
			{
				APIName: "Total",
				Type:    constants.FieldTypeRollupSummary,
				RollupConfig: &models.RollupConfig{
					CalcType:          "SUM",
					SummaryObject:     "ObjectB",
					SummaryField:      "Amount",
					RelationshipField: "ObjectAId",
				},
			},
			{
				APIName:       "ObjectBId",
				Type:          constants.FieldTypeReference,
				ReferenceTo:   []string{"ObjectB"},
				Createable:    true,
				Updateable:    true,
				IsPolymorphic: false,
			},
		},
	}
	objectB := &models.ObjectMetadata{
		ID:      "obj_b",
		APIName: "ObjectB",
		Fields: []models.FieldMetadata{
			{APIName: "Amount", Type: constants.FieldTypeCurrency},
			{APIName: "ObjectAId", Type: constants.FieldTypeReference, ReferenceTo: []string{"ObjectA"}},
		},
	}

	metaRepo := &batchRollupMetadataRepo{
		schemas: map[string]*models.ObjectMetadata{
			"objecta": objectA,
			"objectb": objectB,
		},
	}
	schemaRepo := &batchRollupSchemaRepo{}
	ms := NewMetadataService(metaRepo, NewSchemaManager(schemaRepo), nil)

	created, err := ms.BatchCreateFields(context.Background(), "ObjectB", []models.FieldMetadata{{
		ID:      "caller-controlled",
		APIName: "Notes",
		Label:   "Notes",
		Type:    constants.FieldTypeString,
	}})
	if err != nil {
		t.Fatalf("BatchCreateFields(non-rollup) returned error: %v", err)
	}
	if len(created) != 1 || created[0].APIName != "Notes" {
		t.Fatalf("created fields = %+v, want Notes", created)
	}
	if created[0].ID == "" || created[0].ID == "caller-controlled" {
		t.Fatalf("created field incarnation = %q, want fresh owner-minted identity", created[0].ID)
	}
	if schemaRepo.batchCalls != 1 {
		t.Fatalf("BatchAddColumns calls = %d, want 1 for non-rollup batch", schemaRepo.batchCalls)
	}
}
