package testkit

import (
	"github.com/tidbcloud/AgentTeam/internal/api/admin"
	"github.com/tidbcloud/AgentTeam/internal/api/app"
	"github.com/tidbcloud/AgentTeam/internal/api/auth"
	"github.com/tidbcloud/AgentTeam/internal/api/channel"
	"github.com/tidbcloud/AgentTeam/internal/api/im"
	"github.com/tidbcloud/AgentTeam/internal/api/im/fanout"
	"github.com/tidbcloud/AgentTeam/internal/api/media"
	"github.com/tidbcloud/AgentTeam/internal/api/org"
	"github.com/tidbcloud/AgentTeam/internal/api/provider"
	"github.com/tidbcloud/AgentTeam/internal/api/public"
	"github.com/tidbcloud/AgentTeam/internal/api/serviceaccount"
	"github.com/tidbcloud/AgentTeam/internal/api/task"
	"github.com/tidbcloud/AgentTeam/internal/api/team"
	"github.com/tidbcloud/AgentTeam/internal/api/trigger"
	"github.com/tidbcloud/AgentTeam/internal/api/webagent"
	"github.com/tidbcloud/AgentTeam/internal/api/workflow/binding"
	"github.com/tidbcloud/AgentTeam/internal/api/workflow/invocation"
	"github.com/tidbcloud/AgentTeam/internal/api/workflow/plan"
	"github.com/tidbcloud/AgentTeam/internal/api/workflow/run"
	"github.com/tidbcloud/AgentTeam/internal/api/workspace"
	"github.com/tidbcloud/AgentTeam/internal/mailer"
	"github.com/tidbcloud/AgentTeam/internal/service/appruntime"
	"github.com/tidbcloud/AgentTeam/internal/service/collaborator"
	messageservice "github.com/tidbcloud/AgentTeam/internal/service/message"
	"github.com/tidbcloud/AgentTeam/internal/service/role/agents"
	"github.com/tidbcloud/AgentTeam/internal/service/serviceevent"
	"github.com/tidbcloud/AgentTeam/internal/sms"
	"github.com/tidbcloud/AgentTeam/internal/store/message"
)

func publicRouteDependencies(runtime *Runtime) public.Dependencies {
	if runtime == nil {
		return public.Dependencies{
			OpenRegistration:           true,
			RegistrationMode:           auth.RegistrationModeOpen,
			TeamsEnabled:               true,
			BillingEnabled:             true,
			MeshXKnowledgeBasesEnabled: true,
			MeshXWorkflowsEnabled:      true,
			MeshXMiniappPairingEnabled: true,
		}
	}
	return public.Dependencies{
		AuthStore:                  runtimeIdentity(runtime),
		MessageSearchStore:         runtime.Store.Message(),
		ContentReports:             runtime.Store.Reporting(),
		PingCAPAuthEnabled:         runtime.PingCAPAuthConfig.Enabled,
		PasswordAuthDisabled:       runtime.PasswordAuthDisabled,
		OpenRegistration:           runtime.RegistrationPolicy.AllowsOpenRegistration(),
		RegistrationMode:           runtime.RegistrationPolicy.Mode(),
		TeamsEnabled:               true,
		BillingEnabled:             true,
		DaemonConnectBaseURL:       runtime.DaemonConnectBaseURL,
		MeshXEnabled:               runtime.MeshXEnabled,
		MeshXKnowledgeBasesEnabled: runtime.MeshXEnabled,
		MeshXWorkflowsEnabled:      runtime.MeshXEnabled,
		MeshXMiniappPairingEnabled: runtime.MeshXEnabled,
		MeshXBaseURL:               runtime.MeshXBaseURL,
	}
}

func authRouteDependencies(runtime *Runtime) auth.Dependencies {
	if runtime == nil {
		return auth.Dependencies{
			RegistrationPolicy: auth.NewRegistrationPolicy(auth.RegistrationModeOpen, nil),
		}
	}
	return auth.Dependencies{
		Store:                            runtimeIdentity(runtime),
		AuthFlow:                         runtimeAuthFlow(runtime),
		Realtime:                         runtime.LiveEmitter,
		Organizations:                    runtime.Organizations,
		MailerProvider:                   func() mailer.Mailer { return runtime.Mailer },
		MobileVerificationSenderProvider: func() sms.Sender { return runtime.MobileVerificationSender },
		EmailVerificationRequired:        runtime.EmailVerificationRequired,
		RegistrationPolicyProvider:       func() auth.RegistrationPolicy { return runtime.RegistrationPolicy },
		VerificationTokenTTL:             runtime.VerificationTokenTTL,
		SessionTTL:                       runtime.SessionTTL,
		PasswordAuthDisabled:             runtime.PasswordAuthDisabled,
		PingCAPAuthConfigProvider:        func() auth.PingCAPAuthConfig { return runtime.PingCAPAuthConfig },
		Logger:                           runtime.Logger,
	}
}

func adminRouteDependencies(runtime *Runtime) admin.Dependencies {
	if runtime == nil {
		return admin.Dependencies{}
	}
	return admin.Dependencies{
		AuthStore:                 runtimeIdentity(runtime),
		AdminStore:                runtime.Store.Admin(),
		RegistrationAllowlist:     runtimeIdentity(runtime),
		PlatformServiceAccounts:   runtime.PlatformServiceAccount,
		EmailVerificationRequired: runtime.EmailVerificationRequired,
		Logger:                    runtime.Logger,
	}
}

func workspaceRouteDependencies(runtime *Runtime) workspace.Dependencies {
	if runtime == nil {
		return workspace.Dependencies{}
	}
	dep := workspace.Dependencies{
		AuthStore:                 runtimeIdentity(runtime),
		WorkspaceService:          runtimeWorkspaceFlow(runtime),
		PlanStore:                 runtime.Store.Plan(),
		Organizations:             runtime.Organizations,
		HostedAvailability:        runtime.HostedAvailability,
		Mailer:                    runtime.Mailer,
		PublicBaseURL:             runtime.PublicBaseURL,
		EmailVerificationRequired: runtime.EmailVerificationRequired,
		LatestDaemonVersion:       runtime.LatestDaemonVersion,
		Logger:                    runtime.Logger,
	}
	if runtime.Hub != nil {
		dep.MachineRuntime = runtime.Hub
	}
	return dep
}

func orgRouteDependencies(runtime *Runtime) org.Dependencies {
	if runtime == nil {
		return org.Dependencies{}
	}
	return org.Dependencies{
		Store:                     runtimeIdentity(runtime),
		Organizations:             runtime.Organizations,
		PlanStore:                 runtime.Store.Plan(),
		Runtime:                   runtime.Hub,
		LLMRegistry:               runtime.LLMRegistry,
		HostedAvailability:        runtime.HostedAvailability,
		KnowledgeQAPolicies:       runtime.Store.KnowledgeQA(),
		Mailer:                    runtime.Mailer,
		EmailVerificationRequired: runtime.EmailVerificationRequired,
		Logger:                    runtime.Logger,
	}
}

func mediaRouteDependencies(runtime *Runtime) media.Dependencies {
	if runtime == nil {
		return media.Dependencies{}
	}
	return media.Dependencies{
		AuthStore:                 runtimeIdentity(runtime),
		WorkspaceStore:            runtime.Store.Workspace(),
		AgentTokenStore:           runtime.Store.Agent(),
		AgentStore:                runtime.Store.App(),
		AttachmentStore:           runtime.Store.Attachment(),
		PersonalStickerStore:      runtime.Store.Attachment(),
		EmailVerificationRequired: runtime.EmailVerificationRequired,
	}
}

func channelRouteDependencies(runtime *Runtime) channel.Dependencies {
	if runtime == nil {
		return channel.Dependencies{}
	}
	fanout := runtimeDeliveryFanout(runtime)
	dep := channel.Dependencies{
		AuthStore:                 runtimeIdentity(runtime),
		WorkspaceStore:            runtime.Store.Workspace(),
		Channels:                  runtime.Store.Channel(),
		AppStore:                  runtime.Store.App(),
		ThreadStore:               runtime.Store.Message(),
		ThreadSubsystemChannels:   runtime.Store.App(),
		ThreadReviewStore:         runtime.Store.Review(),
		EmailVerificationRequired: runtime.EmailVerificationRequired,
		Realtime:                  runtime.LiveEmitter,
		MessageFanout:             fanout,
		Logger:                    runtime.Logger,
	}
	if runtime.Hub != nil {
		dep.Hub = runtime.Hub
	}
	return dep
}

func taskRouteDependencies(runtime *Runtime) task.Dependencies {
	return taskRouteDependenciesWithStreamWriter(runtime, nil)
}

func taskRouteDependenciesWithStreamWriter(runtime *Runtime, streamWriter im.StreamEventWriter) task.Dependencies {
	if runtime == nil {
		return task.Dependencies{}
	}
	fanout := runtimeDeliveryFanout(runtime)
	if streamWriter == nil {
		streamWriter = im.NewPublishingStreamEventWriter(runtime.Store.IM(), im.NewStreamEventHub())
	}
	return task.Dependencies{
		AuthStore:                   runtimeIdentity(runtime),
		WorkspaceStore:              runtime.Store.Workspace(),
		EmailVerificationRequired:   runtime.EmailVerificationRequired,
		TaskBoard:                   runtime.TaskBoard,
		ReviewStore:                 runtime.Store.Review(),
		Store:                       task.NewTaskParityStoreAdapter(runtimeAppStore(runtime), runtime.Store.PrivateAgents()),
		MessageFanout:               fanout,
		UnreadRuntime:               runtime.UnreadRuntime,
		ConversationReadProjector:   im.NewTaskConversationProjector(runtime.Store.Message(), streamWriter, runtime.UnreadRuntime, runtime.LiveEmitter),
		ConversationPolicyProjector: im.NewTaskConversationProjector(runtime.Store.Message(), streamWriter, runtime.UnreadRuntime, runtime.LiveEmitter),
		CollaboratorRequests:        collaborator.NewWithStore(runtime.Store.DB, runtime.Store.Graph.Approval, runtime.TaskBoard),
		Logger:                      runtime.Logger,
	}
}

func teamRouteDependencies(runtime *Runtime) team.Dependencies {
	if runtime == nil {
		return team.Dependencies{}
	}
	return team.Dependencies{
		AuthStore:                 runtimeIdentity(runtime),
		WorkspaceStore:            runtime.Store.Workspace(),
		Teams:                     runtime.Store.Team(),
		EmailVerificationRequired: runtime.EmailVerificationRequired,
	}
}

func triggerRouteDependencies(runtime *Runtime) trigger.Dependencies {
	if runtime == nil {
		return trigger.Dependencies{}
	}
	return trigger.Dependencies{
		AuthStore:                 runtimeIdentity(runtime),
		WorkspaceStore:            runtime.Store.Workspace(),
		EmailVerificationRequired: runtime.EmailVerificationRequired,
		TriggerService:            runtime.Triggers,
	}
}

func workflowPlanRouteDependencies(runtime *Runtime) plan.Dependencies {
	if runtime == nil {
		return plan.Dependencies{}
	}
	return plan.Dependencies{
		AuthStore:                 runtimeIdentity(runtime),
		WorkspaceStore:            runtime.Store.Workspace(),
		EmailVerificationRequired: runtime.EmailVerificationRequired,
		Service:                   runtime.WorkflowPlan,
		AgentEdit:                 runtime.WorkflowPlanAgentEdit,
	}
}

func workflowBindingRouteDependencies(runtime *Runtime) binding.Dependencies {
	if runtime == nil {
		return binding.Dependencies{}
	}
	return binding.Dependencies{
		AuthStore:                 runtimeIdentity(runtime),
		WorkspaceStore:            runtime.Store.Workspace(),
		EmailVerificationRequired: runtime.EmailVerificationRequired,
		Service:                   runtime.WorkflowBinding,
	}
}

func workflowInvocationRouteDependencies(runtime *Runtime) invocation.Dependencies {
	if runtime == nil {
		return invocation.Dependencies{}
	}
	return invocation.Dependencies{
		AuthStore:                 runtimeIdentity(runtime),
		WorkspaceStore:            runtime.Store.Workspace(),
		EmailVerificationRequired: runtime.EmailVerificationRequired,
		Service:                   runtime.WorkflowInvocation,
	}
}

func workflowRunRouteDependencies(runtime *Runtime) run.Dependencies {
	if runtime == nil {
		return run.Dependencies{}
	}
	return run.Dependencies{
		AuthStore:                 runtimeIdentity(runtime),
		WorkspaceStore:            runtime.Store.Workspace(),
		EmailVerificationRequired: runtime.EmailVerificationRequired,
		Service:                   runtime.WorkflowRun,
		Calendar:                  runtime.WorkflowCalendar,
	}
}

func serviceAccountRouteDependencies(runtime *Runtime) serviceaccount.Dependencies {
	if runtime == nil {
		return serviceaccount.Dependencies{}
	}
	return serviceaccount.Dependencies{
		SessionAuth:               runtimeIdentity(runtime),
		WorkspaceStore:            runtime.Store.Workspace(),
		Service:                   runtime.ServiceAccount,
		PendingRequestProjector:   runtime.ApprovalAppProjector,
		EmailVerificationRequired: runtime.EmailVerificationRequired,
	}
}

func imRouteDependencies(runtime *Runtime) im.Dependencies {
	if runtime == nil {
		return im.Dependencies{}
	}
	messaging := messageservice.New(messageservice.Dependencies{
		Sessions:   runtimeIdentity(runtime),
		Workspaces: runtime.Store.Workspace(),
		Creation:   runtime.Store.App(),
		Queries:    runtime.Store.App(),
		Search:     runtime.Store.Message(),
		Logger:     runtime.Logger,
	})
	imStreamHub := im.NewStreamEventHub()
	imStreamWriter := im.NewPublishingStreamEventWriter(runtime.Store.IM(), imStreamHub)
	dependencies := im.Dependencies{
		SessionAuth:                runtimeIdentity(runtime),
		WorkspaceStore:             runtime.Store.Workspace(),
		Logger:                     runtime.Logger,
		Messages:                   messaging,
		MessageQueries:             messaging,
		Channels:                   runtime.Store.Channel(),
		ChannelAccess:              runtime.Store.Channel(),
		ChannelMembers:             runtime.Store.App(),
		MessageSearch:              runtime.Store.Message(),
		HumanMessages:              messaging,
		ScopeSnapshotTaskBoard:     im.NewScopeSnapshotTaskBoard(runtime.TaskBoard),
		AgentDeliveries:            runtime.Store.App(),
		ReplyThreadRootPreloader:   fanout.NewScopeReplyThreadRootPreloader(runtime.Store.Message()),
		ScopeFanoutMarker:          runtime.Store.App(),
		TaskScopeLifecycle:         runtime.Store.Message(),
		RewindStore:                runtime.Store.Rewind(),
		DMs:                        runtime.Store.App(),
		OwnerContexts:              runtime.Store.App(),
		ChannelNotifications:       runtime.Store.PrivateAgents(),
		ThreadNotifications:        runtime.Store.PrivateAgents(),
		Conversations:              runtime.Store.Message(),
		ConversationSidebarThreads: runtime.Store.Message(),
		Reactions:                  runtime.Store.Reaction(),
		StreamEvents:               runtime.Store.IM(),
		VisibleScopes:              message.NewSQLVisibleScopeAuthorizer(runtime.Pool),
		StreamWriter:               imStreamWriter,
		StreamSubscriptions:        imStreamHub,
		StreamCursors:              runtime.Store.IM(),
		ConversationIndex:          runtime.Store.IM(),
		UnreadRuntime:              runtime.UnreadRuntime,
		MessageUnreadRuntime:       runtime.UnreadRuntime,
		UnreadDeltaSink:            runtime.LiveEmitter,
		EmailVerificationRequired:  runtime.EmailVerificationRequired,
	}
	if runtime.Hub != nil {
		dependencies.AgentNotifier = runtime.Hub
		dependencies.RewindHub = runtime.Hub
	}
	return dependencies
}

func appRouteDependencies(runtime *Runtime) app.Dependencies {
	if runtime == nil {
		return app.Dependencies{}
	}
	fanout := runtimeDeliveryFanout(runtime)
	appStore := runtime.Store.App()
	appRunnerService := appruntime.NewRunnerService(appruntime.RunnerServiceOptions{Store: runtime.Store.Graph.ServiceActor, Authority: appStore})
	return app.Dependencies{
		SessionAuth:                     runtimeIdentity(runtime),
		WorkspaceStore:                  runtime.Store.Workspace(),
		AppStore:                        appStore,
		AppRuntimeStore:                 appStore,
		AppRunnerService:                appRunnerService,
		FollowupDeliverer:               fanout,
		ServiceEventSubscriptionBlocker: serviceevent.NewServiceEventSubscriptionCoordinator(runtime.Store.App()),
		InstallationRequestProjector:    runtime.ApprovalAppProjector,
		Realtime:                        runtime.LiveEmitter,
		EmailVerificationRequired:       runtime.EmailVerificationRequired,
		Logger:                          runtime.Logger,
	}
}

func providerRouteDependencies(runtime *Runtime) provider.Dependencies {
	if runtime == nil {
		return provider.Dependencies{}
	}
	return provider.Dependencies{
		SessionAuth:                     runtimeAppSessionAuth(runtime),
		WorkspaceStore:                  runtimeAppWorkspaceStore(runtime),
		Store:                           runtime.Store.App().Provider(),
		GitHubAdminStore:                runtime.Store.App(),
		GiteaAdminStore:                 runtime.Store.App(),
		InstallationRequestProjector:    runtime.ApprovalAppProjector,
		ServiceEventSubscriptionBlocker: serviceevent.NewServiceEventSubscriptionCoordinator(runtime.Store.App()),
		GitHubConfig: provider.GitHubConfig{
			FlowStateKey: runtime.GitHubFlowStateKey,
			BaseURL:      runtime.GitHubBaseURL,
			APIBaseURL:   runtime.GitHubAPIBaseURL,
		},
		HTTPClient:                runtime.HTTPClient,
		PublicBaseURL:             runtime.PublicBaseURL,
		EmailVerificationRequired: runtime.EmailVerificationRequired,
		Logger:                    runtime.Logger,
	}
}

func webAgentRouteDependencies(runtime *Runtime) webagent.Dependencies {
	if runtime == nil {
		return webagent.Dependencies{}
	}
	dep := webagent.Dependencies{
		AgentStore:                runtime.Store.App(),
		RoleRealizer:              testkitRoleRealizer(runtime),
		RoleAgentCreate:           testkitRoleAgentCreate(runtime),
		SessionAuth:               runtimeIdentity(runtime),
		WorkspaceStore:            runtime.Store.Workspace(),
		ReviewStore:               runtime.Store.Review(),
		Organizations:             runtime.Organizations,
		Realtime:                  runtime.LiveEmitter,
		PublicBaseURL:             runtime.PublicBaseURL,
		EmailVerificationRequired: runtime.EmailVerificationRequired,
		Logger:                    runtime.Logger,
	}
	if runtime.AgentRuntime != nil {
		dep.RuntimeCommands = runtime.AgentRuntime
	}
	if runtime.Hub != nil {
		if runtime.AgentRuntime != nil {
			runtime.AgentRuntime.SetRuntimeControl(runtime.Hub)
		}
		dep.Runtime = runtime.Hub
	}
	return dep
}

func testkitRoleAgentCreate(runtime *Runtime) *agents.Service {
	if runtime == nil || runtime.Store == nil {
		return nil
	}
	return agents.NewService(runtime.Store.App(), testkitRoleRealizer(runtime), agents.ServiceOptions{
		BillingEnabled: true,
		QuotaStore:     runtime.Store.Plan(),
	})
}

func testkitRoleRealizer(runtime *Runtime) agents.RoleRealizer {
	if runtime == nil || !runtime.MeshXEnabled {
		return nil
	}
	return agents.NewMeshXRealizer(agents.MeshXRealizerOptions{
		BaseURL:    runtime.MeshXBaseURL,
		HTTPClient: runtime.HTTPClient,
	})
}
