package wire

import (
	"encoding/json"
	"fmt"
	"strings"
	"testing"

	sharedErrors "github.com/nexuscrm/shared/pkg/errors"
)

// Phase-2 L0-B PR-0b (goldens-first) capture-and-freeze regression.
//
// This test BYTE-pins the EXACT current v63 wire output of the dialect copy of
// SFErrorFromDomainForVersion (wire.go) for ALL SEVEN neutral WireConditionKinds
// plus the CreateFromSourceGuard special case. It is the mirror of the kernel's
// domain_mapping_test.go table, but pinned at the SERIALIZED-BYTE level (the
// marshalled []SFError JSON, with errorCode/message/fields-omitempty) AND the HTTP
// status, so any drift in the dialect's v63 error envelope after the L0-B carve
// fails here.
//
// The frozen values are inline literals: SFErrorFromDomainForVersion is a fully
// deterministic pure transform (kind -> code/status, message verbatim, fields
// carried in order with omitempty), so an inline literal is the tightest possible
// pin. apiVersion is fixed at "v63.0".
//
// ZERO production change: this file only drives the existing exported
// SFErrorFromDomainForVersion and asserts equality. It modifies no production .go.

// goldenDomainKindCase freezes, per WireConditionKind, the produced status and the
// marshalled []SFError JSON bytes (the wire body the boundary emits).
type goldenDomainKindCase struct {
	name       string
	err        error
	apiVersion string // defaults to "v63.0" when empty
	wantStatus int
	wantBody   string
	// causeAbsent, when set, asserts the marshalled body does NOT contain this
	// substring — freezing the contract that an internal cause is logged, not
	// serialized (mirrors kernel TestSFErrorFromDomain_UnwrapsWrappedCause).
	causeAbsent string
}

func TestSFErrorFromDomainForVersionByteGolden(t *testing.T) {
	cases := []goldenDomainKindCase{
		{
			name:       "WireInvalidReference",
			err:        sharedErrors.NewInvalidReferenceError("m"),
			wantStatus: 400,
			wantBody:   `[{"errorCode":"INVALID_CROSS_REFERENCE_KEY","message":"m"}]`,
		},
		{
			name:       "WireFieldIntegrity",
			err:        sharedErrors.NewFieldIntegrityError("m"),
			wantStatus: 400,
			wantBody:   `[{"errorCode":"FIELD_INTEGRITY_EXCEPTION","message":"m"}]`,
		},
		{
			name:       "WireInvalidOperation",
			err:        sharedErrors.NewInvalidOperationError("m"),
			wantStatus: 400,
			wantBody:   `[{"errorCode":"INVALID_OPERATION","message":"m"}]`,
		},
		{
			name:       "WireInvalidField",
			err:        sharedErrors.NewInvalidFieldError("m"),
			wantStatus: 400,
			wantBody:   `[{"errorCode":"INVALID_FIELD","message":"m"}]`,
		},
		{
			name:       "WireNotFound",
			err:        sharedErrors.NewNotFoundWireError("m"),
			wantStatus: 404,
			wantBody:   `[{"errorCode":"NOT_FOUND","message":"m"}]`,
		},
		{
			name:       "WireMalformedRequest",
			err:        sharedErrors.NewMalformedRequestError("m"),
			wantStatus: 400,
			wantBody:   `[{"errorCode":"INVALID_REQUEST","message":"m"}]`,
		},
		{
			name:       "WireRequestTooLarge",
			err:        sharedErrors.NewRequestTooLargeError("m"),
			wantStatus: 413,
			wantBody:   `[{"errorCode":"INVALID_REQUEST","message":"m"}]`,
		},
		// Fields are carried in order with the omitempty contract preserved: a kind
		// WITH fields serializes the "fields" key after message.
		{
			name:       "WireInvalidReference_withFields",
			err:        sharedErrors.NewInvalidReferenceError("msg", "layoutId", "objectApiName"),
			wantStatus: 400,
			wantBody:   `[{"errorCode":"INVALID_CROSS_REFERENCE_KEY","message":"msg","fields":["layoutId","objectApiName"]}]`,
		},
		// CreateFromSourceGuard special case: the message is REPLACED by the versioned
		// endpoint rendering; the kind stays INVALID_OPERATION at 400 and carries no
		// fields. Action names are dedup/sorted by the shared formatter, so the multi
		// rendering is deterministic.
		{
			name:       "CreateFromSourceGuard_single",
			err:        sharedErrors.NewCreateFromSourceGuardError("Case", []string{"Case.Escalate"}),
			wantStatus: 400,
			wantBody: `[{"errorCode":"INVALID_OPERATION","message":"Direct create of Case with a configured source-FK populated is not permitted. ` +
				`Invoke the owning standard action endpoint instead: /services/data/v63.0/actions/standard/Case.Escalate"}]`,
		},
		{
			name:       "CreateFromSourceGuard_multi",
			err:        sharedErrors.NewCreateFromSourceGuardError("TargetObject", []string{"actionB", "actionA"}),
			wantStatus: 400,
			wantBody: `[{"errorCode":"INVALID_OPERATION","message":"Direct create of TargetObject with a configured source-FK populated is not permitted. ` +
				`Invoke one of the owning standard action endpoints instead: ` +
				`/services/data/v63.0/actions/standard/actionA, /services/data/v63.0/actions/standard/actionB"}]`,
		},
		// Cause redaction: a DomainWireError carrying an internal cause (WithCause)
		// must serialize ONLY the public Message — the cause is logged, not leaked.
		// Mirrors kernel TestSFErrorFromDomain_UnwrapsWrappedCause.
		{
			name:        "WireInvalidField_withCause_redacted",
			err:         sharedErrors.NewInvalidFieldError("Existing Flow metadata is malformed", "Metadata").WithCause(fmt.Errorf("json: unexpected end of input")),
			wantStatus:  400,
			wantBody:    `[{"errorCode":"INVALID_FIELD","message":"Existing Flow metadata is malformed","fields":["Metadata"]}]`,
			causeAbsent: "unexpected end of input",
		},
		// Versioned endpoint: the request apiVersion must flow through to the URL; a
		// non-v63 version proves the mapper is NOT hardcoded to v63.0.
		{
			name:       "CreateFromSourceGuard_v59_versioned",
			err:        sharedErrors.NewCreateFromSourceGuardError("Case", []string{"Case.Escalate"}),
			apiVersion: "v59.0",
			wantStatus: 400,
			wantBody: `[{"errorCode":"INVALID_OPERATION","message":"Direct create of Case with a configured source-FK populated is not permitted. ` +
				`Invoke the owning standard action endpoint instead: /services/data/v59.0/actions/standard/Case.Escalate"}]`,
		},
	}

	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			apiVersion := tc.apiVersion
			if apiVersion == "" {
				apiVersion = "v63.0"
			}
			sf, status, ok := SFErrorFromDomainForVersion(tc.err, apiVersion)
			if !ok {
				t.Fatalf("SFErrorFromDomainForVersion returned ok=false for %T", tc.err)
			}
			if status != tc.wantStatus {
				t.Errorf("status = %d; want %d", status, tc.wantStatus)
			}
			body, err := json.Marshal([]SFError{sf})
			if err != nil {
				t.Fatalf("marshal: %v", err)
			}
			if string(body) != tc.wantBody {
				t.Errorf("wire body drifted from frozen v63 bytes:\n got: %s\nwant: %s", string(body), tc.wantBody)
			}
			if tc.causeAbsent != "" && strings.Contains(string(body), tc.causeAbsent) {
				t.Errorf("internal cause leaked into v63 wire body: %q must not appear in %s", tc.causeAbsent, string(body))
			}
		})
	}
}
