package app

import (
	"context"
	"net/http"
	"time"

	"github.com/tidbcloud/AgentTeam/internal/api"
	"github.com/tidbcloud/AgentTeam/internal/extension/providers/searchengine/runtime"
	"github.com/tidbcloud/AgentTeam/internal/extension/runner"
	"github.com/tidbcloud/AgentTeam/internal/service/app/installation"
	"github.com/tidbcloud/AgentTeam/internal/service/appruntime"
	"github.com/tidbcloud/AgentTeam/internal/store/app"
	"github.com/tidbcloud/AgentTeam/internal/store/app/registry"
	"github.com/tidbcloud/AgentTeam/internal/store/message"
	"github.com/tidbcloud/AgentTeam/internal/store/serviceactor"
)

var (
	ErrNotFound     = api.ErrNotFound
	ErrUnauthorized = api.ErrUnauthorized
)

type FollowupDeliverer interface {
	DeliverFollowupMessages(ctx context.Context, workspaceID string, deliveries []message.MessageDelivery)
}

type ServiceEventSubscriptionBlocker interface {
	BlockServiceEventSubscriptions(ctx context.Context, workspaceID, provider, connectionRef string) (int, error)
	UnblockServiceEventSubscriptions(ctx context.Context, workspaceID, provider, connectionRef string) (int, error)
}

type AppInstallationRequestProjector interface {
	ProjectPendingAppInstallationRequest(context.Context, app.AppInstallationRequestView) ([]app.AppNotificationResult, error)
	ProjectAppInstallationRequestDecision(context.Context, app.AppInstallationRequestView) (app.AppNotificationResult, error)
	ReconcileAppInstallationRequestProjections(context.Context, string, int) (int, error)
}

// WorkspaceAppRealtime publishes cache-invalidation facts after App mutations
// have committed. Implementations must not embed role-sensitive App data in a
// workspace-wide event; each browser refetches its own authorized projection.
type WorkspaceAppRealtime interface {
	EmitWorkspaceAppsUpdated(workspaceID string)
	EmitChannelUpdated(workspaceID string)
}

type WebhookStore interface {
	HandleAppWebhook(ctx context.Context, appSlug string, headers http.Header, payload []byte) (app.AppWebhookResult, error)
}

// AppRunnerService owns runner authentication, heartbeat, claim, and offline
// orchestration for the external App runtime routes.
type AppRunnerService interface {
	AuthenticateRunner(ctx context.Context, bearerToken string) (appruntime.RunnerAuthentication, error)
	Heartbeat(ctx context.Context, authenticated appruntime.RunnerAuthentication, params appruntime.RunnerHeartbeatParams) (appruntime.RunnerSessionView, error)
	ClaimDeliveries(ctx context.Context, authenticated appruntime.RunnerAuthentication, params appruntime.RunnerClaimParams) (appruntime.RunnerClaimResult, error)
	MarkSessionOffline(ctx context.Context, authenticated appruntime.RunnerAuthentication, sessionID string) (appruntime.RunnerSessionView, error)
}

// AppRuntimeStore is the credential/delivery-token surface used only by the
// isolated external App runner runtime routes. Keeping it separate from the
// browser Store avoids coupling ordinary workspace handlers and their fakes to
// runtime-only operations.
type AppRuntimeStore interface {
	AuthenticateAppNotificationRuntime(ctx context.Context, bearerToken string, requiredScopes []string) (app.AppNotificationRuntimePrincipal, error)
	CreateAppNotificationForRuntime(ctx context.Context, authenticated app.AppNotificationRuntimePrincipal, command app.AppNotificationCommand) (app.AppNotificationResult, error)
	GetAppNotificationForRuntime(ctx context.Context, authenticated app.AppNotificationRuntimePrincipal, notificationID string) (app.AppNotificationResult, error)
	MutateAppNotificationForRuntime(ctx context.Context, authenticated app.AppNotificationRuntimePrincipal, command app.AppNotificationMutationCommand) (app.AppNotificationResult, error)
	MutateAppNotificationForDelivery(ctx context.Context, authenticated app.AppRuntimeDeliveryAuth, command app.AppNotificationMutationCommand) (app.AppNotificationResult, error)
	AuthenticateAppRuntimeDelivery(ctx context.Context, bearerToken string, deliveryID string, attemptID string) (app.AppRuntimeDeliveryAuth, error)
	ReadAppRuntimeDeliveryContext(ctx context.Context, auth app.AppRuntimeDeliveryAuth, params app.AppRuntimeContextParams) (message.ScopeAPIMessagePage, error)
	ReplyAppRuntimeDelivery(ctx context.Context, auth app.AppRuntimeDeliveryAuth, params app.AppRuntimeReplyParams) (app.AppRuntimeReplyResult, error)
	AckAppRuntimeDelivery(ctx context.Context, auth app.AppRuntimeDeliveryAuth, params app.AppRuntimeAckParams) (app.AppRuntimeDeliveryActionResult, error)
	FailAppRuntimeDelivery(ctx context.Context, auth app.AppRuntimeDeliveryAuth, params app.AppRuntimeFailParams) (app.AppRuntimeDeliveryActionResult, error)
}

type Store interface {
	GetExtensionProviderConnectionForUser(ctx context.Context, userID string, workspaceID string, installationID string, target app.ExtensionProviderTarget) (app.ExtensionProviderConnectionView, error)
	UpsertExtensionProviderConnectionForUser(ctx context.Context, userID string, workspaceID string, installationID string, target app.ExtensionProviderTarget, credentials map[string]string, config runner.ConnectionConfig, expectedUpdatedAt *time.Time) (app.ExtensionProviderConnectionView, error)
	DeleteExtensionProviderConnectionForUser(ctx context.Context, userID string, workspaceID string, installationID string, target app.ExtensionProviderTarget) error
	AuthorizeExtensionProviderConnectionAdmin(ctx context.Context, userID string, workspaceID string, installationID string, target app.ExtensionProviderTarget) error
	ResolveExtensionProviderRuntimeConnection(ctx context.Context, workspaceID string, installationID string, target app.ExtensionProviderTarget, configContract runner.ConnectionConfigContract) (map[string]string, runner.ConnectionConfig, error)

	ListAppsForUser(ctx context.Context, userID, workspaceID string) ([]registry.AppDefinitionView, error)
	ListAppInstallationsForUser(ctx context.Context, userID, workspaceID string) ([]app.AppInstallationView, error)
	ListAppInstallationChannelsForUser(ctx context.Context, userID, workspaceID, installationID string) ([]app.AppInstallationChannelView, error)
	ListInstallationExtensionsForUser(ctx context.Context, userID, workspaceID, installationID string) ([]app.InstallationExtensionView, error)
	AddInstallationExtensionForUser(ctx context.Context, userID, workspaceID, installationID, definitionDomain, extensionName string) (serviceactor.InstallationExtension, error)
	RenameInstallationExtensionForUser(ctx context.Context, userID, workspaceID, installationID, selectionID, extensionName string) (serviceactor.InstallationExtension, error)
	EnableInstallationExtensionForUser(ctx context.Context, userID, workspaceID, installationID, selectionID string) (serviceactor.InstallationExtension, error)
	DisableInstallationExtensionForUser(ctx context.Context, userID, workspaceID, installationID, selectionID string) (serviceactor.InstallationExtension, error)
	GetSearchEngineConnectionForUser(ctx context.Context, userID, workspaceID, installationID string) (app.SearchEngineConnectionView, error)
	UpsertSearchEngineConnectionForUser(ctx context.Context, userID, workspaceID, installationID string, params app.UpsertSearchEngineConnectionParams) (app.SearchEngineConnectionView, error)
	DeleteSearchEngineConnectionForUser(ctx context.Context, userID, workspaceID, installationID string) (app.SearchEngineConnectionView, error)
	InstallAppForWorkspaceAdmin(ctx context.Context, userID, workspaceID string, params app.InstallAppParams) (app.AppInstallationView, error)
	ConfigureAppInstallationForWorkspaceAdmin(ctx context.Context, userID, workspaceID, installationID string, params app.ConfigureAppInstallationParams) (app.AppInstallationView, error)
	InstallCustomAppForWorkspaceAdmin(ctx context.Context, userID, workspaceID string, params app.RequestCustomAppInstallationParams) (app.CustomAppInstallationResult, error)
	RequestCustomAppInstallationForUser(ctx context.Context, userID, workspaceID string, params app.RequestCustomAppInstallationParams) (app.CustomAppRequestResult, error)
	GetAppInitialCredentialsStatusForUser(ctx context.Context, userID, workspaceID, installationID string) (app.AppInitialCredentialsStatusView, error)
	ClaimAppInitialCredentialsForUser(ctx context.Context, userID, workspaceID, installationID string) (app.AppInitialCredentialsClaimResult, error)
	ListAppManagersForUser(ctx context.Context, userID, workspaceID, installationID string) ([]app.AppManagerAssignment, error)
	AddAppManagerForUser(ctx context.Context, userID, workspaceID, installationID, memberID string, now time.Time) (app.AppManagerAssignment, error)
	RemoveAppManagerForUser(ctx context.Context, userID, workspaceID, installationID, managerID string, expectedVersion int64, now time.Time) (app.AppManagerAssignment, error)
	UpdateAppAvatarForUser(ctx context.Context, userID, workspaceID, installationID, avatarAssetID string, now time.Time) (app.AppInstallationView, error)
	ListPendingAppInstallationRequestsForUser(ctx context.Context, userID, workspaceID string) ([]app.AppInstallationRequestView, error)
	RequestAppInstallationForUser(ctx context.Context, userID, workspaceID string, params app.RequestAppInstallationParams) (app.AppInstallationRequestView, error)
	GetAppInstallationRequest(ctx context.Context, workspaceID string, requestID string) (app.AppInstallationRequestView, error)
	ApproveAppInstallationRequestForUser(ctx context.Context, userID, workspaceID string, requestID string, params app.DecideAppInstallationRequestParams) (app.AppInstallationRequestView, error)
	RejectAppInstallationRequestForUser(ctx context.Context, userID, workspaceID string, requestID string, params app.DecideAppInstallationRequestParams) (app.AppInstallationRequestView, error)
	MarkAppInstallationRequestCannotBeApplied(ctx context.Context, workspaceID, requestID string) (app.AppInstallationRequestView, error)
	UninstallAppInstallationForUser(ctx context.Context, userID, workspaceID string, installationID string) (app.AppInstallationView, error)
	UpdateAppDisplayNameForWorkspaceAdmin(ctx context.Context, userID, workspaceID string, installationID string, name string) (app.AppInstallationView, error)
	UpdateAppVisibilityForUser(ctx context.Context, userID, workspaceID, installationID, catalogVisibility string, now time.Time) (app.AppVisibilityUpdateResult, error)
	ListAppPermissionsForUser(ctx context.Context, userID, workspaceID, installationID string) (app.AppPermissionsView, error)
	UpdateAppPermissionsForUser(ctx context.Context, userID, workspaceID, installationID, expectedPolicyDigest string, scopes []string, now time.Time) (app.AppPermissionsUpdateResult, error)
	ListAppRunnerCredentialsForUser(ctx context.Context, userID string, workspaceID string, installationID string) ([]app.AppRunnerCredentialView, error)
	CreateAppRunnerCredentialForUser(ctx context.Context, userID string, workspaceID string, installationID string, params app.AppRunnerCredentialCreateParams) (app.AppRunnerCredentialTokenView, error)
	DisableAppRunnerCredentialForUser(ctx context.Context, userID string, workspaceID string, credentialID string) (app.AppRunnerCredentialView, error)
	RotateAppCallbackSecretForUser(ctx context.Context, userID string, workspaceID string, installationID string) (app.AppCallbackSecretView, error)
	ListAgentAppAuthorizationsForUser(ctx context.Context, userID string, workspaceID string, agentID string) ([]app.AgentAppAuthorizationView, error)
	EnableAgentAppAuthorizationForUser(ctx context.Context, userID string, workspaceID string, agentID string, appInstallationID string) (app.AgentAppAuthorizationView, error)
	DisableAgentAppAuthorizationForUser(ctx context.Context, userID string, workspaceID string, agentID string, appInstallationID string) (app.AgentAppAuthorizationView, error)
	ListServiceEventSubscriptionsForUser(ctx context.Context, userID string, workspaceID string, appInstallationID string, target string, resource string, status string, limit int) ([]app.ServiceEventSubscriptionView, error)
	CreateServiceEventSubscriptionForUser(ctx context.Context, userID string, workspaceID string, appInstallationID string, target string, prRefOrURL string, events []string, notifyPrincipal *bool) (app.ServiceEventSubscriptionActionResult, error)
	PauseServiceEventSubscriptionForUser(ctx context.Context, userID string, workspaceID string, subscriptionID string) (app.ServiceEventSubscriptionActionResult, error)
	ResumeServiceEventSubscriptionForUser(ctx context.Context, userID string, workspaceID string, subscriptionID string) (app.ServiceEventSubscriptionActionResult, error)
	RemoveServiceEventSubscriptionForUser(ctx context.Context, userID string, workspaceID string, subscriptionID string) (app.ServiceEventSubscriptionActionResult, error)
	ListProviderWatchesForUser(ctx context.Context, userID string, workspaceID string, appInstallationID string, target string, resource string, status string, limit int) ([]app.ProviderWatchView, error)
	CreateProviderWatchForUser(ctx context.Context, userID string, workspaceID string, appInstallationID string, target string, prRefOrURL string, events []string, notifyPrincipal *bool) (app.ProviderWatchActionResult, error)
	PauseProviderWatchForUser(ctx context.Context, userID string, workspaceID string, watchID string) (app.ProviderWatchActionResult, error)
	ResumeProviderWatchForUser(ctx context.Context, userID string, workspaceID string, watchID string) (app.ProviderWatchActionResult, error)
	RemoveProviderWatchForUser(ctx context.Context, userID string, workspaceID string, watchID string) (app.ProviderWatchActionResult, error)
	ListAppWebhookDeliveriesForUser(ctx context.Context, userID, workspaceID, installationID string, limit int) ([]app.AppWebhookDeliveryView, error)
	AuthenticateAppNotificationInternalService(ctx context.Context, workspaceID string, secret string) (app.AppNotificationInternalPrincipal, error)
	CreateAppNotificationForInternal(ctx context.Context, principal app.AppNotificationInternalPrincipal, command app.AppNotificationCommand) (app.AppNotificationResult, error)
	GetAppNotificationForInternal(ctx context.Context, principal app.AppNotificationInternalPrincipal, notificationID string) (app.AppNotificationResult, error)
	MutateAppNotificationForInternal(ctx context.Context, principal app.AppNotificationInternalPrincipal, command app.AppNotificationMutationCommand) (app.AppNotificationResult, error)
	GetAppNotificationCardForUser(ctx context.Context, userID string, workspaceID string, messageID string) (app.AppNotificationCardView, error)
	InvokeAppNotificationActionForUser(ctx context.Context, userID string, workspaceID string, command app.AppNotificationActionCommand) (app.AppNotificationActionResult, error)
	WebhookStore
}

// AppInstallationService is the personal-installation orchestration port
// backed by internal/service/app/installation.
type AppInstallationService interface {
	InstallForSelf(ctx context.Context, userID, workspaceID string, params installation.InstallParams) (installation.PersonalInstallation, error)
	GrantAccess(ctx context.Context, userID, workspaceID, installationID string, params installation.AccessGrantParams) (serviceactor.PrincipalAuthorization, error)
	RequestShare(ctx context.Context, userID, workspaceID, installationID, reason string) (installation.ShareRequest, error)
	DecideShare(ctx context.Context, userID, workspaceID, requestID string, approve bool, reason string) (installation.ShareRequest, error)
	ListAccess(ctx context.Context, userID, workspaceID, installationID string) ([]serviceactor.PrincipalAuthorization, error)
	RevokeAccess(ctx context.Context, userID, workspaceID, installationID, authorizationID string) error
}

// ResolvedExtensionProvider is the connection-flow projection of one
// trusted provider Extension: the store target, the declared credential
// fields for the connect form, and the live credential validator.
type ResolvedExtensionProvider struct {
	Target            app.ExtensionProviderTarget
	Auth              runner.AuthSpec
	PrepareConnection runner.ConnectionPreparer
	ConfigContract    runner.ConnectionConfigContract
}

// ProviderConnectionCapability is the browser-safe discoverability projection
// of one trusted, generic provider connection registration.
type ProviderConnectionCapability struct {
	Domain string
}

// ExtensionProviderIndex resolves the trusted provider behind one App
// installation for the extension connection flow. It fails closed with
// api.ErrNotFound when the App ships no provider Extension.
type ExtensionProviderIndex interface {
	ProviderConnectionCapabilityForAppID(
		appID string,
	) (ProviderConnectionCapability, bool)
	ResolveProviderForInstallation(
		ctx context.Context,
		workspaceID string,
		installationID string,
	) (ResolvedExtensionProvider, error)
}

type SearchEngineConnectionRuntimeStore interface {
	ResolveSearchEngineRuntimeConnection(ctx context.Context, workspaceID, installationID, provider string) (runtime.RuntimeConnection, error)
}

type SearchEngineConnectionTester interface {
	TestConnection(ctx context.Context, connection runtime.RuntimeConnection) error
}
