package controlplane

import (
	"context"
	"database/sql"

	"github.com/nexuscrm/analytics"
	"github.com/nexuscrm/auth"
	"github.com/nexuscrm/automation/autonumber"
	"github.com/pingcap-inc/nexuscrm-controlplane/packs"
	"github.com/nexuscrm/kernel/pkg/ports"
	"github.com/nexuscrm/metadata"
	"github.com/nexuscrm/permissions"
	"github.com/nexuscrm/records"
)

// testServiceGraphConfig carries the individual service handles a test wants the
// control-plane path under test to see. It replaces the former dependency on the
// concrete *services.ServiceManager (which lives in backend/internal/application/
// services and cannot be imported from this sibling module under Go's internal/
// visibility rule). Tests populate only the fields the path needs; the rest stay
// nil. Integration/crm-tagged tests that need a real, DB-backed ServiceManager
// build their ServiceGraph through the public controlplane.BuildServiceGraph seam
// (wired by the backend composition root) instead of this adapter.
type testServiceGraphConfig struct {
	Metadata           *metadata.MetadataService
	Schema             *metadata.SchemaManager
	Permissions        *permissions.PermissionService
	FormulaView        *analytics.FormulaViewService
	AutoNumberMetadata *autonumber.MetadataSource
	Auth               *auth.AuthService
	System             packs.SystemAdmin
	ConnectorInbound   ConnectorInboundLifecycle

	// Record-setup handles. The default-build white-box tests that use this
	// adapter only inject the apply-path service handles above and never the
	// record/query API, so these stay nil here; integration/crm-tagged tests that
	// need a real record-setup graph build it through controlplane.BuildServiceGraph
	// (wired by the backend composition root) instead of this adapter.
	Persistence *records.PersistenceService
	QuerySvc    ports.QueryService
	DB          *sql.DB
}

// testServiceGraph implements the ServiceGraph consumer interface directly from
// individual service handles. It mirrors the production adapter in
// pkg/serverruntime (which the test packages cannot import without a cycle:
// serverruntime imports controlplane) without naming any backend-internal type.
type testServiceGraph struct {
	cfg testServiceGraphConfig
}

func newTestServiceGraph(cfg testServiceGraphConfig) ServiceGraph {
	return testServiceGraph{cfg: cfg}
}

var _ ServiceGraph = testServiceGraph{}

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

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

func (a testServiceGraph) ConnectorInbound() ConnectorInboundLifecycle {
	if a.cfg.ConnectorInbound == nil {
		return nil
	}
	return a.cfg.ConnectorInbound
}

func (a testServiceGraph) RefreshMetadataCache(ctx context.Context) error {
	// Mirror services.ServiceManager.RefreshMetadataCache: refresh AutoNumber
	// metadata, then Metadata, then Permissions — each nil-safe.
	if a.cfg.AutoNumberMetadata != nil {
		if err := a.cfg.AutoNumberMetadata.Refresh(ctx); err != nil {
			return err
		}
	}
	if a.cfg.Metadata != nil {
		if err := a.cfg.Metadata.RefreshCache(ctx); err != nil {
			return err
		}
	}
	if a.cfg.Permissions != nil {
		if err := a.cfg.Permissions.RefreshPermissions(ctx); err != nil {
			return err
		}
	}
	return nil
}

func (a testServiceGraph) ReconcileChangedConnectorInboundWorkers(ctx context.Context) error {
	// Mirror services.ServiceManager.ReconcileChangedConnectorInboundWorkers:
	// nil ConnectorInbound is a no-op, otherwise delegate to ReconcileChanged.
	if a.cfg.ConnectorInbound == nil {
		return nil
	}
	return a.cfg.ConnectorInbound.ReconcileChanged(ctx)
}
