package app

import (
	"fmt"
	"time"

	"git.linuxforward.com/byop/byop-engine/auth"
	"git.linuxforward.com/byop/byop-engine/cloud"
	"git.linuxforward.com/byop/byop-engine/dbmanager"
	"git.linuxforward.com/byop/byop-engine/dbstore"
	"git.linuxforward.com/byop/byop-engine/handlers"
	mw "git.linuxforward.com/byop/byop-engine/middleware"
	"git.linuxforward.com/byop/byop-engine/models"
	"git.linuxforward.com/byop/byop-engine/services"
	"github.com/pkg/errors"

	"github.com/gin-gonic/gin"
)

func (a *App) initCommonServices() error {
	// Initialize token store
	a.tokenStore = auth.NewMemoryTokenStore(time.Duration(a.cnf.Auth.CleanupInterval))

	// Initialize authentication service
	a.authService = auth.NewJWTService(
		[]byte(a.cnf.Auth.PrivateKey),
		time.Duration(a.cnf.Auth.TokenDuration),
		a.tokenStore,
	)

	// Initialize providers
	if err := a.loadProviders(); err != nil {
		return errors.Wrap(err, "load providers")
	}

	// Initialize database manager
	switch a.cnf.Database.Type {
	case "sqlite":
		var err error
		a.dbManager, err = dbmanager.NewSQLiteManager(a.cnf.Database.Sqlite.File)
		if err != nil {
			return fmt.Errorf("create sqlite manager: %w", err)
		}
	// case "postgres":
	// 	a.dbManager = dbmanager.NewPostgresDbManager(a.cnf.Db.Host, a.cnf.Db.Port, a.cnf.Db.User, a.cnf.Db.Password, a.cnf.Db.Name)
	// case "mysql":
	// 	a.dbManager = dbmanager.NewMySQLDbManager(a.cnf.Db.Host, a.cnf.Db.Port, a.cnf.Db.User, a.cnf.Db.Password, a.cnf.Db.Name)
	default:
		return fmt.Errorf("unsupported database type: %s", a.cnf.Database.Type)
	}
	if err := a.dbManager.Connect(); err != nil {
		return fmt.Errorf("connect to database: %w", err)
	}

	// Auto migrate database schema
	if err := a.dbManager.Migrate(
		&models.User{},
		&models.Client{},
		&models.Component{},
		&models.Blueprint{},
		&models.Deployment{},
		&models.DeployedComponent{},
		&models.DeployedComponentResource{},
		// Add other models here
	); err != nil {
		return fmt.Errorf("migrate database: %w", err)
	}

	a.entry.Info("Services initialized successfully, including authentication and database manager")
	return nil
}

func (a *App) initHandlers() error {

	// Initialize UserModule
	userStore := dbstore.NewUserStore(a.dbManager)
	userService := services.NewUserService(userStore)
	userHandler := handlers.NewUserHandler(userService)
	a.userModule = &UserModule{
		Store:   userStore,
		Service: userService,
		Handler: userHandler,
	}

	// Initialize ClientModule
	clientStore := dbstore.NewClientStore(a.dbManager)
	clientService := services.NewClientService(clientStore)
	clientHandler := handlers.NewClientHandler(clientService)
	a.clientModule = &ClientModule{
		Store:   clientStore,
		Service: clientService,
		Handler: clientHandler,
	}

	// Initialize ComponentModule
	componentStore := dbstore.NewComponentStore(a.dbManager)
	componentService := services.NewComponentService(componentStore)
	componentHandler := handlers.NewComponentHandler(componentService)
	a.componentModule = &ComponentModule{
		Store:   componentStore,
		Service: componentService,
		Handler: componentHandler,
	}

	// Initialize BlueprintModule
	blueprintStore := dbstore.NewBlueprintStore(a.dbManager)
	blueprintService := services.NewBlueprintService(blueprintStore)
	blueprintHandler := handlers.NewBlueprintHandler(blueprintService)
	a.blueprintModule = &BlueprintModule{
		Store:   blueprintStore,
		Service: blueprintService,
		Handler: blueprintHandler,
	}

	// Initialize DeploymentModule
	deploymentStore := dbstore.NewDeploymentStore(a.dbManager)
	deploymentService := services.NewDeploymentService(
		deploymentStore,
		componentStore,
		blueprintStore,
		clientStore,
	)
	deploymentHandler := handlers.NewDeploymentHandler(deploymentService)
	a.deploymentModule = &DeploymentModule{
		Store:   deploymentStore,
		Service: deploymentService,
		Handler: deploymentHandler,
	}

	// Initialize authentication handler
	a.authHandler = handlers.NewAuthHandler(a.authService, a.userModule.Store)

	// Initialize resource handlers
	a.providerHandler = handlers.NewProviderHandler()
	// Initialize other handlers...

	a.entry.Info("Handlers initialized successfully")
	return nil
}

// Updated loadProviders method
func (a *App) loadProviders() error {
	for name, cnf := range a.cnf.Providers {
		// Use the new InitializeProvider function instead of GetProvider + Initialize
		err := cloud.InitializeProvider(name, cnf)
		if err != nil {
			return fmt.Errorf("initialize provider %s: %w", name, err)
		}
		a.entry.WithField("provider", name).Info("Provider initialized")
	}
	a.entry.Info("All providers loaded successfully")
	return nil
}

func (a *App) setupRoutes() {
	// API version group
	v1 := a.rtr.Group("/api/v1")

	// Auth routes - no middleware required

	// Public routes (no authentication required)
	public := v1.Group("/")
	public.POST("/users", a.userModule.Handler.CreateUser) // Allow user registration without authentication

	// Auth routes - no middleware required
	public.POST("/login", a.authHandler.Login)                // Allow login without authentication
	public.POST("/refresh-token", a.authHandler.RefreshToken) // Allow token refresh without authentication
	public.POST("/logout", a.authHandler.Logout)              // Allow logout without authentication
	public.GET("/health", func(c *gin.Context) {
		c.JSON(200, gin.H{"status": "ok"})
	})

	// Protected routes - require authentication
	protected := v1.Group("/")
	protected.Use(mw.Auth(a.authService)) // Auth middleware with service dependency

	// Register resource routes
	providers := protected.Group("/providers")
	a.providerHandler.RegisterRoutes(providers)

	// Client routes - registering both with and without trailing slash
	clients := protected.Group("/clients")
	clients.GET("", a.clientModule.Handler.ListClients)    // Without trailing slash
	clients.GET("/", a.clientModule.Handler.ListClients)   // With trailing slash
	clients.POST("", a.clientModule.Handler.CreateClient)  // Without trailing slash
	clients.POST("/", a.clientModule.Handler.CreateClient) // With trailing slash
	clients.GET("/:id", a.clientModule.Handler.GetClient)
	clients.PUT("/:id", a.clientModule.Handler.UpdateClient)
	clients.DELETE("/:id", a.clientModule.Handler.DeleteClient)
	clients.GET("/:id/deployments", a.clientModule.Handler.GetClientDeployments)

	// User routes - registering both with and without trailing slash
	users := protected.Group("/users")
	users.GET("", a.userModule.Handler.ListUsers)  // Without trailing slash
	users.GET("/", a.userModule.Handler.ListUsers) // With trailing slash
	users.GET("/:id", a.userModule.Handler.GetUser)
	users.PUT("/:id", a.userModule.Handler.UpdateUser)
	users.DELETE("/:id", a.userModule.Handler.DeleteUser)
	users.GET("/:id/deployments", a.userModule.Handler.GetUserDeployments)

	// Component routes - registering both with and without trailing slash
	components := protected.Group("/components")
	components.GET("", a.componentModule.Handler.ListComponents)    // Without trailing slash
	components.GET("/", a.componentModule.Handler.ListComponents)   // With trailing slash
	components.POST("", a.componentModule.Handler.CreateComponent)  // Without trailing slash
	components.POST("/", a.componentModule.Handler.CreateComponent) // With trailing slash
	components.GET("/:id", a.componentModule.Handler.GetComponent)
	components.PUT("/:id", a.componentModule.Handler.UpdateComponent)
	components.DELETE("/:id", a.componentModule.Handler.DeleteComponent)
	components.GET("/:id/deployments", a.componentModule.Handler.GetComponentDeployments)

	// Blueprint routes - registering both with and without trailing slash
	blueprints := protected.Group("/blueprints")
	blueprints.GET("", a.blueprintModule.Handler.ListBlueprints)    // Without trailing slash
	blueprints.GET("/", a.blueprintModule.Handler.ListBlueprints)   // With trailing slash
	blueprints.POST("", a.blueprintModule.Handler.CreateBlueprint)  // Without trailing slash
	blueprints.POST("/", a.blueprintModule.Handler.CreateBlueprint) // With trailing slash
	blueprints.GET("/:id", a.blueprintModule.Handler.GetBlueprint)
	blueprints.PUT("/:id", a.blueprintModule.Handler.UpdateBlueprint)
	blueprints.DELETE("/:id", a.blueprintModule.Handler.DeleteBlueprint)
	blueprints.GET("/:id/deployments", a.blueprintModule.Handler.GetBlueprintDeployments)

	// Deployment routes - need to handle both versions
	deployments := protected.Group("/deployments")
	deployments.GET("", a.deploymentModule.Handler.ListDeployments)
	deployments.GET("/", a.deploymentModule.Handler.ListDeployments)
	deployments.POST("", a.deploymentModule.Handler.CreateDeployment)
	deployments.GET("/:id", a.deploymentModule.Handler.GetDeployment)
	deployments.PUT("/:id", a.deploymentModule.Handler.UpdateDeployment)
	deployments.DELETE("/:id", a.deploymentModule.Handler.DeleteDeployment)
	deployments.PUT("/:id/status", a.deploymentModule.Handler.UpdateDeploymentStatus)
	deployments.GET("/by-client/:clientId", a.deploymentModule.Handler.GetDeploymentsByClient)
	deployments.GET("/by-blueprint/:blueprintId", a.deploymentModule.Handler.GetDeploymentsByBlueprint)
	deployments.GET("/by-user/:userId", a.deploymentModule.Handler.GetDeploymentsByUser)

	a.entry.Info("Routes configured successfully")
}
