package app

import (
	"github.com/tidbcloud/AgentTeam/internal/store/app/registry"
	"strings"

	"github.com/tidbcloud/AgentTeam/internal/store/model"
)

const (
	appInstallationReviewTransportsKey  = "transports"
	appInstallationReviewCallbackURLKey = "callback_url"
	appInstallationReviewVisibilityKey  = "catalog_visibility"
)

func appInstallationReviewBaseConfig(snapshot *AppInstallationReviewSnapshot) map[string]any {
	if snapshot == nil {
		return map[string]any{}
	}
	transports := normalizedAppReviewTransports(snapshot.RuntimeTransports)
	callbackURL := strings.TrimSpace(snapshot.CallbackURL)
	rawVisibility := strings.TrimSpace(snapshot.CatalogVisibility)
	if len(transports) == 0 && callbackURL == "" && rawVisibility == "" {
		return map[string]any{}
	}
	visibility, err := registry.NormalizeCatalogVisibility(rawVisibility, true)
	if err != nil {
		// Review snapshots are server-authored. Corrupt legacy state must never
		// widen the audience during settlement.
		visibility = registry.CatalogVisibilityPrivate
	}
	review := map[string]any{
		appInstallationReviewTransportsKey: transports,
		appInstallationReviewVisibilityKey: visibility,
	}
	if callbackURL != "" {
		review[appInstallationReviewCallbackURLKey] = callbackURL
	}
	return map[string]any{appInstallationAppReviewSnapshotKey: review}
}

// appInstallationReviewSnapshotFromVersion derives the reviewer-visible App
// runtime summary from the exact manifest version already prepared and pinned
// by request admission. The browser never supplies this security-relevant
// snapshot, so approval reviews the same runtime capability that onboarding
// pinned.
func appInstallationReviewSnapshotFromVersion(version model.InstallableAppVersionRecord) *AppInstallationReviewSnapshot {
	if !version.RuntimeCapabilitySupported {
		return nil
	}
	transports := normalizedAppReviewTransports(version.AppRuntimeTransports)
	if len(transports) == 0 {
		return nil
	}
	return &AppInstallationReviewSnapshot{
		RuntimeTransports: transports,
		CallbackURL:       strings.TrimSpace(version.AppCallbackURL),
		CatalogVisibility: registry.CatalogVisibilityPrivate,
	}
}

func appInstallationReviewSnapshotFromBaseConfig(baseConfig map[string]any) ([]string, string, string) {
	rawReview, ok := baseConfig[appInstallationAppReviewSnapshotKey]
	if !ok || rawReview == nil {
		return nil, "", ""
	}
	review, ok := rawReview.(map[string]any)
	if !ok {
		return nil, "", registry.CatalogVisibilityPrivate
	}
	transports := make([]string, 0, 2)
	switch values := review[appInstallationReviewTransportsKey].(type) {
	case []any:
		for _, value := range values {
			if text, ok := value.(string); ok {
				transports = append(transports, text)
			}
		}
	case []string:
		transports = append(transports, values...)
	}
	callbackURL, _ := review[appInstallationReviewCallbackURLKey].(string)
	visibility, _ := review[appInstallationReviewVisibilityKey].(string)
	visibility, err := registry.NormalizeCatalogVisibility(visibility, true)
	if err != nil {
		visibility = registry.CatalogVisibilityPrivate
	}
	return normalizedAppReviewTransports(transports), strings.TrimSpace(callbackURL), visibility
}

func appInstallationRequestResolvedBaseConfig(request AppInstallationRequestView) map[string]any {
	baseConfig := cloneAppInstallationRequestConfig(request.BaseConfig)
	review := appInstallationReviewBaseConfig(&AppInstallationReviewSnapshot{
		RuntimeTransports: request.AppRuntimeTransports,
		CallbackURL:       request.AppCallbackURL,
		CatalogVisibility: request.AppCatalogVisibility,
	})
	if snapshot, ok := review[appInstallationAppReviewSnapshotKey]; ok {
		baseConfig[appInstallationAppReviewSnapshotKey] = snapshot
	}
	return baseConfig
}

func normalizedAppReviewTransports(values []string) []string {
	seen := map[string]struct{}{}
	result := make([]string, 0, len(values))
	for _, value := range values {
		value = strings.TrimSpace(value)
		switch value {
		case "callback", "runner_stream":
		default:
			continue
		}
		if _, exists := seen[value]; exists {
			continue
		}
		seen[value] = struct{}{}
		result = append(result, value)
	}
	return result
}
