package packs

import (
	"strings"
	"testing"

	connector "github.com/pingcap-inc/nexuscrm-contract/pkg/connector"
)

func TestLoadPersistedConnectorFieldMappingDirectionsSelectsExactComponent(t *testing.T) {
	selected := connector.ConnectorFieldMappingInput{
		Source: "connector-a", SourceObject: "RemoteAccount", TargetObject: "Account",
		ActionType: "upsert", SourceField: "name", TargetField: "Name",
		Direction: connector.DirectionInbound, ConflictPolicy: connector.ConflictPolicyOverwrite,
	}
	other := selected
	other.SourceObject = "RemoteContact"
	other.TargetObject = "Contact"
	projection, err := marshalRuntimeStartupShallowProjection([]connector.ConnectorFieldMappingInput{selected})
	if err != nil {
		t.Fatal(err)
	}
	otherProjection, err := marshalRuntimeStartupShallowProjection([]connector.ConnectorFieldMappingInput{other})
	if err != nil {
		t.Fatal(err)
	}
	raw, err := marshalRuntimeStartupShallowDeclaredRows([]runtimeStartupShallowDeclaredRow{
		{Kind: ComponentKindConnectorFieldMapping, BusinessKey: "connector-a/account", Projection: projection},
		{Kind: ComponentKindConnectorFieldMapping, BusinessKey: "connector-a/contact", Projection: otherProjection},
	})
	if err != nil {
		t.Fatal(err)
	}

	got, outbound, err := LoadPersistedConnectorFieldMappingDirections(raw, "connector-a", "connector-a/account")
	if err != nil {
		t.Fatal(err)
	}
	if len(got) != 1 || got[0] != selected || len(outbound) != 0 {
		t.Fatalf("exact persisted mapping = %#v, want %#v", got, selected)
	}
}

func TestLoadPersistedConnectorFieldMappingDirectionsFailsClosed(t *testing.T) {
	valid := connector.ConnectorFieldMappingInput{
		Source: "connector-a", SourceObject: "RemoteAccount", TargetObject: "Account",
		ActionType: "upsert", SourceField: "name", TargetField: "Name",
		Direction: connector.DirectionInbound, ConflictPolicy: connector.ConflictPolicyOverwrite,
	}
	projection, err := marshalRuntimeStartupShallowProjection([]connector.ConnectorFieldMappingInput{valid})
	if err != nil {
		t.Fatal(err)
	}
	row := runtimeStartupShallowDeclaredRow{
		Kind: ComponentKindConnectorFieldMapping, BusinessKey: "connector-a/account", Projection: projection,
	}
	oneRow, err := marshalRuntimeStartupShallowDeclaredRows([]runtimeStartupShallowDeclaredRow{row})
	if err != nil {
		t.Fatal(err)
	}
	duplicate := "[" + strings.TrimSuffix(strings.TrimPrefix(oneRow, "["), "]") + "," +
		strings.TrimSuffix(strings.TrimPrefix(oneRow, "["), "]") + "]"
	wrongOwner := valid
	wrongOwner.Source = "connector-b"
	wrongOwnerProjection, err := marshalRuntimeStartupShallowProjection([]connector.ConnectorFieldMappingInput{wrongOwner})
	if err != nil {
		t.Fatal(err)
	}
	wrongOwnerRaw, err := marshalRuntimeStartupShallowDeclaredRows([]runtimeStartupShallowDeclaredRow{{
		Kind: ComponentKindConnectorFieldMapping, BusinessKey: "connector-a/account", Projection: wrongOwnerProjection,
	}})
	if err != nil {
		t.Fatal(err)
	}

	tests := []struct {
		name string
		raw  string
		want string
	}{
		{name: "missing generation declaration", raw: "", want: "declaration is missing"},
		{name: "missing component", raw: "[]", want: "absent from persisted generation"},
		{name: "duplicate business key", raw: duplicate, want: "is ambiguous"},
		{name: "wrong owner", raw: wrongOwnerRaw, want: "disagrees"},
		{name: "unknown declaration field", raw: `[{"kind":"connector_field_mapping","businessKey":"connector-a/account","projection":"[]","unexpected":true}]`, want: "unknown field"},
	}
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			_, _, err := LoadPersistedConnectorFieldMappingDirections(test.raw, "connector-a", "connector-a/account")
			if err == nil || !strings.Contains(err.Error(), test.want) {
				t.Fatalf("error = %v, want containing %q", err, test.want)
			}
		})
	}
}

func TestLoadPersistedConnectorFieldMappingDirectionsPreservesExactClosures(t *testing.T) {
	values := []connector.ConnectorFieldMappingInput{
		{Source: "connector-a", SourceObject: "Contact", TargetObject: "contacts", ActionType: "PushToConnector", SourceField: "Name", TargetField: "name", Direction: connector.DirectionOutbound, SortOrder: 2},
		{Source: "connector-a", SourceObject: "Contact", TargetObject: "contacts", ActionType: "PushToConnector", SourceField: "Email", TargetField: "email", Direction: connector.DirectionBidirectional, SortOrder: 1},
		{Source: "connector-a", SourceObject: "Contact", TargetObject: "contacts", ActionType: "PushToConnector", SourceField: "Phone", TargetField: "phone", Direction: connector.DirectionInbound, SortOrder: 0},
		{Source: "connector-a", SourceObject: "Contact", TargetObject: "contacts", ActionType: "PushToConnector", SourceField: "Title", TargetField: "title", Direction: connector.DirectionOutbound, ProjectionOnly: true, SortOrder: 0},
	}
	projection, err := marshalRuntimeStartupShallowProjection(values)
	if err != nil {
		t.Fatal(err)
	}
	raw := mustRuntimeStartupShallowJSON(t, []runtimeStartupShallowDeclaredRow{{
		Kind: ComponentKindConnectorFieldMapping, BusinessKey: "connector-a/contact", Projection: projection,
	}})

	inbound, outbound, err := LoadPersistedConnectorFieldMappingDirections(raw, "connector-a", "connector-a/contact")
	if err != nil {
		t.Fatal(err)
	}
	if len(inbound) != 2 || inbound[0].Direction != connector.DirectionInbound || inbound[1].Direction != connector.DirectionInbound {
		t.Fatalf("inbound closure = %#v", inbound)
	}
	if len(outbound) != 2 || outbound[0].Direction != connector.DirectionOutbound ||
		outbound[1].Direction != connector.DirectionBidirectional || outbound[1].SourceField != "Email" {
		t.Fatalf("outbound closure = %#v", outbound)
	}
}
