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 TestVerifyObjectFieldIdentityShadowAcceptsEquivalentHeaders(t *testing.T) {
	schema := &models.ObjectMetadata{
		APIName: "WorkItem",
		Fields: []models.FieldMetadata{
			{APIName: "Summary", Type: constants.FieldTypeString},
			{APIName: "Status", Type: constants.FieldTypeString},
		},
	}
	doc, err := LowerObjectComponentHeaders(schema, testObjectComponentIRSource())
	if err != nil {
		t.Fatalf("LowerObjectComponentHeaders() error = %v", err)
	}

	got, err := VerifyObjectFieldIdentityShadow(schema, doc)
	if err != nil {
		t.Fatalf("VerifyObjectFieldIdentityShadow() error = %v", err)
	}
	want := ObjectFieldIdentityShadowReport{
		Runtime: ObjectFieldIdentityProjection{
			ObjectAPIName: "WorkItem",
			FieldAPINames: []string{"Summary", "Status"},
		},
		IR: ObjectFieldIdentityProjection{
			ObjectAPIName: "WorkItem",
			FieldAPINames: []string{"Summary", "Status"},
		},
	}
	if !reflect.DeepEqual(got, want) {
		t.Fatalf("shadow report = %#v, want %#v", got, want)
	}
}

func TestVerifyObjectFieldIdentityShadowRejectsDrift(t *testing.T) {
	schema := &models.ObjectMetadata{
		APIName: "WorkItem",
		Fields: []models.FieldMetadata{
			{APIName: "Summary", Type: constants.FieldTypeString},
			{APIName: "Status", Type: constants.FieldTypeString},
		},
	}
	source := testObjectComponentIRSource()
	doc := metadatair.Document{
		Version: neutralMetadataIRVersion(),
		Nodes: []metadatair.NodeHeader{
			objectComponentNode(source, metadatair.NodeKindObject, "WorkItem", "/"),
			objectComponentNode(source, metadatair.NodeKindField, "WorkItem.Summary", "/fields/Summary"),
		},
	}

	report, err := VerifyObjectFieldIdentityShadow(schema, doc)
	if err == nil {
		t.Fatal("VerifyObjectFieldIdentityShadow() error = nil, want mismatch")
	}
	if !strings.Contains(err.Error(), "metadata IR object field shadow mismatch") {
		t.Fatalf("VerifyObjectFieldIdentityShadow() error = %q, want shadow mismatch", err.Error())
	}
	if !reflect.DeepEqual(report.Runtime.FieldAPINames, []string{"Summary", "Status"}) || !reflect.DeepEqual(report.IR.FieldAPINames, []string{"Summary"}) {
		t.Fatalf("shadow report = %#v, want runtime/ir drift details", report)
	}
}

func TestProjectObjectFieldIdentityHeadersRejectsMixedNodeKinds(t *testing.T) {
	source := testObjectComponentIRSource()
	doc := metadatair.Document{
		Version: neutralMetadataIRVersion(),
		Nodes: []metadatair.NodeHeader{
			objectComponentNode(source, metadatair.NodeKindObject, "WorkItem", "/"),
			flowComponentNode(testFlowComponentIRSource(), metadatair.NodeKindFlowElement, "WorkItem.v1.Start", "/assignments/0"),
		},
	}

	_, err := ProjectObjectFieldIdentityHeaders(doc)
	if err == nil {
		t.Fatal("ProjectObjectFieldIdentityHeaders() error = nil, want mixed node kind rejection")
	}
	if !strings.Contains(err.Error(), "requires only object/field nodes") {
		t.Fatalf("ProjectObjectFieldIdentityHeaders() error = %q, want mixed node kind rejection", err.Error())
	}
}

func TestProjectObjectFieldIdentityHeadersRejectsWrongSourceFormat(t *testing.T) {
	doc := metadatair.Document{
		Version: neutralMetadataIRVersion(),
		Nodes: []metadatair.NodeHeader{
			flowComponentNode(testFlowComponentIRSource(), metadatair.NodeKindObject, "WorkItem", "/"),
		},
	}

	_, err := ProjectObjectFieldIdentityHeaders(doc)
	if err == nil {
		t.Fatal("ProjectObjectFieldIdentityHeaders() error = nil, want source-format rejection")
	}
	if !strings.Contains(err.Error(), "source format \"flow_metadata_json\" is not \"pack_component_json\"") {
		t.Fatalf("ProjectObjectFieldIdentityHeaders() error = %q, want source-format rejection", err.Error())
	}
}

func TestVerifyFlowElementIdentityShadowAcceptsEquivalentHeaders(t *testing.T) {
	definition := &models.FlowDefinition{
		Id:            "fd-workflow",
		DeveloperName: "FulfillmentFlow",
	}
	version := &models.FlowVersion{
		DefinitionId:  "fd-workflow",
		VersionNumber: 3,
		Metadata: models.FlowMetadata{
			Decisions:   []models.FlowDecision{{Name: "CheckStatus"}},
			Assignments: []models.FlowAssignment{{Name: "SetStatus"}},
			ActionCalls: []models.FlowActionCall{{Name: "NotifyOwner"}},
		},
	}
	doc, err := LowerFlowComponentHeaders(definition, version, testFlowComponentIRSource())
	if err != nil {
		t.Fatalf("LowerFlowComponentHeaders() error = %v", err)
	}

	got, err := VerifyFlowElementIdentityShadow(definition, version, doc)
	if err != nil {
		t.Fatalf("VerifyFlowElementIdentityShadow() error = %v", err)
	}
	want := FlowElementIdentityShadowReport{
		Runtime: FlowElementIdentityProjection{
			VersionNodeID: "FulfillmentFlow.v3",
			ElementNames:  []string{"CheckStatus", "SetStatus", "NotifyOwner"},
		},
		IR: FlowElementIdentityProjection{
			VersionNodeID: "FulfillmentFlow.v3",
			ElementNames:  []string{"CheckStatus", "SetStatus", "NotifyOwner"},
		},
	}
	if !reflect.DeepEqual(got, want) {
		t.Fatalf("shadow report = %#v, want %#v", got, want)
	}
}

func TestVerifyFlowElementIdentityShadowRejectsDrift(t *testing.T) {
	definition := &models.FlowDefinition{DeveloperName: "FulfillmentFlow"}
	version := &models.FlowVersion{
		VersionNumber: 3,
		Metadata: models.FlowMetadata{
			Assignments: []models.FlowAssignment{{Name: "SetStatus"}, {Name: "NotifyOwner"}},
		},
	}
	source := testFlowComponentIRSource()
	doc := metadatair.Document{
		Version: neutralMetadataIRVersion(),
		Nodes: []metadatair.NodeHeader{
			flowComponentNode(source, metadatair.NodeKindFlowElement, "FulfillmentFlow.v3.SetStatus", "/assignments/0"),
		},
	}

	report, err := VerifyFlowElementIdentityShadow(definition, version, doc)
	if err == nil {
		t.Fatal("VerifyFlowElementIdentityShadow() error = nil, want mismatch")
	}
	if !strings.Contains(err.Error(), "metadata IR flow element shadow mismatch") {
		t.Fatalf("VerifyFlowElementIdentityShadow() error = %q, want shadow mismatch", err.Error())
	}
	if !reflect.DeepEqual(report.Runtime.ElementNames, []string{"SetStatus", "NotifyOwner"}) || !reflect.DeepEqual(report.IR.ElementNames, []string{"SetStatus"}) {
		t.Fatalf("shadow report = %#v, want runtime/ir drift details", report)
	}
}
