package serverruntime

import (
	"context"
	"database/sql"
	"errors"
	"net/http"
	"reflect"
	"strings"
	"testing"

	"github.com/nexuscrm/auth"
	"github.com/nexuscrm/automation"
	"github.com/nexuscrm/shared/pkg/contract"
	"github.com/nexuscrm/shared/pkg/dialect"
)

func TestComposeProductDistributionMergesFakeProducts(t *testing.T) {
	var dialectCalls []string
	var validationCalls []string
	var lifecycleCalls []string

	hooks, err := ComposeProductDistribution(NewProductDistribution(
		ProductHooks{
			OwningAppPacks: []string{"alpha-pack"},
			Capabilities: ProductCapabilities{
				HostPrimitives: []HostPrimitive{
					{Kind: "renderer", Name: "app-pack:shared/action-renderer/common", Version: "v1"},
					{Kind: "renderer", Name: "app-pack:alpha/action-renderer/open", Version: "v1"},
				},
				FlowActionCallHandlerImplementations: []automation.FlowActionCallHandlerImplementation{
					{HandlerRef: "alpha:automation/do-work", Build: fakeFlowActionBuilder},
				},
				PublicLoginProviders: []PublicLoginProviderRegistration{fakePublicLoginProvider("Alpha_Login")},
				OAuthUserInfoProfiles: []OAuthUserInfoProfileRegistration{
					{ProfileID: "Alpha_Compat", Applies: fakeUserInfoProfileApplies, Build: fakeUserInfoProfileBuilder},
				},
				OAuthSessionIssuerClasses: []OAuthSessionIssuerClassRegistration{{Class: "Alpha_Compat"}},
				OAuthTokenResponseProfiles: []OAuthTokenResponseProfileRegistration{
					{ProfileID: "Alpha_Token", Applies: fakeTokenResponseProfileApplies, Build: fakeTokenResponseProfileBuilder},
				},
				DetachedReferenceResolvers: []DetachedReferenceResolverRegistration{
					{SourceType: "Alpha_Blob", Resolver: fakeDetachedReferenceResolver, OwningAppPacks: []string{"alpha-pack"}},
				},
				DynamicCORSRouteFamilies: []DynamicCORSRouteFamilyRegistration{
					{Name: "Alpha_ID", Allows: fakeDynamicCORSRoutePredicate("/alpha/id")},
				},
				OAuthScopeRouteFamilies: []OAuthScopeRouteFamilyRegistration{
					{Name: "Alpha_OpenID", Scopes: []string{"openid"}, Allows: fakeOAuthScopeRoutePredicate("/alpha/id")},
				},
				AuthChallengeProfiles: []AuthChallengeProfileRegistration{
					{ProfileID: "Alpha_Challenge", Build: fakeAuthChallengeBuilder("alpha_challenge")},
				},
			},
			Dialects: func(DialectRegistrationContext) []dialect.Dialect {
				dialectCalls = append(dialectCalls, "alpha")
				return []dialect.Dialect{fakeProductDialect{name: "alpha", pack: "alpha-pack"}}
			},
			ValidateComposition: func(context.Context, *sql.DB, []ActiveAppPack) error {
				validationCalls = append(validationCalls, "alpha")
				return nil
			},
			RegisterLifecycle: func(ProductLifecycle) error {
				lifecycleCalls = append(lifecycleCalls, "alpha")
				return nil
			},
		},
		ProductHooks{
			OwningAppPacks: []string{"beta-pack"},
			Capabilities: ProductCapabilities{
				HostPrimitives: []HostPrimitive{
					{Kind: "Renderer", Name: "app-pack:shared/action-renderer/common", Version: "v1"},
					{Kind: "renderer", Name: "app-pack:beta/action-renderer/transition", Version: "v2"},
				},
				FlowActionCallHandlerImplementations: []automation.FlowActionCallHandlerImplementation{
					{HandlerRef: "beta:automation/transition", Build: fakeFlowActionBuilder},
				},
				PublicLoginProviders: []PublicLoginProviderRegistration{fakePublicLoginProvider("beta_login")},
				OAuthUserInfoProfiles: []OAuthUserInfoProfileRegistration{
					{ProfileID: "beta_compat", Applies: fakeUserInfoProfileApplies, Build: fakeUserInfoProfileBuilder},
				},
				OAuthSessionIssuerClasses: []OAuthSessionIssuerClassRegistration{{Class: "beta_compat"}},
				OAuthTokenResponseProfiles: []OAuthTokenResponseProfileRegistration{
					{ProfileID: "beta_token", Applies: fakeTokenResponseProfileApplies, Build: fakeTokenResponseProfileBuilder},
				},
				DetachedReferenceResolvers: []DetachedReferenceResolverRegistration{
					{SourceType: "beta_blob", Resolver: fakeDetachedReferenceResolver, OwningAppPacks: []string{"beta-pack"}},
				},
				DynamicCORSRouteFamilies: []DynamicCORSRouteFamilyRegistration{
					{Name: "beta_id", Allows: fakeDynamicCORSRoutePredicate("/beta/id")},
				},
				OAuthScopeRouteFamilies: []OAuthScopeRouteFamilyRegistration{
					{Name: "beta_openid", Scopes: []string{"OpenID", " openid "}, Allows: fakeOAuthScopeRoutePredicate("/beta/id")},
				},
				AuthChallengeProfiles: []AuthChallengeProfileRegistration{
					{ProfileID: "beta_challenge", Build: fakeAuthChallengeBuilder("beta_challenge")},
				},
			},
			Dialects: func(DialectRegistrationContext) []dialect.Dialect {
				dialectCalls = append(dialectCalls, "beta")
				return []dialect.Dialect{fakeProductDialect{name: "beta", pack: "beta-pack"}}
			},
			ValidateComposition: func(context.Context, *sql.DB, []ActiveAppPack) error {
				validationCalls = append(validationCalls, "beta")
				return nil
			},
			RegisterLifecycle: func(ProductLifecycle) error {
				lifecycleCalls = append(lifecycleCalls, "beta")
				return nil
			},
		},
	))
	if err != nil {
		t.Fatalf("ComposeProductDistribution: %v", err)
	}

	wantPrimitives := []HostPrimitive{
		{Kind: "renderer", Name: "app-pack:shared/action-renderer/common", Version: "v1"},
		{Kind: "renderer", Name: "app-pack:alpha/action-renderer/open", Version: "v1"},
		{Kind: "renderer", Name: "app-pack:beta/action-renderer/transition", Version: "v2"},
	}
	if !reflect.DeepEqual(hooks.Capabilities.HostPrimitives, wantPrimitives) {
		t.Fatalf("Capabilities.HostPrimitives = %#v, want %#v", hooks.Capabilities.HostPrimitives, wantPrimitives)
	}

	var handlerRefs []string
	for _, implementation := range hooks.Capabilities.FlowActionCallHandlerImplementations {
		handlerRefs = append(handlerRefs, implementation.HandlerRef)
	}
	if want := []string{"alpha:automation/do-work", "beta:automation/transition"}; !reflect.DeepEqual(handlerRefs, want) {
		t.Fatalf("FlowActionCallHandlerImplementations refs = %#v, want %#v", handlerRefs, want)
	}
	if want := []string{"alpha_login", "beta_login"}; !reflect.DeepEqual(publicLoginProviderIDs(hooks.Capabilities.PublicLoginProviders), want) {
		t.Fatalf("PublicLoginProviders = %#v, want IDs %#v", hooks.Capabilities.PublicLoginProviders, want)
	}

	if want := []string{"alpha_compat", "beta_compat"}; !reflect.DeepEqual(oauthUserInfoProfileIDs(hooks.Capabilities.OAuthUserInfoProfiles), want) {
		t.Fatalf("OAuthUserInfoProfiles = %#v, want IDs %#v", hooks.Capabilities.OAuthUserInfoProfiles, want)
	}

	if want := []string{"alpha_compat", "beta_compat"}; !reflect.DeepEqual(oauthSessionIssuerClassNames(hooks.Capabilities.OAuthSessionIssuerClasses), want) {
		t.Fatalf("OAuthSessionIssuerClasses = %#v, want %#v", hooks.Capabilities.OAuthSessionIssuerClasses, want)
	}

	if want := []string{"alpha_token", "beta_token"}; !reflect.DeepEqual(oauthTokenResponseProfileIDs(hooks.Capabilities.OAuthTokenResponseProfiles), want) {
		t.Fatalf("OAuthTokenResponseProfiles = %#v, want IDs %#v", hooks.Capabilities.OAuthTokenResponseProfiles, want)
	}

	if want := []string{"alpha_blob", "beta_blob"}; !reflect.DeepEqual(detachedReferenceResolverSourceTypes(hooks.Capabilities.DetachedReferenceResolvers), want) {
		t.Fatalf("DetachedReferenceResolvers = %#v, want sourceTypes %#v", hooks.Capabilities.DetachedReferenceResolvers, want)
	}

	if want := []string{"alpha_id", "beta_id"}; !reflect.DeepEqual(dynamicCORSRouteFamilyNames(hooks.Capabilities.DynamicCORSRouteFamilies), want) {
		t.Fatalf("DynamicCORSRouteFamilies = %#v, want names %#v", hooks.Capabilities.DynamicCORSRouteFamilies, want)
	}

	if want := []string{"alpha_openid", "beta_openid"}; !reflect.DeepEqual(oauthScopeRouteFamilyNames(hooks.Capabilities.OAuthScopeRouteFamilies), want) {
		t.Fatalf("OAuthScopeRouteFamilies = %#v, want names %#v", hooks.Capabilities.OAuthScopeRouteFamilies, want)
	}
	if got, want := hooks.Capabilities.OAuthScopeRouteFamilies[1].Scopes, []string{"openid"}; !reflect.DeepEqual(got, want) {
		t.Fatalf("OAuthScopeRouteFamilies[1].Scopes = %#v, want canonical scopes %#v", got, want)
	}

	if got, want := authChallengeProfileIDs(hooks.Capabilities.AuthChallengeProfiles), []string{"alpha_challenge", "beta_challenge"}; !reflect.DeepEqual(got, want) {
		t.Fatalf("AuthChallengeProfiles = %#v, want profile IDs %#v", hooks.Capabilities.AuthChallengeProfiles, want)
	}

	dialects := hooks.Dialects(DialectRegistrationContext{})
	if got, want := dialectNames(dialects), []string{"alpha", "beta"}; !reflect.DeepEqual(got, want) {
		t.Fatalf("Dialects names = %#v, want %#v", got, want)
	}
	if !reflect.DeepEqual(dialectCalls, []string{"alpha", "beta"}) {
		t.Fatalf("dialect calls = %#v, want alpha then beta", dialectCalls)
	}

	if err := hooks.ValidateComposition(context.Background(), nil, nil); err != nil {
		t.Fatalf("ValidateComposition: %v", err)
	}
	if !reflect.DeepEqual(validationCalls, []string{"alpha", "beta"}) {
		t.Fatalf("validation calls = %#v, want alpha then beta", validationCalls)
	}

	if err := hooks.RegisterLifecycle(ProductLifecycle{}); err != nil {
		t.Fatalf("RegisterLifecycle: %v", err)
	}
	if !reflect.DeepEqual(lifecycleCalls, []string{"alpha", "beta"}) {
		t.Fatalf("lifecycle calls = %#v, want alpha then beta", lifecycleCalls)
	}
}

func TestComposeProductDistributionRejectsProductDialectOwnedByDifferentPack(t *testing.T) {
	hooks, err := ComposeProductDistribution(NewProductDistribution(ProductHooks{
		OwningAppPacks: []string{"alpha-pack"},
		Dialects: func(DialectRegistrationContext) []dialect.Dialect {
			return []dialect.Dialect{fakeProductDialect{name: "alpha", pack: "beta-pack"}}
		},
	}))
	if err != nil {
		t.Fatalf("ComposeProductDistribution: %v", err)
	}

	requirePanicContains(t, func() {
		_ = hooks.Dialects(DialectRegistrationContext{})
	}, "product[0]", "alpha", "beta-pack", "ProductHooks.OwningAppPacks")
}

func TestComposeProductDistributionAcceptsCaseInsensitiveDialectOwner(t *testing.T) {
	hooks, err := ComposeProductDistribution(NewProductDistribution(ProductHooks{
		OwningAppPacks: []string{"Alpha-Pack"},
		Dialects: func(DialectRegistrationContext) []dialect.Dialect {
			return []dialect.Dialect{fakeProductDialect{name: "alpha", pack: "alpha-pack"}}
		},
	}))
	if err != nil {
		t.Fatalf("ComposeProductDistribution: %v", err)
	}

	if got, want := dialectNames(hooks.Dialects(DialectRegistrationContext{})), []string{"alpha"}; !reflect.DeepEqual(got, want) {
		t.Fatalf("Dialects names = %#v, want %#v", got, want)
	}
}

func TestComposeProductDistributionRejectsProductDialectWithoutOwningPack(t *testing.T) {
	hooks, err := ComposeProductDistribution(NewProductDistribution(ProductHooks{
		OwningAppPacks: []string{"alpha-pack"},
		Dialects: func(DialectRegistrationContext) []dialect.Dialect {
			return []dialect.Dialect{fakeProductDialect{name: "alpha"}}
		},
	}))
	if err != nil {
		t.Fatalf("ComposeProductDistribution: %v", err)
	}

	requirePanicContains(t, func() {
		_ = hooks.Dialects(DialectRegistrationContext{})
	}, "product[0]", "alpha", "requires non-empty owning pack")
}

func TestRegisterProductDistributionCapabilitiesSeedsHeadlessOAuthSessionIssuerClassPolicy(t *testing.T) {
	reset := auth.SetProductOAuthSessionIssuerClasses(nil)
	t.Cleanup(reset)

	_, err := RegisterProductDistributionCapabilities(NewProductDistribution(ProductHooks{
		OwningAppPacks: []string{"crm-pack"},
		Capabilities: ProductCapabilities{
			OAuthSessionIssuerClasses: []OAuthSessionIssuerClassRegistration{{Class: "Salesforce_Compat"}},
		},
	}))
	if err != nil {
		t.Fatalf("RegisterProductDistributionCapabilities: %v", err)
	}

	policy := auth.ProductOAuthSessionIssuerClassPolicy{}
	if !policy.AllowsConnectedAppSessionIssuerClass("salesforce_compat") {
		t.Fatal("headless product OAuth session issuer policy rejects registered product class")
	}
	if !policy.AllowsConnectedAppSessionIssuerClassForPack("salesforce_compat", "crm-pack") {
		t.Fatal("headless product OAuth session issuer policy rejects registered product class for owning pack")
	}
	if policy.AllowsConnectedAppSessionIssuerClassForPack("salesforce_compat", "other-pack") {
		t.Fatal("headless product OAuth session issuer policy allows product class for non-owning pack")
	}
	if policy.AllowsConnectedAppSessionIssuerClass("unregistered_product") {
		t.Fatal("headless product OAuth session issuer policy allows unregistered product class")
	}
}

func TestRegisterProductDistributionCapabilitiesSeedsHeadlessOAuthRouteScopePolicy(t *testing.T) {
	reset := auth.SetProductOAuthRouteScopeOwners(nil)
	t.Cleanup(reset)

	_, err := RegisterProductDistributionCapabilities(NewProductDistribution(ProductHooks{
		OwningAppPacks: []string{"crm-pack"},
		Capabilities: ProductCapabilities{
			OAuthRouteScopes: []OAuthRouteScopeRegistration{{Scope: "Vendor.Builtin"}},
		},
	}))
	if err != nil {
		t.Fatalf("RegisterProductDistributionCapabilities: %v", err)
	}

	policy := auth.ProductOAuthRouteScopePolicy{}
	if !policy.AllowsOAuthRouteScope("vendor.builtin") {
		t.Fatal("headless product OAuth route scope policy rejects registered product scope")
	}
	if !policy.AllowsOAuthRouteScopeForPack("vendor.builtin", "crm-pack") {
		t.Fatal("headless product OAuth route scope policy rejects registered product scope for owning pack")
	}
	if policy.AllowsOAuthRouteScopeForPack("vendor.builtin", "other-pack") {
		t.Fatal("headless product OAuth route scope policy allows product scope for non-owning pack")
	}
	if policy.AllowsOAuthRouteScope("issue.tracker") {
		t.Fatal("headless product OAuth route scope policy allows unregistered product scope")
	}
	if !policy.AllowsOAuthRouteScope("openid") {
		t.Fatal("headless product OAuth route scope policy rejects platform openid scope")
	}
}

func TestComposeProductDistributionRejectsHostPrimitiveVersionConflict(t *testing.T) {
	_, err := ComposeProductDistribution(NewProductDistribution(
		ProductHooks{Capabilities: ProductCapabilities{HostPrimitives: []HostPrimitive{{Kind: "renderer", Name: "app-pack:shared/action-renderer/common", Version: "v1"}}}},
		ProductHooks{Capabilities: ProductCapabilities{HostPrimitives: []HostPrimitive{{Kind: "Renderer", Name: "app-pack:shared/action-renderer/common", Version: "v2"}}}},
	))
	if err == nil {
		t.Fatal("ComposeProductDistribution succeeded; want host primitive version conflict")
	}
	if got := err.Error(); !strings.Contains(got, "product[1]") || !strings.Contains(got, "version") {
		t.Fatalf("error = %q, want product index and version conflict", got)
	}
}

func TestComposeProductDistributionCanonicalizesHostPrimitiveKind(t *testing.T) {
	hooks, err := ComposeProductDistribution(NewProductDistribution(
		ProductHooks{Capabilities: ProductCapabilities{HostPrimitives: []HostPrimitive{{Kind: " Renderer ", Name: " app-pack:shared/action-renderer/common ", Version: " v1 "}}}},
		ProductHooks{Capabilities: ProductCapabilities{HostPrimitives: []HostPrimitive{{Kind: "renderer", Name: "app-pack:shared/action-renderer/common", Version: "v1"}}}},
	))
	if err != nil {
		t.Fatalf("ComposeProductDistribution: %v", err)
	}

	want := []HostPrimitive{{Kind: "renderer", Name: "app-pack:shared/action-renderer/common", Version: "v1"}}
	if !reflect.DeepEqual(hooks.Capabilities.HostPrimitives, want) {
		t.Fatalf("Capabilities.HostPrimitives = %#v, want %#v", hooks.Capabilities.HostPrimitives, want)
	}
}

func TestComposeProductDistributionSkipsInactiveProductValidator(t *testing.T) {
	var validationCalls []string
	inactiveErr := errors.New("inactive product validator called")

	hooks, err := ComposeProductDistribution(NewProductDistribution(
		ProductHooks{
			OwningAppPacks: []string{"alpha-pack"},
			Capabilities: ProductCapabilities{
				HostPrimitives: []HostPrimitive{
					{Kind: "renderer", Name: "app-pack:alpha/action-renderer/open"},
				},
			},
			ValidateComposition: func(context.Context, *sql.DB, []ActiveAppPack) error {
				validationCalls = append(validationCalls, "alpha")
				return inactiveErr
			},
		},
		ProductHooks{
			OwningAppPacks: []string{"beta-pack"},
			ValidateComposition: func(context.Context, *sql.DB, []ActiveAppPack) error {
				validationCalls = append(validationCalls, "beta")
				return nil
			},
		},
	))
	if err != nil {
		t.Fatalf("ComposeProductDistribution: %v", err)
	}

	err = hooks.ValidateComposition(context.Background(), nil, []ActiveAppPack{{Name: "beta-pack"}})
	if err != nil {
		t.Fatalf("ValidateComposition = %v, want inactive alpha validator skipped", err)
	}
	if !reflect.DeepEqual(validationCalls, []string{"beta"}) {
		t.Fatalf("validation calls = %#v, want only beta", validationCalls)
	}
	if want := []string{"alpha-pack", "beta-pack"}; !reflect.DeepEqual(hooks.OwningAppPacks, want) {
		t.Fatalf("OwningAppPacks = %#v, want %#v", hooks.OwningAppPacks, want)
	}
}

func TestComposedProductHooksRejectsActivePacksWhenNoCompiledProductPackIsActive(t *testing.T) {
	var validationCalls []string
	hooks, err := ComposeProductDistribution(NewProductDistribution(
		ProductHooks{
			OwningAppPacks: []string{"alpha-pack"},
			ValidateComposition: func(context.Context, *sql.DB, []ActiveAppPack) error {
				validationCalls = append(validationCalls, "alpha")
				return nil
			},
		},
	))
	if err != nil {
		t.Fatalf("ComposeProductDistribution: %v", err)
	}

	err = hooks.ValidateComposition(context.Background(), nil, []ActiveAppPack{{Name: "companion-pack"}})
	if err == nil || !strings.Contains(err.Error(), "bundles product app pack(s) alpha-pack") || !strings.Contains(err.Error(), "companion-pack") {
		t.Fatalf("ValidateComposition = %v, want no compiled product pack active error", err)
	}
	if len(validationCalls) != 0 {
		t.Fatalf("validation calls = %#v, want distribution coverage guard before product validators", validationCalls)
	}
}

func TestComposedProductHooksRejectsKnownProductPackNotCompiledIntoDistribution(t *testing.T) {
	var validationCalls []string
	hooks, err := ComposeProductDistribution(NewProductDistribution(
		ProductHooks{
			OwningAppPacks:       []string{"alpha-pack"},
			KnownProductAppPacks: []string{"alpha-pack", "beta-pack"},
			ValidateComposition: func(context.Context, *sql.DB, []ActiveAppPack) error {
				validationCalls = append(validationCalls, "alpha")
				return nil
			},
		},
	))
	if err != nil {
		t.Fatalf("ComposeProductDistribution: %v", err)
	}

	err = hooks.ValidateComposition(context.Background(), nil, []ActiveAppPack{{Name: "alpha-pack"}, {Name: "beta-pack"}, {Name: "companion-pack"}})
	if err == nil || !strings.Contains(err.Error(), "active first-party product app pack(s) beta-pack") || !strings.Contains(err.Error(), "compiled product app pack(s): alpha-pack") {
		t.Fatalf("ValidateComposition = %v, want known product pack not compiled error", err)
	}
	if len(validationCalls) != 0 {
		t.Fatalf("validation calls = %#v, want distribution coverage guard before product validators", validationCalls)
	}
}

func TestComposedProductHooksAllowsUnknownCompanionAlongsideCompiledProductPack(t *testing.T) {
	var validationCalls []string
	hooks, err := ComposeProductDistribution(NewProductDistribution(
		ProductHooks{
			OwningAppPacks:       []string{"alpha-pack"},
			KnownProductAppPacks: []string{"alpha-pack", "beta-pack"},
			ValidateComposition: func(context.Context, *sql.DB, []ActiveAppPack) error {
				validationCalls = append(validationCalls, "alpha")
				return nil
			},
		},
	))
	if err != nil {
		t.Fatalf("ComposeProductDistribution: %v", err)
	}

	err = hooks.ValidateComposition(context.Background(), nil, []ActiveAppPack{{Name: "alpha-pack"}, {Name: "companion-pack"}})
	if err != nil {
		t.Fatalf("ValidateComposition = %v, want unknown companion allowed with compiled product pack active", err)
	}
	if !reflect.DeepEqual(validationCalls, []string{"alpha"}) {
		t.Fatalf("validation calls = %#v, want alpha validator", validationCalls)
	}
}

func TestComposeProductDistributionRejectsDuplicateFlowHandlerRef(t *testing.T) {
	_, err := ComposeProductDistribution(NewProductDistribution(
		ProductHooks{Capabilities: ProductCapabilities{FlowActionCallHandlerImplementations: []automation.FlowActionCallHandlerImplementation{{HandlerRef: "app:automation/do-work", Build: fakeFlowActionBuilder}}}},
		ProductHooks{Capabilities: ProductCapabilities{FlowActionCallHandlerImplementations: []automation.FlowActionCallHandlerImplementation{{HandlerRef: "app:automation/do-work", Build: fakeFlowActionBuilder}}}},
	))
	if err == nil {
		t.Fatal("ComposeProductDistribution succeeded; want duplicate handlerRef conflict")
	}
	if got := err.Error(); !strings.Contains(got, "product[1]") || !strings.Contains(got, "handlerRef") || !strings.Contains(got, "product[0]") {
		t.Fatalf("error = %q, want duplicate handlerRef with product indexes", got)
	}
}

func TestComposeProductDistributionRejectsDuplicatePublicLoginProvider(t *testing.T) {
	_, err := ComposeProductDistribution(NewProductDistribution(
		ProductHooks{
			OwningAppPacks: []string{"alpha-pack"},
			Capabilities: ProductCapabilities{
				PublicLoginProviders: []PublicLoginProviderRegistration{fakePublicLoginProvider("alpha-login")},
			},
		},
		ProductHooks{
			OwningAppPacks: []string{"beta-pack"},
			Capabilities: ProductCapabilities{
				PublicLoginProviders: []PublicLoginProviderRegistration{fakePublicLoginProvider("Alpha_Login")},
			},
		},
	))
	if err == nil {
		t.Fatal("ComposeProductDistribution succeeded; want duplicate public login provider conflict")
	}
	if got := err.Error(); !strings.Contains(got, "product[1]") || !strings.Contains(got, "public login provider") || !strings.Contains(got, "product[0]") {
		t.Fatalf("error = %q, want duplicate public login provider with product indexes", got)
	}
}

func TestComposeProductDistributionRejectsPublicLoginProviderAliasProviderIDConflict(t *testing.T) {
	_, err := ComposeProductDistribution(NewProductDistribution(
		ProductHooks{
			OwningAppPacks: []string{"alpha-pack"},
			Capabilities: ProductCapabilities{
				PublicLoginProviders: []PublicLoginProviderRegistration{fakePublicLoginProviderWithAliases("alpha_login", "beta-login")},
			},
		},
		ProductHooks{
			OwningAppPacks: []string{"beta-pack"},
			Capabilities: ProductCapabilities{
				PublicLoginProviders: []PublicLoginProviderRegistration{fakePublicLoginProvider("beta_login")},
			},
		},
	))
	if err == nil {
		t.Fatal("ComposeProductDistribution succeeded; want public login provider alias/provider-id conflict")
	}
	if got := err.Error(); !strings.Contains(got, "alias") || !strings.Contains(got, "provider id") || !strings.Contains(got, "product[1]") {
		t.Fatalf("error = %q, want alias/provider-id conflict with product indexes", got)
	}
}

func TestComposeProductDistributionRejectsDuplicatePublicLoginProviderAlias(t *testing.T) {
	_, err := ComposeProductDistribution(NewProductDistribution(
		ProductHooks{
			OwningAppPacks: []string{"alpha-pack"},
			Capabilities: ProductCapabilities{
				PublicLoginProviders: []PublicLoginProviderRegistration{fakePublicLoginProviderWithAliases("alpha_login", "shared-login")},
			},
		},
		ProductHooks{
			OwningAppPacks: []string{"beta-pack"},
			Capabilities: ProductCapabilities{
				PublicLoginProviders: []PublicLoginProviderRegistration{fakePublicLoginProviderWithAliases("beta_login", "shared_login")},
			},
		},
	))
	if err == nil {
		t.Fatal("ComposeProductDistribution succeeded; want duplicate public login provider alias conflict")
	}
	if got := err.Error(); !strings.Contains(got, "alias") || !strings.Contains(got, "duplicates") || !strings.Contains(got, "product[0]") {
		t.Fatalf("error = %q, want duplicate alias conflict with product indexes", got)
	}
}

func TestComposeProductDistributionRejectsPublicLoginProviderBuiltinIDConflict(t *testing.T) {
	_, err := ComposeProductDistribution(NewProductDistribution(
		ProductHooks{
			OwningAppPacks: []string{"alpha-pack"},
			Capabilities: ProductCapabilities{
				PublicLoginProviders: []PublicLoginProviderRegistration{fakePublicLoginProvider("google-oauth")},
			},
		},
	))
	if err == nil {
		t.Fatal("ComposeProductDistribution succeeded; want built-in public login provider ID conflict")
	}
	if got := err.Error(); !strings.Contains(got, "built-in login provider") || !strings.Contains(got, "google") {
		t.Fatalf("error = %q, want built-in login provider ID conflict", got)
	}
}

func TestComposeProductDistributionRejectsPublicLoginProviderBuiltinAliasConflict(t *testing.T) {
	_, err := ComposeProductDistribution(NewProductDistribution(
		ProductHooks{
			OwningAppPacks: []string{"alpha-pack"},
			Capabilities: ProductCapabilities{
				PublicLoginProviders: []PublicLoginProviderRegistration{fakePublicLoginProviderWithAliases("alpha_login", "password")},
			},
		},
	))
	if err == nil {
		t.Fatal("ComposeProductDistribution succeeded; want built-in public login provider alias conflict")
	}
	if got := err.Error(); !strings.Contains(got, "alias") || !strings.Contains(got, "built-in login provider") || !strings.Contains(got, "backend") {
		t.Fatalf("error = %q, want built-in login provider alias conflict", got)
	}
}

func TestComposeProductDistributionRejectsPublicLoginProviderWithoutOwningPack(t *testing.T) {
	_, err := ComposeProductDistribution(NewProductDistribution(
		ProductHooks{
			Capabilities: ProductCapabilities{
				PublicLoginProviders: []PublicLoginProviderRegistration{fakePublicLoginProvider("alpha_login")},
			},
		},
	))
	if err == nil {
		t.Fatal("ComposeProductDistribution succeeded; want public login provider owner-pack error")
	}
	if got := err.Error(); !strings.Contains(got, "public login provider") || !strings.Contains(got, "requires owning app pack") {
		t.Fatalf("error = %q, want public login provider owner-pack details", got)
	}
}

func TestComposeProductDistributionRejectsPublicLoginProviderWithoutDescriptorBuilder(t *testing.T) {
	_, err := ComposeProductDistribution(NewProductDistribution(
		ProductHooks{
			OwningAppPacks: []string{"crm-pack"},
			Capabilities: ProductCapabilities{
				PublicLoginProviders: []PublicLoginProviderRegistration{{ProviderID: "alpha_login"}},
			},
		},
	))
	if err == nil {
		t.Fatal("ComposeProductDistribution succeeded; want public login provider descriptor-builder error")
	}
	if got := err.Error(); !strings.Contains(got, "public login provider") || !strings.Contains(got, "requires descriptor builder") {
		t.Fatalf("error = %q, want public login provider descriptor-builder details", got)
	}
}

func TestComposeProductDistributionRejectsDuplicateOAuthUserInfoProfile(t *testing.T) {
	_, err := ComposeProductDistribution(NewProductDistribution(
		ProductHooks{
			OwningAppPacks: []string{"alpha-pack"},
			Capabilities: ProductCapabilities{
				OAuthUserInfoProfiles: []OAuthUserInfoProfileRegistration{{ProfileID: "salesforce-compatible", Applies: fakeUserInfoProfileApplies, Build: fakeUserInfoProfileBuilder}},
			},
		},
		ProductHooks{
			OwningAppPacks: []string{"beta-pack"},
			Capabilities: ProductCapabilities{
				OAuthUserInfoProfiles: []OAuthUserInfoProfileRegistration{{ProfileID: "Salesforce-Compatible", Applies: fakeUserInfoProfileApplies, Build: fakeUserInfoProfileBuilder}},
			},
		},
	))
	if err == nil {
		t.Fatal("ComposeProductDistribution succeeded; want duplicate OAuth userinfo profile conflict")
	}
	if got := err.Error(); !strings.Contains(got, "product[1]") || !strings.Contains(got, "OAuth userinfo profile") || !strings.Contains(got, "product[0]") {
		t.Fatalf("error = %q, want duplicate OAuth userinfo profile with product indexes", got)
	}
}

func TestComposeProductDistributionRejectsDuplicateOAuthSessionIssuerClass(t *testing.T) {
	_, err := ComposeProductDistribution(NewProductDistribution(
		ProductHooks{
			OwningAppPacks: []string{"alpha-pack"},
			Capabilities: ProductCapabilities{
				OAuthSessionIssuerClasses: []OAuthSessionIssuerClassRegistration{{Class: "salesforce-compatible"}},
			},
		},
		ProductHooks{
			OwningAppPacks: []string{"beta-pack"},
			Capabilities: ProductCapabilities{
				OAuthSessionIssuerClasses: []OAuthSessionIssuerClassRegistration{{Class: "Salesforce_Compatible"}},
			},
		},
	))
	if err == nil {
		t.Fatal("ComposeProductDistribution succeeded; want duplicate OAuth session issuer class conflict")
	}
	if got := err.Error(); !strings.Contains(got, "product[1]") || !strings.Contains(got, "OAuth session issuer class") || !strings.Contains(got, "product[0]") {
		t.Fatalf("error = %q, want duplicate OAuth session issuer class with product indexes", got)
	}
}

func TestComposeProductDistributionRejectsDuplicateOAuthTokenResponseProfile(t *testing.T) {
	_, err := ComposeProductDistribution(NewProductDistribution(
		ProductHooks{
			OwningAppPacks: []string{"alpha-pack"},
			Capabilities: ProductCapabilities{
				OAuthTokenResponseProfiles: []OAuthTokenResponseProfileRegistration{{ProfileID: "salesforce-compatible", Applies: fakeTokenResponseProfileApplies, Build: fakeTokenResponseProfileBuilder}},
			},
		},
		ProductHooks{
			OwningAppPacks: []string{"beta-pack"},
			Capabilities: ProductCapabilities{
				OAuthTokenResponseProfiles: []OAuthTokenResponseProfileRegistration{{ProfileID: "Salesforce-Compatible", Applies: fakeTokenResponseProfileApplies, Build: fakeTokenResponseProfileBuilder}},
			},
		},
	))
	if err == nil {
		t.Fatal("ComposeProductDistribution succeeded; want duplicate OAuth token response profile conflict")
	}
	if got := err.Error(); !strings.Contains(got, "product[1]") || !strings.Contains(got, "OAuth token response profile") || !strings.Contains(got, "product[0]") {
		t.Fatalf("error = %q, want duplicate OAuth token response profile with product indexes", got)
	}
}

func TestComposedOAuthTokenResponseProfilesRejectOverlappingMatches(t *testing.T) {
	hooks, err := ComposeProductDistribution(NewProductDistribution(
		ProductHooks{
			OwningAppPacks: []string{"alpha-pack"},
			Capabilities: ProductCapabilities{
				OAuthTokenResponseProfiles: []OAuthTokenResponseProfileRegistration{
					{ProfileID: "alpha-compatible", Applies: fakeTokenResponseProfileApplies, Build: fakeTokenResponseProfileBuilder},
				},
			},
		},
		ProductHooks{
			OwningAppPacks: []string{"beta-pack"},
			Capabilities: ProductCapabilities{
				OAuthTokenResponseProfiles: []OAuthTokenResponseProfileRegistration{
					{ProfileID: "beta-compatible", Applies: fakeTokenResponseProfileApplies, Build: fakeTokenResponseProfileBuilder},
				},
			},
		},
	))
	if err != nil {
		t.Fatalf("ComposeProductDistribution: %v", err)
	}
	var profiles []auth.TokenResponseProfileRegistration
	for _, profile := range hooks.Capabilities.OAuthTokenResponseProfiles {
		profiles = append(profiles, auth.TokenResponseProfileRegistration{
			ProfileID: profile.ProfileID, Applies: profile.Applies, Build: profile.Build,
		})
	}
	if got, want := activeOAuthTokenResponseProfileIDs(profiles), []string{"alpha_compatible", "beta_compatible"}; !reflect.DeepEqual(got, want) {
		t.Fatalf("active OAuth token response profiles = %#v, want %#v", got, want)
	}

	claims, err := auth.BuildTokenResponseProfileClaims(auth.TokenResponseProfileContext{}, profiles)
	if !errors.Is(err, auth.ErrTokenResponseProfileConflict) {
		t.Fatalf("BuildTokenResponseProfileClaims claims=%#v err=%v, want ErrTokenResponseProfileConflict", claims, err)
	}
}

func TestComposeProductDistributionRejectsOAuthTokenResponseProfileWithoutBuilderOrOwningPack(t *testing.T) {
	for _, tc := range []struct {
		name    string
		product ProductHooks
		want    string
	}{
		{
			name: "missing request selector",
			product: ProductHooks{
				OwningAppPacks: []string{"alpha-pack"},
				Capabilities: ProductCapabilities{
					OAuthTokenResponseProfiles: []OAuthTokenResponseProfileRegistration{{ProfileID: "salesforce-compatible", Build: fakeTokenResponseProfileBuilder}},
				},
			},
			want: "requires request selector",
		},
		{
			name: "missing builder",
			product: ProductHooks{
				OwningAppPacks: []string{"alpha-pack"},
				Capabilities: ProductCapabilities{
					OAuthTokenResponseProfiles: []OAuthTokenResponseProfileRegistration{{ProfileID: "salesforce-compatible", Applies: fakeTokenResponseProfileApplies}},
				},
			},
			want: "requires builder",
		},
		{
			name: "missing owning pack",
			product: ProductHooks{
				Capabilities: ProductCapabilities{
					OAuthTokenResponseProfiles: []OAuthTokenResponseProfileRegistration{{ProfileID: "salesforce-compatible", Applies: fakeTokenResponseProfileApplies, Build: fakeTokenResponseProfileBuilder}},
				},
			},
			want: "requires owning app pack",
		},
	} {
		t.Run(tc.name, func(t *testing.T) {
			_, err := ComposeProductDistribution(NewProductDistribution(tc.product))
			if err == nil {
				t.Fatal("ComposeProductDistribution succeeded; want OAuth token response profile error")
			}
			if got := err.Error(); !strings.Contains(got, "OAuth token response profile") || !strings.Contains(got, tc.want) {
				t.Fatalf("error = %q, want %q", got, tc.want)
			}
		})
	}
}

func TestComposeProductDistributionRejectsOAuthSessionIssuerClassWithoutClassOrOwningPack(t *testing.T) {
	for _, tc := range []struct {
		name    string
		product ProductHooks
		want    string
	}{
		{
			name: "missing class",
			product: ProductHooks{
				OwningAppPacks: []string{"alpha-pack"},
				Capabilities: ProductCapabilities{
					OAuthSessionIssuerClasses: []OAuthSessionIssuerClassRegistration{{}},
				},
			},
			want: "requires class",
		},
		{
			name: "missing owning pack",
			product: ProductHooks{
				Capabilities: ProductCapabilities{
					OAuthSessionIssuerClasses: []OAuthSessionIssuerClassRegistration{{Class: "salesforce-compatible"}},
				},
			},
			want: "requires owning app pack",
		},
	} {
		t.Run(tc.name, func(t *testing.T) {
			_, err := ComposeProductDistribution(NewProductDistribution(tc.product))
			if err == nil {
				t.Fatal("ComposeProductDistribution succeeded; want OAuth session issuer class error")
			}
			if got := err.Error(); !strings.Contains(got, "OAuth session issuer class") || !strings.Contains(got, tc.want) {
				t.Fatalf("error = %q, want %q", got, tc.want)
			}
		})
	}
}

func TestComposeProductDistributionRejectsOAuthUserInfoProfileWithoutBuilderOrOwningPack(t *testing.T) {
	for _, tc := range []struct {
		name    string
		product ProductHooks
		want    string
	}{
		{
			name: "missing request selector",
			product: ProductHooks{
				OwningAppPacks: []string{"alpha-pack"},
				Capabilities: ProductCapabilities{
					OAuthUserInfoProfiles: []OAuthUserInfoProfileRegistration{{ProfileID: "salesforce-compatible", Build: fakeUserInfoProfileBuilder}},
				},
			},
			want: "requires request selector",
		},
		{
			name: "missing builder",
			product: ProductHooks{
				OwningAppPacks: []string{"alpha-pack"},
				Capabilities: ProductCapabilities{
					OAuthUserInfoProfiles: []OAuthUserInfoProfileRegistration{{ProfileID: "salesforce-compatible", Applies: fakeUserInfoProfileApplies}},
				},
			},
			want: "requires builder",
		},
		{
			name: "missing owning pack",
			product: ProductHooks{
				Capabilities: ProductCapabilities{
					OAuthUserInfoProfiles: []OAuthUserInfoProfileRegistration{{ProfileID: "salesforce-compatible", Applies: fakeUserInfoProfileApplies, Build: fakeUserInfoProfileBuilder}},
				},
			},
			want: "requires owning app pack",
		},
	} {
		t.Run(tc.name, func(t *testing.T) {
			_, err := ComposeProductDistribution(NewProductDistribution(tc.product))
			if err == nil {
				t.Fatal("ComposeProductDistribution succeeded; want OAuth userinfo profile error")
			}
			if got := err.Error(); !strings.Contains(got, "OAuth userinfo profile") || !strings.Contains(got, tc.want) {
				t.Fatalf("error = %q, want %q", got, tc.want)
			}
		})
	}
}

func TestComposeProductDistributionMergesSourceScopedDetachedReferenceResolvers(t *testing.T) {
	alphaResolver := func(reference string) (contract.FileBlobReference, bool) {
		return contract.FileBlobReference{StorageKey: "alpha/" + reference}, true
	}
	betaResolver := func(reference string) (contract.FileBlobReference, bool) {
		return contract.FileBlobReference{StorageKey: "beta/" + reference}, true
	}

	hooks, err := ComposeProductDistribution(NewProductDistribution(
		ProductHooks{
			OwningAppPacks: []string{"alpha-pack"},
			Capabilities: ProductCapabilities{
				DetachedReferenceResolvers: []DetachedReferenceResolverRegistration{
					{SourceType: "Alpha_Compat", Resolver: alphaResolver, OwningAppPacks: []string{"alpha-pack"}},
				},
			},
		},
		ProductHooks{
			OwningAppPacks: []string{"beta-pack"},
			Capabilities: ProductCapabilities{
				DetachedReferenceResolvers: []DetachedReferenceResolverRegistration{
					{SourceType: "beta_compat", Resolver: betaResolver, OwningAppPacks: []string{"beta-pack"}},
				},
			},
		},
	))
	if err != nil {
		t.Fatalf("ComposeProductDistribution: %v", err)
	}

	want := []DetachedReferenceResolverRegistration{
		{SourceType: "alpha_compat", Resolver: alphaResolver, OwningAppPacks: []string{"alpha-pack"}, owningAppPacks: []string{"alpha-pack"}},
		{SourceType: "beta_compat", Resolver: betaResolver, OwningAppPacks: []string{"beta-pack"}, owningAppPacks: []string{"beta-pack"}},
	}
	if len(hooks.Capabilities.DetachedReferenceResolvers) != len(want) {
		t.Fatalf("Capabilities.DetachedReferenceResolvers = %#v, want %#v", hooks.Capabilities.DetachedReferenceResolvers, want)
	}
	for i := range want {
		if hooks.Capabilities.DetachedReferenceResolvers[i].SourceType != want[i].SourceType {
			t.Fatalf("Capabilities.DetachedReferenceResolvers[%d].SourceType = %q, want %q", i, hooks.Capabilities.DetachedReferenceResolvers[i].SourceType, want[i].SourceType)
		}
		if !reflect.DeepEqual(hooks.Capabilities.DetachedReferenceResolvers[i].OwningAppPacks, want[i].OwningAppPacks) {
			t.Fatalf("Capabilities.DetachedReferenceResolvers[%d].OwningAppPacks = %#v, want %#v", i, hooks.Capabilities.DetachedReferenceResolvers[i].OwningAppPacks, want[i].OwningAppPacks)
		}
		if !reflect.DeepEqual(hooks.Capabilities.DetachedReferenceResolvers[i].owningAppPacks, want[i].owningAppPacks) {
			t.Fatalf("Capabilities.DetachedReferenceResolvers[%d].owningAppPacks = %#v, want %#v", i, hooks.Capabilities.DetachedReferenceResolvers[i].owningAppPacks, want[i].owningAppPacks)
		}
		ref, ok := hooks.Capabilities.DetachedReferenceResolvers[i].Resolver("blob")
		if !ok {
			t.Fatalf("Capabilities.DetachedReferenceResolvers[%d].Resolver ok=false, want true", i)
		}
		wantStoragePrefix := strings.TrimSuffix(want[i].SourceType, "_compat")
		if ref.StorageKey != wantStoragePrefix+"/blob" {
			t.Fatalf("DetachedReferenceResolvers[%d] StorageKey = %q, want %s/blob", i, ref.StorageKey, wantStoragePrefix)
		}
	}
}

func TestComposeProductDistributionRejectsDetachedReferenceResolverWithoutOwningPack(t *testing.T) {
	_, err := ComposeProductDistribution(NewProductDistribution(ProductHooks{
		OwningAppPacks: []string{"content-pack"},
		Capabilities: ProductCapabilities{
			DetachedReferenceResolvers: []DetachedReferenceResolverRegistration{
				{SourceType: "content_compat", Resolver: fakeDetachedReferenceResolver},
			},
		},
	}))
	if err == nil {
		t.Fatal("ComposeProductDistribution succeeded; want detached resolver owning app pack error")
	}
	if got := err.Error(); !strings.Contains(got, "detached reference resolver") || !strings.Contains(got, "requires owning app pack") {
		t.Fatalf("error = %q, want detached resolver owning app pack failure", got)
	}
}

func TestComposeProductDistributionRejectsDetachedReferenceResolverOwnerOutsideProduct(t *testing.T) {
	_, err := ComposeProductDistribution(NewProductDistribution(ProductHooks{
		OwningAppPacks: []string{"alpha-pack"},
		Capabilities: ProductCapabilities{
			DetachedReferenceResolvers: []DetachedReferenceResolverRegistration{
				{SourceType: "alpha_compat", Resolver: fakeDetachedReferenceResolver, OwningAppPacks: []string{"beta-pack"}},
			},
		},
	}))
	if err == nil {
		t.Fatal("ComposeProductDistribution succeeded; want detached resolver owner boundary error")
	}
	if got := err.Error(); !strings.Contains(got, "detached reference resolver") || !strings.Contains(got, "outside ProductHooks.OwningAppPacks") {
		t.Fatalf("error = %q, want detached resolver owner boundary failure", got)
	}
}

func TestComposeProductDistributionRejectsDuplicateDetachedReferenceSourceType(t *testing.T) {
	_, err := ComposeProductDistribution(NewProductDistribution(
		ProductHooks{
			OwningAppPacks: []string{"alpha-pack"},
			Capabilities: ProductCapabilities{
				DetachedReferenceResolvers: []DetachedReferenceResolverRegistration{
					{SourceType: "content_compat", Resolver: fakeDetachedReferenceResolver, OwningAppPacks: []string{"alpha-pack"}},
				},
			},
		},
		ProductHooks{
			OwningAppPacks: []string{"beta-pack"},
			Capabilities: ProductCapabilities{
				DetachedReferenceResolvers: []DetachedReferenceResolverRegistration{
					{SourceType: "Content_Compat", Resolver: fakeDetachedReferenceResolver, OwningAppPacks: []string{"beta-pack"}},
				},
			},
		},
	))
	if err == nil {
		t.Fatal("ComposeProductDistribution succeeded; want duplicate detached resolver sourceType conflict")
	}
	if got := err.Error(); !strings.Contains(got, "product[1]") || !strings.Contains(got, "sourceType") || !strings.Contains(got, "product[0]") {
		t.Fatalf("error = %q, want duplicate sourceType with product indexes", got)
	}
}

func TestComposeProductDistributionMergesNoSubscriberRecordEventObjects(t *testing.T) {
	hooks, err := ComposeProductDistribution(NewProductDistribution(
		ProductHooks{
			Capabilities: ProductCapabilities{
				NoSubscriberRecordEventObjects: []string{" IssueWorklog ", "IssueWorklog"},
			},
		},
		ProductHooks{
			Capabilities: ProductCapabilities{
				NoSubscriberRecordEventObjects: []string{"IssueComment"},
			},
		},
	))
	if err != nil {
		t.Fatalf("ComposeProductDistribution: %v", err)
	}
	want := []string{"IssueWorklog", "IssueComment"}
	if !reflect.DeepEqual(hooks.Capabilities.NoSubscriberRecordEventObjects, want) {
		t.Fatalf("NoSubscriberRecordEventObjects = %#v, want %#v", hooks.Capabilities.NoSubscriberRecordEventObjects, want)
	}
}

func TestComposedProductHooksWrapValidationAndLifecycleErrors(t *testing.T) {
	validationErr := errors.New("validation boom")
	lifecycleErr := errors.New("lifecycle boom")

	hooks, err := ComposeProductDistribution(NewProductDistribution(
		ProductHooks{},
		ProductHooks{
			ValidateComposition: func(context.Context, *sql.DB, []ActiveAppPack) error {
				return validationErr
			},
			RegisterLifecycle: func(ProductLifecycle) error {
				return lifecycleErr
			},
		},
	))
	if err != nil {
		t.Fatalf("ComposeProductDistribution: %v", err)
	}

	if err := hooks.ValidateComposition(context.Background(), nil, nil); err == nil || !strings.Contains(err.Error(), "product[1] composition validation") || !errors.Is(err, validationErr) {
		t.Fatalf("ValidateComposition error = %v, want wrapped product[1] validation error", err)
	}
	if err := hooks.RegisterLifecycle(ProductLifecycle{}); err == nil || !strings.Contains(err.Error(), "product[1] lifecycle registration") || !errors.Is(err, lifecycleErr) {
		t.Fatalf("RegisterLifecycle error = %v, want wrapped product[1] lifecycle error", err)
	}
}

const testWriteValidatorCapability = "test.record-write.title-required"

func fakeFlowActionBuilder(automation.FlowActionCallRegistrationDescriptor) (automation.FlowActionCallRegistration, error) {
	return automation.FlowActionCallRegistration{}, nil
}

func fakeDetachedReferenceResolver(string) (contract.FileBlobReference, bool) {
	return contract.FileBlobReference{}, false
}

func fakeUserInfoProfileBuilder(auth.UserInfoProfileContext) map[string]any {
	return map[string]any{"compat": true}
}

func fakeUserInfoProfileApplies(auth.UserInfoProfileContext) bool {
	return true
}

func fakeTokenResponseProfileBuilder(auth.TokenResponseProfileContext) map[string]any {
	return map[string]any{"token_compat": true}
}

func fakeTokenResponseProfileApplies(auth.TokenResponseProfileContext) bool {
	return true
}

func fakeDynamicCORSRoutePredicate(path string) func(DynamicCORSRouteContext) bool {
	return func(ctx DynamicCORSRouteContext) bool {
		return ctx.Path == path && ctx.RequestedMethod == http.MethodGet
	}
}

func fakeOAuthScopeRoutePredicate(path string) func(OAuthScopeRouteContext) bool {
	return func(ctx OAuthScopeRouteContext) bool {
		return ctx.FullPath == path && ctx.Method == http.MethodGet
	}
}

func fakeAuthChallengeBuilder(code string) func(AuthChallengeContext) (AuthChallenge, bool) {
	return func(ctx AuthChallengeContext) (AuthChallenge, bool) {
		if !ctx.MissingToken {
			return AuthChallenge{}, false
		}
		return AuthChallenge{Status: http.StatusForbidden, Code: code, Message: code}, true
	}
}

func fakePublicLoginProvider(providerID string) PublicLoginProviderRegistration {
	return fakePublicLoginProviderWithAliases(providerID)
}

func fakePublicLoginProviderWithAliases(providerID string, aliases ...string) PublicLoginProviderRegistration {
	return PublicLoginProviderRegistration{
		ProviderID: providerID,
		Aliases:    aliases,
		Build: func(auth.PublicLoginProviderContext) auth.PublicLoginProviderDescriptor {
			return auth.PublicLoginProviderDescriptor{
				Kind:      "test",
				Label:     providerID,
				Placement: "advanced",
			}
		},
	}
}

func publicLoginProviderIDs(providers []PublicLoginProviderRegistration) []string {
	out := make([]string, 0, len(providers))
	for _, provider := range providers {
		out = append(out, provider.ProviderID)
	}
	return out
}

func oauthUserInfoProfileIDs(profiles []OAuthUserInfoProfileRegistration) []string {
	out := make([]string, 0, len(profiles))
	for _, profile := range profiles {
		out = append(out, profile.ProfileID)
	}
	return out
}

func oauthSessionIssuerClassNames(classes []OAuthSessionIssuerClassRegistration) []string {
	out := make([]string, 0, len(classes))
	for _, class := range classes {
		out = append(out, class.Class)
	}
	return out
}

func oauthTokenResponseProfileIDs(profiles []OAuthTokenResponseProfileRegistration) []string {
	out := make([]string, 0, len(profiles))
	for _, profile := range profiles {
		out = append(out, profile.ProfileID)
	}
	return out
}

func activeOAuthTokenResponseProfileIDs(profiles []auth.TokenResponseProfileRegistration) []string {
	out := make([]string, 0, len(profiles))
	for _, profile := range profiles {
		out = append(out, profile.ProfileID)
	}
	return out
}

func detachedReferenceResolverSourceTypes(resolvers []DetachedReferenceResolverRegistration) []string {
	out := make([]string, 0, len(resolvers))
	for _, resolver := range resolvers {
		out = append(out, resolver.SourceType)
	}
	return out
}

func dynamicCORSRouteFamilyNames(families []DynamicCORSRouteFamilyRegistration) []string {
	out := make([]string, 0, len(families))
	for _, family := range families {
		out = append(out, family.Name)
	}
	return out
}

func oauthScopeRouteFamilyNames(families []OAuthScopeRouteFamilyRegistration) []string {
	out := make([]string, 0, len(families))
	for _, family := range families {
		out = append(out, family.Name)
	}
	return out
}

func authChallengeProfileIDs(profiles []AuthChallengeProfileRegistration) []string {
	out := make([]string, 0, len(profiles))
	for _, profile := range profiles {
		out = append(out, profile.ProfileID)
	}
	return out
}

func dialectNames(dialects []dialect.Dialect) []string {
	names := make([]string, 0, len(dialects))
	for _, d := range dialects {
		names = append(names, d.Name())
	}
	return names
}

func requirePanicContains(t *testing.T, fn func(), wants ...string) {
	t.Helper()

	defer func() {
		recovered := recover()
		if recovered == nil {
			t.Fatal("function did not panic")
		}
		got, ok := recovered.(string)
		if !ok {
			t.Fatalf("panic = %#v, want string panic", recovered)
		}
		for _, want := range wants {
			if !strings.Contains(got, want) {
				t.Fatalf("panic = %q, want substring %q", got, want)
			}
		}
	}()

	fn()
}

type fakeProductDialect struct {
	name string
	pack string
}

func (d fakeProductDialect) Name() string { return d.name }

func (d fakeProductDialect) OwningPack() string { return d.pack }

func (fakeProductDialect) BasePath() string { return "" }

func (fakeProductDialect) Versions() []string { return []string{"v1"} }

func (fakeProductDialect) Manifest() []dialect.Route { return nil }

func (fakeProductDialect) ErrorProjector() dialect.Projector { return nil }

func (fakeProductDialect) InternalErrorProjector() dialect.InternalErrorProjector { return nil }
