package packs

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

func TestSourceBackedConnectorCalloutProfilePersistsFinalizationProjection(t *testing.T) {
	source := &ResolvedPackSource{
		Kind: PackSourceKindLocalPath, URI: "/tmp/connector-profile-finalization",
		Manifest: &PackManifest{
			ManifestFormatVersion: SupportedManifestFormatVersion,
			Name:                  "connector-a", DisplayName: "Connector A", Category: ConnectorPack, Version: "1.0.0",
			PlatformCompatibility: PlatformCompatibility{Min: "0.1.0", MaxExclusive: "2.0.0"},
			Components: []ManifestComponent{{
				Kind: ComponentKindConnectorCalloutProfile, BusinessKey: "connector-a/default", Manageability: ManageabilityLocked,
				Payload: json.RawMessage(`{"profileName":"default","credentialRef":"credential-a","endpointClass":"crm.write","retryPolicy":{"maxAttempts":3,"baseDelay":1000000,"multiplier":2,"maxDelay":10000000,"retryableStatusCodes":[503,429]}}`),
			}},
		},
	}
	pack, err := NewSourceBackedPack(source)
	if err != nil {
		t.Fatal(err)
	}
	declaration, err := BuildPersistedRuntimeDeclaration(pack)
	if err != nil {
		t.Fatal(err)
	}
	got, err := LoadPersistedConnectorCalloutProfileFinalization(
		declaration.StartupShallowDeclaredRowsJSON(),
		"connector-a",
		"connector-a/default",
	)
	if err != nil {
		t.Fatal(err)
	}
	if got.CredentialRef != "credential-a" || got.MaxAttempts != 3 ||
		got.BaseDelayNanos != 1_000_000 || len(got.RetryableStatusCodes) != 2 {
		t.Fatalf("persisted source profile = %#v", got)
	}
}

func TestLoadPersistedConnectorCalloutProfileFinalizationSelectsExactProjection(t *testing.T) {
	projection := ConnectorCalloutProfileFinalizationProjection{
		OwnerPack: "connector-a", ProfileName: "default",
		CredentialRef: "credential-a", EndpointClass: "crm.write",
		MaxAttempts: 3, BaseDelayNanos: 1_000_000, Multiplier: 2, MaxDelayNanos: 10_000_000,
		RetryableStatusCodes: []int{503, 429},
	}
	encoded, err := marshalConnectorCalloutProfileFinalizationProjection(projection)
	if err != nil {
		t.Fatal(err)
	}
	raw := mustRuntimeStartupShallowJSON(t, []runtimeStartupShallowDeclaredRow{{
		Kind: ComponentKindConnectorCalloutProfile, BusinessKey: "default", Projection: encoded,
	}})

	got, err := LoadPersistedConnectorCalloutProfileFinalization(raw, "connector-a", "connector-a/default")
	if err != nil {
		t.Fatal(err)
	}
	if got.OwnerPack != projection.OwnerPack || got.ProfileName != projection.ProfileName ||
		got.CredentialRef != projection.CredentialRef || len(got.RetryableStatusCodes) != 2 {
		t.Fatalf("persisted profile = %#v", got)
	}
	got.RetryableStatusCodes[0] = 500
	again, err := LoadPersistedConnectorCalloutProfileFinalization(raw, "connector-a", "connector-a/default")
	if err != nil {
		t.Fatal(err)
	}
	if again.RetryableStatusCodes[0] != 503 {
		t.Fatalf("persisted profile status codes alias caller memory: %#v", again.RetryableStatusCodes)
	}
}

func TestLoadPersistedConnectorCalloutProfileFinalizationFailsClosed(t *testing.T) {
	projection := ConnectorCalloutProfileFinalizationProjection{
		OwnerPack: "connector-a", ProfileName: "default",
		CredentialRef: "credential-a", EndpointClass: "crm.write", MaxAttempts: 1,
	}
	encoded, err := marshalConnectorCalloutProfileFinalizationProjection(projection)
	if err != nil {
		t.Fatal(err)
	}
	row := runtimeStartupShallowDeclaredRow{
		Kind: ComponentKindConnectorCalloutProfile, BusinessKey: "default", Projection: encoded,
	}
	valid := mustRuntimeStartupShallowJSON(t, []runtimeStartupShallowDeclaredRow{row})
	wrongOwnerProjection := projection
	wrongOwnerProjection.OwnerPack = "connector-b"
	wrongOwnerEncoded, err := marshalConnectorCalloutProfileFinalizationProjection(wrongOwnerProjection)
	if err != nil {
		t.Fatal(err)
	}
	wrongOwner := mustRuntimeStartupShallowJSON(t, []runtimeStartupShallowDeclaredRow{{
		Kind: ComponentKindConnectorCalloutProfile, BusinessKey: "default", Projection: wrongOwnerEncoded,
	}})
	rowJSON := mustMarshalConnectorRuntimeRow(t, row)
	duplicate := `[` + rowJSON + `,` + rowJSON + `]`
	tests := []struct {
		name string
		raw  string
		key  string
		want string
	}{
		{name: "projectionless stopped-upgrade generation", raw: `[{"kind":"connector_callout_profile","businessKey":"default"}]`, key: "connector-a/default", want: "projection is required"},
		{name: "absent", raw: `[]`, key: "connector-a/default", want: "absent from persisted generation"},
		{name: "duplicate", raw: duplicate, key: "connector-a/default", want: "is ambiguous"},
		{name: "wrong owner", raw: wrongOwner, key: "connector-a/default", want: "identity disagrees"},
		{name: "wrong binding owner", raw: valid, key: "connector-b/default", want: "not owned"},
	}
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			_, err := LoadPersistedConnectorCalloutProfileFinalization(test.raw, "connector-a", test.key)
			if err == nil || !strings.Contains(err.Error(), test.want) {
				t.Fatalf("error = %v, want containing %q", err, test.want)
			}
		})
	}
}
