package app

import (
	"bytes"
	"fmt"
	"strings"

	"github.com/tidbcloud/AgentTeam/internal/store/app/registry"
	"github.com/tidbcloud/AgentTeam/internal/store/model"
	"github.com/tidbcloud/AgentTeam/internal/store/serviceactor"
)

type pinnedAppInstallationMetadata struct {
	Manifest model.JSONDocument `json:"manifest,omitempty"`
}

// pinnedRuntimeCapability reads App authority from the immutable manifest snapshot
// stored on an installation. Definition capabilities describe the latest App
// version and must never upgrade or downgrade an existing installation.
func pinnedRuntimeCapability(installation serviceactor.Installation) (registry.StoredAppRuntimeCapability, error) {
	if strings.TrimSpace(installation.ServiceKind) != serviceactor.KindApp {
		return registry.StoredAppRuntimeCapability{}, nil
	}
	metadataJSON := bytes.TrimSpace(installation.Metadata)
	if len(metadataJSON) == 0 || bytes.Equal(metadataJSON, []byte("null")) {
		return registry.StoredAppRuntimeCapability{}, nil
	}
	var metadata pinnedAppInstallationMetadata
	if err := model.UnmarshalJSONObject("app service installation metadata", metadataJSON, &metadata); err != nil {
		return registry.StoredAppRuntimeCapability{}, fmt.Errorf("decode pinned app installation metadata: %w", err)
	}
	manifestJSON := bytes.TrimSpace(metadata.Manifest)
	if len(manifestJSON) == 0 || bytes.Equal(manifestJSON, []byte("null")) {
		return registry.StoredAppRuntimeCapability{}, nil
	}
	capability, err := registry.ParseStoredAppRuntimeCapability(manifestJSON)
	if err != nil {
		return registry.StoredAppRuntimeCapability{}, fmt.Errorf("decode pinned App capability: %w", err)
	}
	return capability, nil
}

func requirePinnedRuntimeCapability(installation serviceactor.Installation) (registry.StoredAppRuntimeCapability, error) {
	capability, err := pinnedRuntimeCapability(installation)
	if err != nil {
		return registry.StoredAppRuntimeCapability{}, err
	}
	if !capability.Supported {
		return registry.StoredAppRuntimeCapability{}, badRequest("App installation does not expose App capability")
	}
	return capability, nil
}

// pinnedAppManifestScopes returns the full pinned manifest scope set for an App
// installation: the union of the top-level generic scopes and
// capabilities.runtime.scopes as parsed by registry.ParseStoredManifest. This is
// the anti-escalation ceiling for App permission narrowing and must match the set
// granted at install, so a full-set PATCH never revokes an existing generic
// scope (for example profile.read). It falls back to the runtime capability scopes
// only when the installation carries no stored manifest payload.
func pinnedAppManifestScopes(installation serviceactor.Installation) ([]string, error) {
	capability, err := pinnedRuntimeCapability(installation)
	if err != nil {
		return nil, err
	}
	if !capability.Supported {
		return nil, badRequest("App installation does not expose App capability")
	}
	metadataJSON := bytes.TrimSpace(installation.Metadata)
	if len(metadataJSON) == 0 || bytes.Equal(metadataJSON, []byte("null")) {
		return append([]string(nil), capability.Scopes...), nil
	}
	var metadata pinnedAppInstallationMetadata
	if err := model.UnmarshalJSONObject("app service installation metadata", metadataJSON, &metadata); err != nil {
		return nil, fmt.Errorf("decode pinned app installation metadata: %w", err)
	}
	manifestJSON := bytes.TrimSpace(metadata.Manifest)
	if len(manifestJSON) == 0 || bytes.Equal(manifestJSON, []byte("null")) {
		return append([]string(nil), capability.Scopes...), nil
	}
	manifest, err := registry.ParseStoredManifest(manifestJSON)
	if err != nil {
		return nil, fmt.Errorf("decode pinned App manifest scopes: %w", err)
	}
	return manifest.Scopes, nil
}

func requirePinnedAppEvent(installation serviceactor.Installation, event string) (registry.StoredAppRuntimeCapability, error) {
	capability, err := requirePinnedRuntimeCapability(installation)
	if err != nil {
		return registry.StoredAppRuntimeCapability{}, err
	}
	if !pinnedRuntimeCapabilityDeclaresEvent(capability, event) {
		return registry.StoredAppRuntimeCapability{}, badRequest(fmt.Sprintf("App installation does not declare App event %s", strings.TrimSpace(event)))
	}
	return capability, nil
}

func pinnedRuntimeCapabilityDeclaresEvent(capability registry.StoredAppRuntimeCapability, event string) bool {
	event = strings.TrimSpace(event)
	if event == "" {
		return false
	}
	for _, candidate := range capability.Events {
		if strings.TrimSpace(candidate) == event {
			return true
		}
	}
	return false
}

func pinnedAppTransportEnabled(installation serviceactor.Installation, transport string) (bool, error) {
	capability, err := requirePinnedRuntimeCapability(installation)
	if err != nil {
		return false, err
	}
	transport = strings.TrimSpace(transport)
	for _, candidate := range capability.Transports {
		if strings.TrimSpace(candidate) == transport {
			return true, nil
		}
	}
	return false, nil
}

// pinnedRuntimeCapabilityView exposes only the normalized, non-secret App
// capability snapshot stored on an installation. Catalog/latest-definition
// capabilities must not describe what an already-installed App can run.
func pinnedRuntimeCapabilityView(installation serviceactor.Installation) (*registry.AppRuntimeCapabilityView, error) {
	capability, err := pinnedRuntimeCapability(installation)
	if err != nil {
		return nil, err
	}
	if !capability.Supported {
		return nil, nil
	}
	var notifications *registry.AppNotificationCapabilityView
	if capability.Notifications.Supported {
		notifications = &registry.AppNotificationCapabilityView{
			Targets: capability.Notifications.Targets,
			Content: capability.Notifications.Content,
		}
	}
	return &registry.AppRuntimeCapabilityView{
		Supported:        true,
		Transports:       capability.Transports,
		DefaultTransport: capability.DefaultTransport,
		Events:           capability.Events,
		Scopes:           capability.Scopes,
		Notifications:    notifications,
		ConfigSchema:     capability.ConfigSchema,
	}, nil
}
