package packs

import (
	"context"
	"encoding/json"
	"fmt"
	"strings"
)

func newSourceBackedOAuthCustomScopeComponent(packName string, component ManifestComponent, payload []byte) (PackComponent, error) {
	return newSourceBackedOAuthCustomScopeComponentWithCompat(packName, component, payload, false)
}

func newSourceBackedOAuthCustomScopeComponentWithCompat(packName string, component ManifestComponent, payload []byte, allowLegacyMissingRouteFamilyPrefix bool) (PackComponent, error) {
	seed, err := parseOAuthCustomScopeComponentPayload(payload, componentPayloadLabel(component), allowLegacyMissingRouteFamilyPrefix)
	if err != nil {
		return nil, err
	}
	if businessKey := strings.TrimSpace(component.BusinessKey); businessKey != seed.Name {
		return nil, fmt.Errorf("oauth_custom_scope businessKey %q must match payload name %q", businessKey, seed.Name)
	}
	seed.OwnerPack = strings.TrimSpace(packName)
	return &sourceBackedManagedComponent{
		packName:          packName,
		manifestComponent: component,
		payload:           append([]byte(nil), payload...),
		expectedProjection: func() (any, error) {
			return seed, nil
		},
		liveProjection: func(ctx context.Context, deps VerifyDeps) (any, error) {
			return loadOAuthCustomScopeSeed(ctx, deps.DB, seed.Name)
		},
		apply: func(ctx context.Context, deps ApplyDeps) error {
			return upsertOAuthCustomScopeSeed(ctx, deps.DB, seed)
		},
	}, nil
}

func parseOAuthCustomScopeComponentPayload(payload []byte, source string, allowLegacyMissingRouteFamilyPrefix bool) (bootstrapOAuthCustomScope, error) {
	var wrapped bootstrapOAuthScopesData
	if err := json.Unmarshal(payload, &wrapped); err == nil && len(wrapped.OAuthCustomScopes) > 0 {
		if len(wrapped.OAuthCustomScopes) != 1 {
			return bootstrapOAuthCustomScope{}, fmt.Errorf("%s must declare exactly one oauth_custom_scope component payload, got %d", source, len(wrapped.OAuthCustomScopes))
		}
		return normalizeOAuthCustomScopeSeed(wrapped.OAuthCustomScopes[0], allowLegacyMissingRouteFamilyPrefix)
	}

	var seed bootstrapOAuthCustomScope
	if err := json.Unmarshal(payload, &seed); err != nil {
		return seed, fmt.Errorf("parse %s: %w", source, err)
	}
	return normalizeOAuthCustomScopeSeed(seed, allowLegacyMissingRouteFamilyPrefix)
}

func validateOAuthCustomScopeComponent(component ManifestComponent, payload []byte, pathPrefix string) []PackDiagnostic {
	return validateOAuthCustomScopeComponentWithCompat(component, payload, pathPrefix, false)
}

func validateOAuthCustomScopeComponentWithCompat(component ManifestComponent, payload []byte, pathPrefix string, allowLegacyMissingRouteFamilyPrefix bool) []PackDiagnostic {
	seed, err := parseOAuthCustomScopeComponentPayload(payload, componentPayloadLabel(component), allowLegacyMissingRouteFamilyPrefix)
	if err != nil {
		return []PackDiagnostic{diag("component.payload.invalid_schema", pathPrefix, err.Error())}
	}
	if businessKey := strings.TrimSpace(component.BusinessKey); businessKey != seed.Name {
		return []PackDiagnostic{diag(
			"oauth_custom_scope.business_key.mismatch_name",
			pathPrefix+".businessKey",
			fmt.Sprintf("oauth_custom_scope businessKey %q must match payload name %q", businessKey, seed.Name),
		)}
	}
	return nil
}
