package catalogsnapshot_test

import (
	"context"
	"testing"

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

const legacyParityRevision catalogsnapshot.CatalogRevision = 73

type legacyParityRepository struct {
	ports.MetadataRepository
	schemas       []*models.ObjectMetadata
	relationships []*models.Relationship
	rules         map[string][]*models.SharingRule
}

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

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

func (r *legacyParityRepository) GetAllFlowDefinitions(context.Context) ([]*models.FlowDefinition, error) {
	return nil, nil
}

func (r *legacyParityRepository) GetAllValidationRules(context.Context) ([]*models.ValidationRule, error) {
	return nil, nil
}

func (r *legacyParityRepository) GetSharingRules(_ context.Context, objectAPIName string) ([]*models.SharingRule, error) {
	return r.rules[objectAPIName], nil
}

type legacyParityDecorator struct{}

func (legacyParityDecorator) DecorateSchema(_ context.Context, schema *models.ObjectMetadata) (*models.ObjectMetadata, error) {
	if schema == nil || schema.APIName != "Entry" {
		return schema, nil
	}
	return models.CloneWithAdditionalFields(schema, []models.FieldMetadata{{
		ID: "field-decorated", APIName: "Decorated", Label: "Decorated",
		Type: constants.FieldTypeBoolean, IsVirtual: true, VirtualKind: "derived-test",
	}}), nil
}

// TestSelectedRootParityAgainstCurrentMetadataService proves that the P7b
// artifact preserves the effective (post-decorator, post-relationship-build)
// facts returned by today's in-process source. The adapter exists only in this
// test file; production has no path from MetadataService into the new store.
func TestSelectedRootParityAgainstCurrentMetadataService(t *testing.T) {
	parent := &models.ObjectMetadata{
		ID: "obj-container", APIName: "Container", Label: "Container", PluralLabel: "Containers",
		SharingModel: constants.SharingModelPrivate,
		Fields:       []models.FieldMetadata{{ID: "container-id", APIName: "Id", Type: constants.FieldTypeString, PhysicalName: "id"}},
	}
	child := &models.ObjectMetadata{
		ID: "obj-entry", APIName: "Entry", Label: "Entry", PluralLabel: "Entries",
		SharingModel: constants.SharingModelControlledByParent,
		Fields: []models.FieldMetadata{
			{ID: "entry-id", APIName: "Id", Type: constants.FieldTypeString, PhysicalName: "id"},
			{ID: "entry-title", APIName: "Title", Type: constants.FieldTypeString, PhysicalName: "entry_title"},
			{ID: "entry-container", APIName: "ContainerId", Type: constants.FieldTypeReference, PhysicalName: "container_id", ReferenceTo: []string{"Container"}, IsMasterDetail: true},
		},
	}
	repository := &legacyParityRepository{
		schemas: []*models.ObjectMetadata{child, parent},
		relationships: []*models.Relationship{{
			ID: "rel-container", ChildObjectAPIName: "Entry", ParentObjectAPIName: "Container",
			FieldAPIName: "ContainerId", RelationshipName: "Container", RelationshipType: "masterdetail", IsSharingParent: true,
		}},
		rules: map[string][]*models.SharingRule{
			"Entry": {{ID: "rule-entry-read", ObjectAPIName: "Entry", Name: "Readers", AccessLevel: constants.PermRead}},
		},
	}
	legacy := metadata.NewMetadataService(repository, nil, nil)
	legacy.RegisterSchemaDecorator(legacyParityDecorator{})
	if err := legacy.RefreshCache(t.Context()); err != nil {
		t.Fatalf("refresh current MetadataService: %v", err)
	}

	effectiveObjects := legacy.GetSchemas(t.Context())
	input := catalogsnapshot.CompileInput{
		OrgID:   "org-parity",
		Lineage: catalogsnapshot.Lineage{CatalogRevision: legacyParityRevision, SchemaGeneration: 9, EngineGeneration: 3},
		Projections: []catalogsnapshot.RuntimeProjectionInput{{
			CatalogRevision:   legacyParityRevision,
			AppInstallationID: "01900000-0000-7000-8000-000000000073",
			CapabilityID:      "runtime.records.read-selected-root.v1",
			Profile:           catalogsnapshot.ProjectionSelectedRootReadV1,
			ObjectAPINames:    []string{"Container", "Entry"},
		}},
	}
	parity := catalogsnapshot.ParityInput{Objects: effectiveObjects}
	seenRelationship := map[string]bool{}
	for _, object := range effectiveObjects {
		input.Objects = append(input.Objects, catalogsnapshot.ObjectInput{CatalogRevision: legacyParityRevision, Metadata: object})
		for _, relationship := range legacy.GetRelationships(t.Context(), object.APIName) {
			key := relationship.ChildObjectAPIName + "\x00" + relationship.ParentObjectAPIName + "\x00" + relationship.FieldAPIName
			if seenRelationship[key] {
				continue
			}
			seenRelationship[key] = true
			input.Relationships = append(input.Relationships, catalogsnapshot.RelationshipInput{CatalogRevision: legacyParityRevision, Metadata: relationship})
			parity.Relationships = append(parity.Relationships, relationship)
		}
		for _, rule := range legacy.GetSharingRules(t.Context(), object.APIName) {
			input.SharingRules = append(input.SharingRules, catalogsnapshot.SharingRuleInput{CatalogRevision: legacyParityRevision, Metadata: rule})
			parity.SharingRules = append(parity.SharingRules, rule)
		}
	}

	snapshotArtifact, err := catalogsnapshot.CompileCatalogSnapshot(input)
	if err != nil {
		t.Fatalf("compile current effective graph: %v", err)
	}
	snapshot, err := catalogsnapshot.OpenCatalogSnapshot(snapshotArtifact)
	if err != nil {
		t.Fatalf("open snapshot: %v", err)
	}
	viewArtifact, err := catalogsnapshot.ProjectRuntimeMetadataView(snapshot, catalogsnapshot.ProjectionKey{
		AppInstallationID: input.Projections[0].AppInstallationID,
		CapabilityID:      input.Projections[0].CapabilityID,
	})
	if err != nil {
		t.Fatalf("project Runtime view: %v", err)
	}
	view, err := catalogsnapshot.OpenRuntimeMetadataView(viewArtifact)
	if err != nil {
		t.Fatalf("open Runtime view: %v", err)
	}
	if report := catalogsnapshot.CompareRuntimeView(view, parity); !report.Equivalent() {
		t.Fatalf("current-source parity differences: %#v", report.Differences)
	}
	effectiveEntry, ok := view.Object("Entry")
	if !ok || models.FindField(effectiveEntry, "Decorated") == nil {
		t.Fatal("post-decorator effective field was not preserved")
	}
	if relationship, ok := view.SharingParentRelationship("Entry"); !ok || relationship.ParentObjectAPIName != "Container" {
		t.Fatalf("effective sharing-parent relationship = %#v, %v", relationship, ok)
	}
}
