package app

import (
	"context"
	"strings"
	"time"

	"github.com/tidbcloud/AgentTeam/internal/coredb"
	"github.com/tidbcloud/AgentTeam/internal/store/app/installation"
	"github.com/tidbcloud/AgentTeam/internal/store/serviceactor"
)

// InstallCustomAppForWorkspaceAdmin atomically registers a workspace App definition,
// records its original catalog creator, installs it, and materializes its
// initial runner credential. It writes no installation request and returns the
// callback secret/runner token only on this create response.
func (store *Store) InstallCustomAppForWorkspaceAdmin(
	ctx context.Context,
	userID string,
	workspaceID string,
	params RequestCustomAppInstallationParams,
) (CustomAppInstallationResult, error) {
	adminMember, err := store.requireWorkspaceAdmin(ctx, userID, workspaceID)
	if err != nil {
		return CustomAppInstallationResult{}, err
	}
	preparedDefinition, err := prepareWorkspaceAppDefinition(params)
	if err != nil {
		return CustomAppInstallationResult{}, err
	}
	preparedDefinition, err = withGeneratedAppCallbackSigningSecret(preparedDefinition)
	if err != nil {
		return CustomAppInstallationResult{}, err
	}

	runnerEnabled := containsTrimmedString(
		preparedDefinition.reviewSnapshot.RuntimeTransports,
		serviceactor.RuntimeTransportKindRunnerStream,
	)
	runnerToken := ""

	var registration appDefinitionRegistrationResult
	var installationID string
	var installationView AppInstallationView
	if err := coredb.WithTx(ctx, store.db, func(tx coredb.Tx) error {
		now := time.Now().UTC()
		// This is create-only: revalidate the canonical admin first, then rely on
		// the apps.slug unique constraint to reject existing/concurrent creators.
		// Avoid taking apps before membership because installation paths can take
		// the inverse order through their App FK check.
		currentAdmin, err := store.revalidateServiceInstallationManagementForUpdate(
			ctx,
			tx,
			userID,
			workspaceID,
			adminMember.Member.ID,
		)
		if err != nil {
			return err
		}
		registration, err = store.registerPreparedWorkspaceAppDefinitionInTx(
			ctx,
			tx,
			workspaceID,
			currentAdmin.Member.ID,
			preparedDefinition.catalogVisibility,
			preparedDefinition,
			now,
		)
		if err != nil {
			return err
		}
		preparedInstallation, err := prepareInstalledAppConfigurationAtVersion(
			ctx,
			tx,
			registration.appID,
			preparedDefinition.manifest.Version,
			params.Config,
		)
		if err != nil {
			return err
		}
		if err := authorizeAppInstallationRequest(
			ctx,
			tx,
			workspaceID,
			preparedInstallation,
		); err != nil {
			return err
		}
		definition, err := store.syncAppServiceDefinition(ctx, tx, registration.appID, now)
		if err != nil {
			return err
		}
		if strings.TrimSpace(definition.ID) != strings.TrimSpace(registration.appID) {
			return conflict("App definition changed. Please retry.")
		}
		if err := installation.LockAppInstallationMutationScope(ctx, tx, definition.ID); err != nil {
			return err
		}
		installationID, err = store.installPreparedAppInTx(
			ctx,
			tx,
			userID,
			workspaceID,
			currentAdmin.Member.ID,
			preparedInstallation,
			now,
		)
		if err != nil {
			return err
		}
		txStore := store.serviceActorStore().InTx(tx)
		summary, err := txStore.LoadInstallationSummaryByKind(
			ctx,
			workspaceID,
			installationID,
			serviceactor.KindApp,
		)
		if err != nil {
			return err
		}
		if runnerEnabled {
			metadata, err := appRunnerCredentialMetadata(nil)
			if err != nil {
				return err
			}
			created, err := txStore.CreateRuntimeCredentialToken(ctx, serviceactor.RuntimeCredentialTokenCreate{
				RuntimeCredentialCreate: serviceactor.RuntimeCredentialCreate{
					WorkspaceID:           strings.TrimSpace(workspaceID),
					ServiceDefinitionID:   summary.Installation.ServiceDefinitionID,
					ServiceInstallationID: summary.Installation.ID,
					ServiceMemberID:       summary.Member.ID,
					ServiceKind:           serviceactor.KindApp,
					CredentialKind:        serviceactor.RuntimeCredentialKindRunnerStreamToken,
					TransportKind:         serviceactor.RuntimeTransportKindRunnerStream,
					Name:                  defaultAppRunnerCredentialName,
					Status:                serviceactor.RuntimeCredentialStatusActive,
					CreatedByMemberID:     currentAdmin.Member.ID,
					Now:                   now,
					Metadata:              metadata,
				},
			})
			if err != nil {
				return err
			}
			runnerToken = created.Token
		}
		installationView, err = installationViewFromServiceSummary(summary)
		if err != nil {
			return err
		}
		// This response belongs to the workspace administrator who just created
		// the App. Keep the command acknowledgement aligned with the canonical
		// list/detail read model instead of briefly projecting a non-manager.
		installationView.CanManageApp = true
		installationView.CatalogVisibility = preparedDefinition.catalogVisibility
		installationView.Grants, err = txStore.ListActiveGrantScopes(ctx, installationID)
		if err != nil {
			return err
		}
		connections, err := txStore.ListActiveConnections(ctx, installationID)
		if err != nil {
			return err
		}
		installationView.Connections = serviceConnectionsToAppViews(connections)
		return nil
	}); err != nil {
		return CustomAppInstallationResult{}, err
	}
	return CustomAppInstallationResult{
		Installation:          installationView,
		CallbackSigningSecret: registration.callbackSigningSecret,
		RunnerToken:           runnerToken,
	}, nil
}
