package flowcommandv2

import (
	"bytes"
	"testing"

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

func TestPayloadCanonicalRoundTrip(t *testing.T) {
	payload := Payload{
		DefinitionRef:     "flow.neutral",
		DefinitionVersion: "flow-version-7",
		CanonicalInputs:   []byte(`{"record":{"id":"001"},"retry":false}`),
		DependencySource: DependencySourceScope{
			ObjectKind: "contacts",
			SourceID:   "remote-001",
		},
	}
	raw, err := Encode(payload)
	if err != nil {
		t.Fatalf("Encode: %v", err)
	}
	want := []byte(
		`{"canonicalInputs":{"record":{"id":"001"},"retry":false},"definitionRef":"flow.neutral","definitionVersion":"flow-version-7","dependencySource":{"objectKind":"contacts","sourceId":"remote-001"}}`,
	)
	if !bytes.Equal(raw, want) {
		t.Fatalf("payload = %s, want %s", raw, want)
	}
	decoded, err := Decode(raw)
	if err != nil {
		t.Fatalf("Decode: %v", err)
	}
	if decoded.DefinitionRef != payload.DefinitionRef ||
		decoded.DefinitionVersion != payload.DefinitionVersion ||
		decoded.DependencySource != payload.DependencySource ||
		!bytes.Equal(decoded.CanonicalInputs, payload.CanonicalInputs) {
		t.Fatalf("decoded = %#v, want %#v", decoded, payload)
	}
}

func TestPayloadVersionBoundariesAreFailClosed(t *testing.T) {
	v1, err := flowcommandv1.Encode(flowcommandv1.Payload{
		DefinitionRef:     "flow.neutral",
		DefinitionVersion: "flow-version-7",
		CanonicalInputs:   []byte(`{}`),
	})
	if err != nil {
		t.Fatal(err)
	}
	v2, err := Encode(Payload{
		DefinitionRef:     "flow.neutral",
		DefinitionVersion: "flow-version-7",
		CanonicalInputs:   []byte(`{}`),
		DependencySource: DependencySourceScope{
			ObjectKind: "contacts",
			SourceID:   "remote-001",
		},
	})
	if err != nil {
		t.Fatal(err)
	}
	if _, err := Decode(v1); err == nil {
		t.Fatal("v2 decoder accepted v1 payload without dependency source")
	}
	if _, err := flowcommandv1.Decode(v2); err == nil {
		t.Fatal("v1 decoder accepted v2 payload")
	}
}

func TestPayloadFailsClosed(t *testing.T) {
	for name, raw := range map[string][]byte{
		"non canonical":          []byte(`{"canonicalInputs":{"b":1,"a":2},"definitionRef":"flow","definitionVersion":"v1","dependencySource":{"objectKind":"contacts","sourceId":"remote"}}`),
		"unknown field":          []byte(`{"canonicalInputs":{},"definitionRef":"flow","definitionVersion":"v1","dependencySource":{"objectKind":"contacts","sourceId":"remote"},"extra":true}`),
		"duplicate field":        []byte(`{"canonicalInputs":{},"definitionRef":"flow","definitionRef":"other","definitionVersion":"v1","dependencySource":{"objectKind":"contacts","sourceId":"remote"}}`),
		"scalar inputs":          []byte(`{"canonicalInputs":1,"definitionRef":"flow","definitionVersion":"v1","dependencySource":{"objectKind":"contacts","sourceId":"remote"}}`),
		"missing source":         []byte(`{"canonicalInputs":{},"definitionRef":"flow","definitionVersion":"v1"}`),
		"missing source id":      []byte(`{"canonicalInputs":{},"definitionRef":"flow","definitionVersion":"v1","dependencySource":{"objectKind":"contacts"}}`),
		"source unknown field":   []byte(`{"canonicalInputs":{},"definitionRef":"flow","definitionVersion":"v1","dependencySource":{"objectKind":"contacts","sourceId":"remote","extra":true}}`),
		"source duplicate field": []byte(`{"canonicalInputs":{},"definitionRef":"flow","definitionVersion":"v1","dependencySource":{"objectKind":"contacts","objectKind":"companies","sourceId":"remote"}}`),
		"blank source object":    []byte(`{"canonicalInputs":{},"definitionRef":"flow","definitionVersion":"v1","dependencySource":{"objectKind":"","sourceId":"remote"}}`),
		"blank source id":        []byte(`{"canonicalInputs":{},"definitionRef":"flow","definitionVersion":"v1","dependencySource":{"objectKind":"contacts","sourceId":""}}`),
		"noncanonical source":    []byte(`{"canonicalInputs":{},"definitionRef":"flow","definitionVersion":"v1","dependencySource":{"objectKind":" contacts","sourceId":"remote"}}`),
		"case variant":           []byte(`{"canonicalInputs":{},"definitionRef":"flow","definitionVersion":"v1","DependencySource":{"objectKind":"contacts","sourceId":"remote"}}`),
		"trailing":               []byte(`{"canonicalInputs":{},"definitionRef":"flow","definitionVersion":"v1","dependencySource":{"objectKind":"contacts","sourceId":"remote"}} {}`),
	} {
		t.Run(name, func(t *testing.T) {
			if _, err := Decode(raw); err == nil {
				t.Fatal("Decode succeeded")
			}
		})
	}
}
