package container import ( "fmt" "attune-heart-therapy/internal/config" "attune-heart-therapy/internal/database" "attune-heart-therapy/internal/handlers" "attune-heart-therapy/internal/logger" "attune-heart-therapy/internal/repositories" "attune-heart-therapy/internal/services" ) // Container holds all application dependencies type Container struct { Config *config.Config Database *database.DB Log *logger.Logger // Repositories Repositories *repositories.Repositories // Services JWTService services.JWTService UserService services.UserService BookingService services.BookingService PaymentService services.PaymentService NotificationService services.NotificationService JitsiService services.JitsiService AdminService services.AdminService JobManagerService services.JobManagerService // Handlers AuthHandler *handlers.AuthHandler BookingHandler *handlers.BookingHandler PaymentHandler *handlers.PaymentHandler AdminHandler *handlers.AdminHandler MeetingHandler *handlers.MeetingHandler } // New creates a new dependency injection container func New(cfg *config.Config) *Container { return &Container{ Config: cfg, Log: logger.New("container"), } } // Initialize sets up all dependencies in the correct order func (c *Container) Initialize() error { c.Log.Info("Initializing application dependencies...") // Initialize database connection if err := c.initializeDatabase(); err != nil { return fmt.Errorf("failed to initialize database: %w", err) } // Initialize repositories c.initializeRepositories() // Initialize services if err := c.initializeServices(); err != nil { return fmt.Errorf("failed to initialize services: %w", err) } // Initialize handlers c.initializeHandlers() c.Log.Info("Application dependencies initialized successfully") return nil } // initializeDatabase sets up database connection and runs migrations func (c *Container) initializeDatabase() error { c.Log.Info("Initializing database connection...") db, err := database.New(c.Config) if err != nil { return fmt.Errorf("failed to create database connection: %w", err) } c.Database = db // Run database migrations c.Log.Info("Running database migrations...") if err := c.Database.Migrate(); err != nil { return fmt.Errorf("failed to run database migrations: %w", err) } // Seed database with initial data c.Log.Info("Seeding database with initial data...") if err := c.Database.Seed(); err != nil { return fmt.Errorf("failed to seed database: %w", err) } c.Log.Info("Database initialization completed") return nil } // initializeRepositories sets up all repository instances func (c *Container) initializeRepositories() { c.Log.Info("Initializing repositories...") c.Repositories = c.Database.GetRepositories() c.Log.Info("Repositories initialized") } // initializeServices sets up all service instances with proper dependency injection func (c *Container) initializeServices() error { c.Log.Info("Initializing services...") // Initialize JWT service (no dependencies) c.JWTService = services.NewJWTService(c.Config.JWT.Secret, c.Config.JWT.Expiration) // Initialize Jitsi service (no dependencies) c.JitsiService = services.NewJitsiService(&c.Config.Jitsi) // Initialize notification service (depends on notification repository, config, and jitsi service) c.NotificationService = services.NewNotificationService(c.Repositories.Notification, c.Config, c.JitsiService) // Initialize user service (depends on user repository, JWT service, and notification service) c.UserService = services.NewUserService(c.Repositories.User, c.JWTService, c.NotificationService) // Initialize payment service (depends on config, booking/user repositories, and notification service) c.PaymentService = services.NewPaymentService(c.Config, c.Repositories.Booking, c.Repositories.User, c.NotificationService) // Initialize job manager service (depends on notification service and repositories) c.JobManagerService = services.NewJobManagerService(c.NotificationService, c.Repositories.Booking, c.Repositories.User) // Start the job manager c.Log.Info("Starting job manager...") if err := c.JobManagerService.Start(); err != nil { return fmt.Errorf("failed to start job manager: %w", err) } c.Log.Info("Job manager started successfully") // Initialize booking service (depends on multiple repositories and services) c.BookingService = services.NewBookingService( c.Repositories.Booking, c.Repositories.Schedule, c.Repositories.User, c.JitsiService, c.PaymentService, c.NotificationService, c.JobManagerService, ) // Initialize admin service (depends on repositories) c.AdminService = services.NewAdminService(c.Repositories.User, c.Repositories.Booking, c.Repositories.Schedule) c.Log.Info("Services initialized successfully") return nil } // initializeHandlers sets up all HTTP handlers with service dependencies func (c *Container) initializeHandlers() { c.Log.Info("Initializing handlers...") c.AuthHandler = handlers.NewAuthHandler(c.UserService) c.BookingHandler = handlers.NewBookingHandler(c.BookingService, c.Repositories.User, c.JitsiService) c.PaymentHandler = handlers.NewPaymentHandler(c.PaymentService) c.AdminHandler = handlers.NewAdminHandler(c.AdminService, c.Repositories.User, c.JitsiService) c.MeetingHandler = handlers.NewMeetingHandler(c.Repositories.Booking, c.Repositories.User, c.JitsiService) c.Log.Info("Handlers initialized successfully") } // Shutdown gracefully shuts down all services and connections func (c *Container) Shutdown() error { c.Log.Info("Shutting down application dependencies...") var shutdownErrors []error // Stop job manager first if c.JobManagerService != nil && c.JobManagerService.IsRunning() { c.Log.Info("Stopping job manager...") if err := c.JobManagerService.Stop(); err != nil { c.Log.Error("Error stopping job manager", err) shutdownErrors = append(shutdownErrors, fmt.Errorf("job manager shutdown error: %w", err)) } else { c.Log.Info("Job manager stopped successfully") } } // Close database connection if c.Database != nil { c.Log.Info("Closing database connection...") if err := c.Database.Close(); err != nil { c.Log.Error("Error closing database", err) shutdownErrors = append(shutdownErrors, fmt.Errorf("database shutdown error: %w", err)) } else { c.Log.Info("Database connection closed successfully") } } if len(shutdownErrors) > 0 { return fmt.Errorf("shutdown completed with errors: %v", shutdownErrors) } c.Log.Info("Application dependencies shutdown completed successfully") return nil } // GetJWTService returns the JWT service for middleware usage func (c *Container) GetJWTService() services.JWTService { return c.JWTService } // HealthCheck performs a health check on all critical dependencies func (c *Container) HealthCheck() map[string]interface{} { health := map[string]interface{}{ "status": "ok", } // Check database connectivity if c.Database != nil { if err := c.Database.Health(); err != nil { health["database"] = map[string]interface{}{ "status": "error", "error": err.Error(), } health["status"] = "degraded" } else { health["database"] = map[string]interface{}{ "status": "healthy", } } } else { health["database"] = map[string]interface{}{ "status": "not_initialized", } health["status"] = "error" } // Check job manager status if c.JobManagerService != nil { isRunning := c.JobManagerService.IsRunning() health["job_manager"] = map[string]interface{}{ "status": "healthy", "running": isRunning, } if !isRunning { health["job_manager"].(map[string]interface{})["status"] = "unhealthy" health["status"] = "degraded" } } else { health["job_manager"] = map[string]interface{}{ "status": "not_initialized", } health["status"] = "degraded" } // Check services initialization servicesHealth := map[string]interface{}{} if c.UserService != nil { servicesHealth["user_service"] = "initialized" } else { servicesHealth["user_service"] = "not_initialized" health["status"] = "degraded" } if c.BookingService != nil { servicesHealth["booking_service"] = "initialized" } else { servicesHealth["booking_service"] = "not_initialized" health["status"] = "degraded" } if c.PaymentService != nil { servicesHealth["payment_service"] = "initialized" } else { servicesHealth["payment_service"] = "not_initialized" health["status"] = "degraded" } if c.NotificationService != nil { servicesHealth["notification_service"] = "initialized" } else { servicesHealth["notification_service"] = "not_initialized" health["status"] = "degraded" } health["services"] = servicesHealth return health } // IsInitialized returns true if the container has been initialized func (c *Container) IsInitialized() bool { return c.Database != nil && c.Repositories != nil && c.UserService != nil } // GetDependencyStatus returns the status of all dependencies func (c *Container) GetDependencyStatus() map[string]string { status := make(map[string]string) // Database status if c.Database != nil { if err := c.Database.Health(); err != nil { status["database"] = "unhealthy" } else { status["database"] = "healthy" } } else { status["database"] = "not_initialized" } // Services status services := []struct { name string service interface{} }{ {"user_service", c.UserService}, {"booking_service", c.BookingService}, {"payment_service", c.PaymentService}, {"notification_service", c.NotificationService}, {"jitsi_service", c.JitsiService}, {"admin_service", c.AdminService}, {"job_manager_service", c.JobManagerService}, {"jwt_service", c.JWTService}, } for _, svc := range services { if svc.service != nil { status[svc.name] = "initialized" } else { status[svc.name] = "not_initialized" } } // Job manager running status if c.JobManagerService != nil { if c.JobManagerService.IsRunning() { status["job_manager_running"] = "true" } else { status["job_manager_running"] = "false" } } return status }