package metadata

import (
	"reflect"
	"strings"
	"testing"

	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/nexuscrm/shared/pkg/models"
	"github.com/pingcap-inc/nexuscrm-contract/pkg/metadatair"
)

func TestLowerObjectComponentHeadersProjectsEquivalentObjectFieldIdentity(t *testing.T) {
	schema := &models.ObjectMetadata{
		APIName: "WorkItem",
		Label:   "Work Item",
		Fields: []models.FieldMetadata{
			{APIName: "Summary", Label: "Summary", Type: constants.FieldTypeString},
			{APIName: "Status", Label: "Status", Type: constants.FieldTypeString},
		},
	}

	doc, err := LowerObjectComponentHeaders(schema, testObjectComponentIRSource())
	if err != nil {
		t.Fatalf("LowerObjectComponentHeaders() error = %v", err)
	}

	got, err := ProjectObjectFieldIdentityHeaders(doc)
	if err != nil {
		t.Fatalf("ProjectObjectFieldIdentityHeaders() error = %v", err)
	}
	want := ObjectFieldIdentityProjection{
		ObjectAPIName: "WorkItem",
		FieldAPINames: []string{"Summary", "Status"},
	}
	if !reflect.DeepEqual(got, want) {
		t.Fatalf("projected identity = %#v, want %#v", got, want)
	}
	if got := len(doc.Nodes); got != 3 {
		t.Fatalf("lowered node count = %d, want 3", got)
	}
	if node := doc.Nodes[0]; node.Kind != metadatair.NodeKindObject || node.ID != "WorkItem" || node.Source.Path != "/" {
		t.Fatalf("object node = %#v", node)
	}
	if node := doc.Nodes[2]; node.Kind != metadatair.NodeKindField || node.ID != "WorkItem.Status" || node.Source.Path != "/fields/Status" {
		t.Fatalf("field node = %#v", node)
	}
}

func TestLowerObjectComponentHeadersFailsClosedOnDuplicateFieldIdentity(t *testing.T) {
	schema := &models.ObjectMetadata{
		APIName: "WorkItem",
		Fields: []models.FieldMetadata{
			{APIName: "Status", Type: constants.FieldTypeString},
			{APIName: "Status", Type: constants.FieldTypeString},
		},
	}

	_, err := LowerObjectComponentHeaders(schema, testObjectComponentIRSource())
	if err == nil {
		t.Fatal("LowerObjectComponentHeaders() error = nil, want duplicate rejection")
	}
	if !strings.Contains(err.Error(), "duplicate node identity field:WorkItem.Status") {
		t.Fatalf("LowerObjectComponentHeaders() error = %q, want duplicate identity", err.Error())
	}
}

func TestLowerObjectComponentHeadersRequiresPackOwnerForPackSource(t *testing.T) {
	source := testObjectComponentIRSource()
	source.Owner = metadatair.Owner{Layer: metadatair.OwnerLayerL1}

	_, err := LowerObjectComponentHeaders(&models.ObjectMetadata{APIName: "WorkItem"}, source)
	if err == nil {
		t.Fatal("LowerObjectComponentHeaders() error = nil, want owner-pack rejection")
	}
	if !strings.Contains(err.Error(), "L1 owner requires an owner pack") {
		t.Fatalf("LowerObjectComponentHeaders() error = %q, want owner-pack rejection", err.Error())
	}
}

func TestProjectObjectFieldIdentityHeadersRejectsForeignFieldNode(t *testing.T) {
	version, err := metadatair.ParseVersion("1.0.0")
	if err != nil {
		t.Fatal(err)
	}
	source := testObjectComponentIRSource()
	doc := metadatair.Document{
		Version: version,
		Nodes: []metadatair.NodeHeader{
			objectComponentNode(source, metadatair.NodeKindObject, "WorkItem", "/"),
			objectComponentNode(source, metadatair.NodeKindField, "Other.Status", "/fields/Status"),
		},
	}

	_, err = ProjectObjectFieldIdentityHeaders(doc)
	if err == nil {
		t.Fatal("ProjectObjectFieldIdentityHeaders() error = nil, want foreign field rejection")
	}
	if !strings.Contains(err.Error(), "does not belong to object WorkItem") {
		t.Fatalf("ProjectObjectFieldIdentityHeaders() error = %q, want object ownership rejection", err.Error())
	}
}

func testObjectComponentIRSource() ObjectComponentIRSource {
	return ObjectComponentIRSource{
		Owner: metadatair.Owner{
			Layer:     metadatair.OwnerLayerL1,
			OwnerPack: "toy-workflow-pack",
		},
		URI:       "pack://toy-workflow/objects/WorkItem.json",
		Component: "objects/WorkItem",
		Dialect:   "toy",
	}
}
