package server

import (
	"log/slog"
	"time"

	"github.com/tidbcloud/AgentTeam/internal/agsbridge/wiki"
	"github.com/tidbcloud/AgentTeam/internal/automation/blob"
	"github.com/tidbcloud/AgentTeam/internal/service/automation"
	kbservice "github.com/tidbcloud/AgentTeam/internal/service/kb"
	"github.com/tidbcloud/AgentTeam/internal/service/memory"
	"github.com/tidbcloud/AgentTeam/internal/service/providertokens"
	"github.com/tidbcloud/AgentTeam/internal/service/resource/activity"
	"github.com/tidbcloud/AgentTeam/internal/service/resource/repos"

	automationstore "github.com/tidbcloud/AgentTeam/internal/store/automation"
	"github.com/tidbcloud/AgentTeam/internal/store/kb"
	providertokensstore "github.com/tidbcloud/AgentTeam/internal/store/providertokens"
	activitystore "github.com/tidbcloud/AgentTeam/internal/store/resource/activity"
	memorystore "github.com/tidbcloud/AgentTeam/internal/store/resource/memory"
	reposstore "github.com/tidbcloud/AgentTeam/internal/store/resource/repos"
)

// resourceServiceStores groups MeshX domain stores. Each store is independent from AGS DB state.
type resourceServiceStores struct {
	KB                   *kb.Store
	Automations          *automationstore.Store
	Activity             *activitystore.Store
	Memory               *memorystore.Store
	PersonalAccessTokens *providertokensstore.Store
	Repos                *reposstore.Store
}

// resourceServiceOptions configures MeshX resource services.
type resourceServiceOptions struct {
	Now                                   func() time.Time
	Logger                                *slog.Logger
	AutomationSourceResolver              automation.AutomationSourceResolver
	AutomationRepositoryCredentialChecker automation.AutomationRepositoryCredentialChecker
	AutomationLimits                      automation.AutomationLimits
	AutomationAuthorizer                  automation.AutomationAuthorizer
	AutomationBlobStore                   blob.Store
	AutomationCredentialsKey              []byte
	PersonalAccessTokenCredentialsKey     []byte
	AutomationRunnerSignal                func(workspaceID string)
	KBWikiReader                          kbservice.KBWikiReader
	KBNativeWikiReader                    kbservice.KBWikiReader
	KBContentAuthority                    kbservice.KBSourceAuthorityReader
	KBNativeUADashboardRepository         kbservice.UADashboardRepository
	AGSRepositoryStore                    wiki.AGSRepositoryStore
	KBAgentOwnershipSource                kbservice.AgentOwnershipSource
	MemoryRepositoryInitializer           memory.MemoryRepositoryInitializer
	MemoryRepositoryForker                memory.MemoryRepositoryForker
	MemoryRepositoryDeleter               repos.RepositoryDeleter
	MemoryRepositoryBackups               memory.MemoryRepositoryBackupStore
	MemoryAgentOwnershipSource            memory.AgentOwnershipSource
	MemoryContentAuthority                memory.ContentAuthorityReader
	NativeMemoryContent                   memory.NativeMemoryContent
	NativeMemoryContentEnabled            bool
	MemoryMigrationSource                 memory.MemoryMigrationSource
	MemoryMigrationWorker                 memory.MemoryContentMigrationWorker
	MemoryMigrationMappings               memory.MemoryMigrationMappingReader
	MemoryMigrationContent                memory.MemoryMigrationContent
	RepositorySyncTokenIssuer             repos.RepositorySyncTokenIssuer
	SourceMirrorSyncer                    kbservice.KBSourceMirrorRepositorySyncer
	SourceMirrorPreflighter               kbservice.KBSourceMirrorPreflighter
	PrincipalWorkspaceAccessVerifier      kbservice.PrincipalWorkspaceAccessVerifier
	WorkspaceGovernanceAuthorizer         kbservice.WorkspaceGovernanceAuthorizer
	KBWikiGenerator                       kbservice.KBWikiGenerator
	ActivityProjector                     activity.ResourceActivityProjectorRefresher
	ActivityRefreshAfter                  time.Duration
}

// resourceServices holds the domain services composed by the MeshX server.
type resourceServices struct {
	KB                   *kbservice.KBService
	Automations          *automation.AutomationsService
	Memory               *memory.MemoryService
	PersonalAccessTokens *providertokens.Service
}

// newResourceServices constructs MeshX resource services from domain stores.
func newResourceServices(stores resourceServiceStores, options resourceServiceOptions) *resourceServices {
	now := options.Now
	if now == nil {
		now = func() time.Time { return time.Now().UTC() }
	}
	automationsService := automation.NewAutomationsService(stores.Automations, now, options.AutomationSourceResolver)
	automationsService.SetRepositoryStores(stores.KB)
	automationsService.SetRepositoryStore(stores.Repos)
	automationsService.SetLimits(options.AutomationLimits)
	automationsService.SetBlobStore(options.AutomationBlobStore)
	automationsService.SetAutomationCredentialsKey(options.AutomationCredentialsKey)
	automationsService.SetRunnerSignal(options.AutomationRunnerSignal)
	automationsService.SetRepositoryCredentialChecker(options.AutomationRepositoryCredentialChecker)
	automationAuthorizer := options.AutomationAuthorizer
	if automationAuthorizer == nil {
		automationAuthorizer = automation.DenyAutomationAuthorizer{}
	}
	automationsService.SetAuthorizer(automationAuthorizer)
	svc := &resourceServices{
		Automations: automationsService,
		PersonalAccessTokens: providertokens.New(stores.PersonalAccessTokens, providertokens.Options{
			Now:            now,
			CredentialsKey: options.PersonalAccessTokenCredentialsKey,
			LinkCounter:    stores.KB,
		}),
	}
	svc.KB = kbservice.NewKBServiceWithOptions(stores.KB, now, kbservice.KBServiceOptions{
		Logger:                            options.Logger,
		WikiReader:                        options.KBWikiReader,
		NativeWikiReader:                  options.KBNativeWikiReader,
		ContentAuthority:                  options.KBContentAuthority,
		WikiStore:                         options.AGSRepositoryStore,
		RepositoryStore:                   stores.Repos,
		UADashboardRepository:             uaDashboardRepository(options.AGSRepositoryStore),
		NativeUADashboardRepository:       options.KBNativeUADashboardRepository,
		SourceMirrorSyncer:                options.SourceMirrorSyncer,
		SourceMirrorPreflighter:           options.SourceMirrorPreflighter,
		SourceRepositoryInitializer:       sourceRepositoryInitializer(options.AGSRepositoryStore),
		AgentOwnershipSource:              options.KBAgentOwnershipSource,
		AllowPrivateSourceMirrorHosts:     true,
		AutomationSecretStore:             stores.Automations,
		AutomationCredentialsKey:          options.AutomationCredentialsKey,
		PersonalAccessTokenStore:          stores.PersonalAccessTokens,
		PersonalAccessTokenCredentialsKey: options.PersonalAccessTokenCredentialsKey,
		PrincipalWorkspaceAccessVerifier:  options.PrincipalWorkspaceAccessVerifier,
		WorkspaceGovernanceAuthorizer:     options.WorkspaceGovernanceAuthorizer,
		WikiGenerator:                     options.KBWikiGenerator,
		ActivityStore:                     stores.Activity,
		ActivityProjector:                 options.ActivityProjector,
		ActivityRefreshAfter:              options.ActivityRefreshAfter,
		AssetAccessOutbox:                 stores.Memory,
		SyncTokenIssuer:                   options.RepositorySyncTokenIssuer,
	})
	memoryInitializer := options.MemoryRepositoryInitializer
	memoryForker := options.MemoryRepositoryForker
	memoryDeleter := options.MemoryRepositoryDeleter
	if memoryInitializer == nil {
		if initializer, ok := options.AGSRepositoryStore.(memory.MemoryRepositoryInitializer); ok {
			memoryInitializer = initializer
		}
	}
	if memoryForker == nil {
		if forker, ok := options.AGSRepositoryStore.(memory.MemoryRepositoryForker); ok {
			memoryForker = forker
		}
	}
	if memoryDeleter == nil {
		if deleter, ok := options.AGSRepositoryStore.(repos.RepositoryDeleter); ok {
			memoryDeleter = deleter
		}
	}
	svc.Memory = memory.NewMemoryServiceWithOptions(stores.Memory, stores.Repos, options.AGSRepositoryStore, now, memory.MemoryServiceOptions{
		RepositoryInitializer: memoryInitializer,
		RepositoryForker:      memoryForker,
		RepositoryDeleter:     memoryDeleter,
		RepositoryBackups:     options.MemoryRepositoryBackups,
		AgentOwnershipSource:  options.MemoryAgentOwnershipSource,
		SyncTokenIssuer:       options.RepositorySyncTokenIssuer,
		ActivityStore:         stores.Activity,
		ActivityProjector:     options.ActivityProjector,
		ActivityRefreshAfter:  options.ActivityRefreshAfter,
		ContentAuthority:      options.MemoryContentAuthority,
		NativeContent:         options.NativeMemoryContent,
		NativeContentEnabled:  options.NativeMemoryContentEnabled,
		MigrationSource:       options.MemoryMigrationSource,
		MigrationWorker:       options.MemoryMigrationWorker,
		MigrationMappings:     options.MemoryMigrationMappings,
		MigrationContent:      options.MemoryMigrationContent,
	})
	return svc
}

func uaDashboardRepository(store wiki.AGSRepositoryStore) kbservice.UADashboardRepository {
	if dashboard, ok := store.(kbservice.UADashboardRepository); ok {
		return dashboard
	}
	return nil
}

func sourceRepositoryInitializer(store wiki.AGSRepositoryStore) kbservice.KBSourceRepositoryInitializer {
	if store == nil {
		return nil
	}
	if initializer, ok := store.(kbservice.KBSourceRepositoryInitializer); ok {
		return initializer
	}
	return nil
}
