package app

import (
	"context"
	"strings"
	"time"

	"github.com/tidbcloud/AgentTeam/internal/coredb"
	"github.com/tidbcloud/AgentTeam/internal/store/app/runtime"
	"github.com/tidbcloud/AgentTeam/internal/store/model"
	"github.com/tidbcloud/AgentTeam/internal/store/provider"
	"github.com/tidbcloud/AgentTeam/internal/store/serviceactor"
)

const (
	ProviderWatchConnectionReady                  = provider.ProviderWatchConnectionReady
	ProviderWatchConnectionWorkspaceNotConfigured = provider.ProviderWatchConnectionWorkspaceNotConfigured
	ProviderWatchConnectionResourceNotAccessible  = provider.ProviderWatchConnectionResourceNotAccessible
)

func (store *Store) providerStore() *provider.Store {
	return provider.New(store.db, provider.Options{
		AuthorizeWorkspaceAccess: func(ctx context.Context, userID string, workspaceID string) error {
			_, err := store.requireWorkspaceAccess(ctx, userID, workspaceID)
			return err
		},
		AuthorizeWorkspaceServiceInstall: store.requireWorkspaceServiceInstall,
		EncryptGitHubSecret:              store.encryptGitHubAppConfigSecret,
		EncryptOptionalGitHubSecret:      store.encryptOptionalGitHubAppConfigSecret,
		DecryptGitHubSecret:              store.decryptGitHubAppConfigSecret,
		EncryptGiteaSecret:               store.encryptGiteaAppConfigSecret,
		DecryptGiteaSecret:               store.decryptGiteaAppConfigSecret,
		FindAppInstallationByWorkspaceAndApp: func(ctx context.Context, workspaceID string, appID string) (provider.StoredAppInstallation, bool, error) {
			installation, found, err := store.findAppInstallationByWorkspaceAndApp(ctx, workspaceID, appID)
			return providerStoredAppInstallationFromModel(installation), found, err
		},
		UpsertAppConnection: func(ctx context.Context, params provider.UpsertAppConnectionParams) (provider.AppConnectionView, error) {
			connection, err := store.UpsertAppConnection(ctx, UpsertAppConnectionParams{
				InstallationID:     params.InstallationID,
				ExternalSubjectRef: params.ExternalSubjectRef,
				DisplayLabel:       params.DisplayLabel,
				Credentials:        params.Credentials,
				CreatedByUserID:    params.CreatedByUserID,
			})
			return providerAppConnectionFromStore(connection), err
		},
		UpsertAppConnectionInTx: func(ctx context.Context, tx coredb.Tx, params provider.UpsertAppConnectionParams) (provider.AppConnectionView, error) {
			connection, err := store.upsertAppConnectionInTx(ctx, tx, UpsertAppConnectionParams{
				InstallationID:     params.InstallationID,
				ExternalSubjectRef: params.ExternalSubjectRef,
				DisplayLabel:       params.DisplayLabel,
				Credentials:        params.Credentials,
				CreatedByUserID:    params.CreatedByUserID,
			}, "", time.Now().UTC())
			return providerAppConnectionFromStore(connection), err
		},
		ListAppConnections: func(ctx context.Context, installationID string) ([]provider.AppConnectionView, error) {
			connections, err := store.ListAppConnections(ctx, installationID)
			return providerAppConnectionsFromStore(connections), err
		},
		DisableAppConnection: store.DisableAppConnection,
		DisableAppConnectionInTx: func(ctx context.Context, tx coredb.Tx, installationID string, connectionID string) error {
			return store.disableAppConnectionInTx(ctx, tx, installationID, connectionID, time.Now().UTC())
		},
		SetGitHubSubscriptionsBlockedInTx: func(ctx context.Context, tx coredb.Tx, workspaceID string, connectionRef string, blocked bool, now time.Time) error {
			return store.setGitHubServiceEventSubscriptionsBlockedInTx(ctx, tx, workspaceID, connectionRef, blocked, now)
		},
		AppInstallationViewByID: func(ctx context.Context, workspaceID string, installationID string) (provider.AppInstallationView, error) {
			installation, err := store.appInstallationViewByID(ctx, workspaceID, installationID)
			return providerAppInstallationFromStore(installation), err
		},
	})
}

// Provider exposes the provider-domain store for callers that should not depend
// on the root Store facade.
func (store *Store) Provider() *provider.Store {
	return store.providerStore()
}

func providerStoredAppInstallationFromModel(installation model.StoredAppInstallation) provider.StoredAppInstallation {
	return provider.StoredAppInstallation{
		ID:          installation.ID,
		AppID:       installation.AppID,
		WorkspaceID: installation.WorkspaceID,
		MemberID:    installation.MemberID,
		Enabled:     installation.Enabled,
	}
}

func providerAppConnectionFromStore(connection runtime.ConnectionView) provider.AppConnectionView {
	return provider.AppConnectionView{
		ID:                 connection.ID,
		ExternalSubjectRef: connection.ExternalSubjectRef,
		DisplayLabel:       connection.DisplayLabel,
		RequiresUpgrade:    connection.RequiresUpgrade,
		MissingPermissions: append([]string(nil), connection.MissingPermissions...),
		MissingEvents:      append([]string(nil), connection.MissingEvents...),
		Enabled:            connection.Enabled,
		CreatedAt:          connection.CreatedAt,
		UpdatedAt:          connection.UpdatedAt,
	}
}

func providerAppConnectionsFromStore(connections []runtime.ConnectionView) []provider.AppConnectionView {
	out := make([]provider.AppConnectionView, 0, len(connections))
	for _, connection := range connections {
		out = append(out, providerAppConnectionFromStore(connection))
	}
	return out
}

func providerAppInstallationFromStore(installation AppInstallationView) provider.AppInstallationView {
	return provider.AppInstallationView{
		ID:          installation.ID,
		AppID:       installation.AppID,
		AppSlug:     installation.AppSlug,
		AppName:     installation.AppName,
		AvatarURL:   installation.AvatarURL,
		MemberID:    installation.MemberID,
		Handle:      installation.Handle,
		Connections: providerAppConnectionsFromStore(installation.Connections),
		Enabled:     installation.Enabled,
		CreatedAt:   installation.CreatedAt,
		UpdatedAt:   installation.UpdatedAt,
	}
}

func (store *Store) findAppInstallationByWorkspaceAndApp(ctx context.Context, workspaceID string, appID string) (model.StoredAppInstallation, bool, error) {
	var zero model.StoredAppInstallation
	summaries, err := store.serviceActorStore().ListInstallationSummaries(ctx, workspaceID, serviceactor.KindApp)
	if err != nil {
		return zero, false, err
	}
	for _, summary := range summaries {
		if strings.TrimSpace(summary.Definition.ID) != strings.TrimSpace(appID) {
			continue
		}
		if strings.TrimSpace(summary.Installation.InstanceKey) !=
			serviceactor.DefaultInstanceKey {
			// This dependency is the legacy singleton provider lookup used by
			// GitHub/Gitea. Named Email Installations must always resolve by
			// their exact Installation identity instead of picking one mailbox.
			continue
		}
		return model.StoredAppInstallation{
			ID:                summary.Installation.ID,
			AppID:             summary.Definition.ID,
			AppSlug:           summary.Definition.Slug,
			AppName:           summary.Definition.Name,
			WorkspaceID:       summary.Installation.WorkspaceID,
			InstanceKey:       summary.Installation.InstanceKey,
			MemberID:          summary.Member.MemberID,
			Handle:            summary.Member.Handle,
			MemberDisplayName: summary.Member.DisplayName,
			InstalledVersion:  summary.Installation.InstalledVersion,
			Config:            summary.Installation.Config,
			Enabled:           summary.Installation.Status == serviceactor.StatusActive,
			CreatedAt:         summary.Installation.CreatedAt,
			UpdatedAt:         summary.Installation.UpdatedAt,
		}, true, nil
	}
	return zero, false, nil
}
