package connector_inbound

import (
	"encoding/json"
	"errors"
	"testing"
)

// TestEncodeQueueEnvelope_RoundTrip verifies that encoding and then decoding
// preserves objectKind, sourceID, and the original payload bytes.
func TestEncodeQueueEnvelope_RoundTrip(t *testing.T) {
	payload := json.RawMessage(`{"name":"Alice","amount":42}`)
	b, err := EncodeQueueEnvelope("contact", "src-123", payload)
	if err != nil {
		t.Fatalf("EncodeQueueEnvelope: %v", err)
	}
	env, err := DecodeQueueEnvelope(b)
	if err != nil {
		t.Fatalf("DecodeQueueEnvelope: %v", err)
	}
	if env.ObjectKind != "contact" {
		t.Errorf("ObjectKind = %q, want %q", env.ObjectKind, "contact")
	}
	if env.SourceID != "src-123" {
		t.Errorf("SourceID = %q, want %q", env.SourceID, "src-123")
	}
	// Payload must round-trip byte-for-byte (json.RawMessage is not re-encoded).
	var got, want map[string]any
	if err := json.Unmarshal(env.Payload, &got); err != nil {
		t.Fatalf("unmarshal round-tripped payload: %v", err)
	}
	if err := json.Unmarshal(payload, &want); err != nil {
		t.Fatalf("unmarshal original payload: %v", err)
	}
	if len(got) != len(want) {
		t.Errorf("payload round-trip: got %d keys, want %d", len(got), len(want))
	}
}

// TestEncodeQueueEnvelope_NilPayload verifies that a nil payload is encoded
// as JSON null and round-trips to a valid (non-nil-bytes) Payload field.
func TestEncodeQueueEnvelope_NilPayload(t *testing.T) {
	b, err := EncodeQueueEnvelope("contact", "src-999", nil)
	if err != nil {
		t.Fatalf("EncodeQueueEnvelope with nil payload: %v", err)
	}
	env, err := DecodeQueueEnvelope(b)
	if err != nil {
		t.Fatalf("DecodeQueueEnvelope: %v", err)
	}
	if env.ObjectKind != "contact" || env.SourceID != "src-999" {
		t.Errorf("unexpected envelope fields: %+v", env)
	}
}

// TestEncodeQueueEnvelope_RejectsEmptyObjectKind verifies that an empty
// objectKind is rejected with ErrInvalidQueueEnvelope.
func TestEncodeQueueEnvelope_RejectsEmptyObjectKind(t *testing.T) {
	_, err := EncodeQueueEnvelope("", "src-123", json.RawMessage(`{}`))
	if !errors.Is(err, ErrInvalidQueueEnvelope) {
		t.Errorf("want ErrInvalidQueueEnvelope for empty objectKind, got %v", err)
	}
}

// TestEncodeQueueEnvelope_RejectsEmptySourceID verifies that an empty
// sourceID is rejected with ErrInvalidQueueEnvelope.
func TestEncodeQueueEnvelope_RejectsEmptySourceID(t *testing.T) {
	_, err := EncodeQueueEnvelope("contact", "", json.RawMessage(`{}`))
	if !errors.Is(err, ErrInvalidQueueEnvelope) {
		t.Errorf("want ErrInvalidQueueEnvelope for empty sourceID, got %v", err)
	}
}

// TestDecodeQueueEnvelope_RejectsEmptyBytes verifies that an empty byte slice
// is rejected with ErrInvalidQueueEnvelope.
func TestDecodeQueueEnvelope_RejectsEmptyBytes(t *testing.T) {
	_, err := DecodeQueueEnvelope([]byte{})
	if !errors.Is(err, ErrInvalidQueueEnvelope) {
		t.Errorf("want ErrInvalidQueueEnvelope for empty bytes, got %v", err)
	}
}

// TestDecodeQueueEnvelope_RejectsMalformedJSON verifies that non-JSON bytes
// are rejected with ErrInvalidQueueEnvelope.
func TestDecodeQueueEnvelope_RejectsMalformedJSON(t *testing.T) {
	_, err := DecodeQueueEnvelope([]byte(`not-json`))
	if !errors.Is(err, ErrInvalidQueueEnvelope) {
		t.Errorf("want ErrInvalidQueueEnvelope for malformed JSON, got %v", err)
	}
}

// TestDecodeQueueEnvelope_RejectsMissingObjectKindAfterParse verifies that a
// JSON object with no objectKind field (or empty string) is rejected.
func TestDecodeQueueEnvelope_RejectsMissingObjectKindAfterParse(t *testing.T) {
	// Encode only sourceID, no objectKind.
	raw := `{"sourceID":"src-abc","payload":{}}`
	_, err := DecodeQueueEnvelope([]byte(raw))
	if !errors.Is(err, ErrInvalidQueueEnvelope) {
		t.Errorf("want ErrInvalidQueueEnvelope for missing objectKind, got %v", err)
	}
}

// TestDecodeQueueEnvelope_RejectsMissingSourceIDAfterParse verifies that a
// JSON object with no sourceID field (or empty string) is rejected.
func TestDecodeQueueEnvelope_RejectsMissingSourceIDAfterParse(t *testing.T) {
	// Encode only objectKind, no sourceID.
	raw := `{"objectKind":"contact","payload":{}}`
	_, err := DecodeQueueEnvelope([]byte(raw))
	if !errors.Is(err, ErrInvalidQueueEnvelope) {
		t.Errorf("want ErrInvalidQueueEnvelope for missing sourceID, got %v", err)
	}
}

// TestDecodeQueueEnvelope_RejectsLegacyReservedKeyShape verifies that
// DecodeQueueEnvelope correctly rejects pre-R22 legacy payload bytes (the format
// that carries __objectKind / __sourceID as reserved keys directly in the user
// payload, without a QueueEnvelope wrapper). The worker requires QueueEnvelope
// as the only runtime read path; legacy rows fail terminal at decode.
func TestDecodeQueueEnvelope_RejectsLegacyReservedKeyShape(t *testing.T) {
	legacy := []byte(`{"__objectKind":"contact","__sourceID":"x","data":{}}`)
	_, err := DecodeQueueEnvelope(legacy)
	if !errors.Is(err, ErrInvalidQueueEnvelope) {
		t.Fatalf("expected ErrInvalidQueueEnvelope on legacy bytes, got %v", err)
	}
}
