package services

import (
	"database/sql"
	"fmt"
	"log"
	"os"
	"strconv"

	database "github.com/nexuscrm/db"
	"github.com/nexuscrm/kernel/pkg/logging"
	"github.com/nexuscrm/permissions"
	"github.com/nexuscrm/persistence"
	"github.com/nexuscrm/records"
)

// BuildInfrastructureDeps constructs the infrastructure dependencies
// (repositories, transaction manager) from the database connection. It is the
// shared composition-root builder used by the live server (serverruntime), the
// headless control-plane pack-apply builder, and integration tests. It lives in
// the services package — the owner of InfrastructureDeps — so every consumer
// (serverruntime, white-box interfaces/rest tests, automationintegration tests)
// can reach it without importing serverruntime (which would cycle through
// interfaces/rest) or the control-plane module.
//
// EventBus and metrics callbacks are left for the caller to set:
//   - live server (runtime.go): leaves EventBus nil → NewServiceManager creates a
//     real EventBus with metrics, and sets real metric callbacks.
//   - headless builder: forces NoOpEventBus + no-op metrics.
func BuildInfrastructureDeps(conn *database.TiDBConnection) InfrastructureDeps {
	sqlDB := conn.DB()

	txm := persistence.NewTransactionManager(conn)
	permissionStateAuthority, err := persistence.NewPermissionStateAuthority(txm)
	if err != nil {
		panic(fmt.Sprintf("permission state authority: %v", err))
	}
	permissionStateDirectWriters, err := persistence.NewPermissionStateDirectWriterCoordinator(
		txm,
		permissionStateAuthority,
	)
	if err != nil {
		panic(fmt.Sprintf("permission state direct writer coordinator: %v", err))
	}
	// PESSIMISTIC_LOCK_WAIT_TIMEOUT_SECS sets the per-session
	// innodb_lock_wait_timeout for pessimistic transactions. Empty or
	// unparseable values default to 10s. Explicit "0" disables the override.
	// Negative values are clamped to 0 (disabled).
	lockWaitSecs := 10
	if raw := os.Getenv("PESSIMISTIC_LOCK_WAIT_TIMEOUT_SECS"); raw != "" {
		n, err := strconv.Atoi(raw)
		if err != nil {
			log.Printf("warning: PESSIMISTIC_LOCK_WAIT_TIMEOUT_SECS=%q is not a valid integer; using default %ds", raw, lockWaitSecs)
		} else {
			if n < 0 {
				n = 0
			}
			lockWaitSecs = n
		}
	}
	txm.SetPessimisticLockWaitTimeout(lockWaitSecs)
	fieldProjectionAdmission, fieldProjectionConfigErr := permissions.FieldProjectionAdmissionLimitsFromEnv(os.Getenv)
	if fieldProjectionConfigErr != nil {
		log.Printf("warning: %v", fieldProjectionConfigErr)
	}
	recordRepo := persistence.NewRecordRepository(sqlDB, persistence.WithIndependentObjectIDProbeDB(func() (*sql.DB, error) {
		return conn.OpenIndependentDB(database.PoolOptions{MaxOpenConns: 1, MaxIdleConns: 1})
	}))
	systemRepo := persistence.NewSystemRepository(sqlDB)
	if err := systemRepo.SetTransactionManager(txm); err != nil {
		panic(fmt.Sprintf("system repository transaction manager: %v", err))
	}
	permissionRepo := persistence.NewPermissionRepository(sqlDB)
	if err := permissionRepo.SetPermissionStateDirectWriterCoordinator(permissionStateDirectWriters); err != nil {
		panic(fmt.Sprintf("permission repository authority: %v", err))
	}
	userRepo := persistence.NewUserRepository(sqlDB)
	if err := userRepo.SetPermissionStateDirectWriterCoordinator(permissionStateDirectWriters); err != nil {
		panic(fmt.Sprintf("user repository authority: %v", err))
	}
	metadataRepo := persistence.NewMetadataRepository(sqlDB)
	if err := metadataRepo.SetPermissionStateDirectWriterCoordinator(permissionStateDirectWriters); err != nil {
		panic(fmt.Sprintf("metadata repository authority: %v", err))
	}
	schemaRepo := persistence.NewSchemaRepository(sqlDB, logging.Default)
	if err := schemaRepo.SetPermissionStateDirectWriterCoordinator(permissionStateDirectWriters); err != nil {
		panic(fmt.Sprintf("schema repository authority: %v", err))
	}

	return InfrastructureDeps{
		DB:                       sqlDB,
		TxManager:                txm,
		PermissionStateAuthority: permissionStateAuthority,
		ParentLaneAdmission:      records.NewParentLaneAdmissionController(persistence.NewParentLaneStore(conn)),
		FieldProjectionAdmission: &fieldProjectionAdmission,

		UserRepo:              userRepo,
		SystemRepo:            systemRepo,
		BulkRepo:              persistence.NewBulkRepository(sqlDB, txm),
		SchemaRepo:            schemaRepo,
		MetadataRepo:          metadataRepo,
		SessionRepo:           persistence.NewSessionRepository(sqlDB),
		PermissionRepo:        permissionRepo,
		EffectiveSnapshotRepo: permissionRepo,
		OAuthRepo:             persistence.NewOAuthRepository(sqlDB),
		ApiTokenRepo:          persistence.NewApiTokenRepository(sqlDB),
		QueryRepo:             persistence.NewQueryRepository(sqlDB),
		RecordRepo:            recordRepo,
		OutboxRepo:            persistence.NewOutboxRepository(sqlDB),
		RecordEventOutboxRepo: persistence.NewRecordEventOutboxRepository(sqlDB),
		RollupRepo:            persistence.NewRollupRepository(sqlDB),
	}
}
