package serverruntime

import (
	"context"
	"database/sql"

	"github.com/nexuscrm/analytics"
	"github.com/nexuscrm/auth"
	"github.com/nexuscrm/automation/autonumber"
	"github.com/nexuscrm/backend/internal/application/services"
	database "github.com/nexuscrm/db"
	"github.com/nexuscrm/events"
	"github.com/nexuscrm/kernel/pkg/ports"
	"github.com/nexuscrm/metadata"
	"github.com/nexuscrm/permissions"
	"github.com/nexuscrm/records"
	controlplane "github.com/pingcap-inc/nexuscrm-controlplane"
	"github.com/pingcap-inc/nexuscrm-controlplane/packs"
)

// This file is the backend composition root's binding of the control-plane
// Runner's ServiceGraph seam. It owns construction of the application service
// graph for the headless (no-HTTP) pack-apply path: building the infrastructure
// dependencies and the ServiceManager. The control-plane module declares the
// narrow consumer interfaces (ServiceGraph + ConnectorInboundLifecycle in
// internal/controlplane/service_graph.go; SystemAdmin in
// internal/controlplane/packs/deps.go); this file supplies the concrete graph
// and adapts it, so internal/controlplane never imports
// internal/application/services.

func init() {
	controlplane.BuildServiceGraph = buildControlplaneServiceGraph
}

// buildControlplaneServiceGraph constructs the headless service graph used by
// controlplane.Runner. It mirrors the former in-package controlplane.NewRunner
// composition: build InfrastructureDeps, force NoOpEventBus + no-op metrics for
// headless mode, then NewServiceManager. The wiring (NoOpEventBus, no-op metric
// callbacks) is preserved byte-for-byte from the previous controlplane.NewRunner.
func buildControlplaneServiceGraph(conn *database.TiDBConnection) (controlplane.ServiceGraph, controlplane.RunnerInfra, error) {
	infraDeps := buildInfrastructureDeps(conn)

	// Headless mode: inject NoOpEventBus so listeners are inert.
	infraDeps.EventBus = &events.NoOpEventBus{}
	// No-op metrics callbacks (no Prometheus in headless mode).
	infraDeps.OnEventDropped = func(string) {}
	infraDeps.OnQueueUtilizationChange = func(float64) {}

	svcMgr, err := services.NewServiceManager(infraDeps)
	if err != nil {
		return nil, controlplane.RunnerInfra{}, err
	}

	infra := controlplane.RunnerInfra{
		TxManager:    infraDeps.TxManager,
		MetadataRepo: infraDeps.MetadataRepo,
		SessionRepo:  infraDeps.SessionRepo,
	}
	return serviceGraphAdapter{sm: svcMgr}, infra, nil
}

// buildInfrastructureDeps constructs setup's repositories and transaction manager.
// The headless builder above supplies an inert event bus and metrics callbacks.
func buildInfrastructureDeps(conn *database.TiDBConnection) services.InfrastructureDeps {
	return services.BuildInfrastructureDeps(conn)
}

// serviceGraphAdapter adapts a *services.ServiceManager to the control-plane's
// ServiceGraph consumer interface. Setup never owns Integration workers.
type serviceGraphAdapter struct {
	sm *services.ServiceManager
}

var _ controlplane.ServiceGraph = serviceGraphAdapter{}

func (a serviceGraphAdapter) Metadata() *metadata.MetadataService         { return a.sm.Metadata }
func (a serviceGraphAdapter) Schema() *metadata.SchemaManager             { return a.sm.Schema }
func (a serviceGraphAdapter) Permissions() *permissions.PermissionService { return a.sm.Permissions }
func (a serviceGraphAdapter) FormulaView() *analytics.FormulaViewService  { return a.sm.FormulaViewSvc }
func (a serviceGraphAdapter) AutoNumberMetadata() *autonumber.MetadataSource {
	return a.sm.AutoNumberMetadata
}
func (a serviceGraphAdapter) Auth() *auth.AuthService   { return a.sm.Auth }
func (a serviceGraphAdapter) System() packs.SystemAdmin { return a.sm.System }

func (a serviceGraphAdapter) Persistence() *records.PersistenceService { return a.sm.Persistence }
func (a serviceGraphAdapter) QuerySvc() ports.QueryService             { return a.sm.QuerySvc }
func (a serviceGraphAdapter) DB() *sql.DB                              { return a.sm.DB() }

func (a serviceGraphAdapter) ConnectorInbound() controlplane.ConnectorInboundLifecycle {
	return nil
}

func (a serviceGraphAdapter) RefreshMetadataCache(ctx context.Context) error {
	return a.sm.RefreshMetadataCache(ctx)
}

func (a serviceGraphAdapter) ReconcileChangedConnectorInboundWorkers(ctx context.Context) error {
	// The Integration process observes installed catalog changes itself.
	return nil
}
