package app

import (
	"context"
	"fmt"
	"net/http"
	"os"
	"os/signal"
	"strconv"
	"syscall"
	"time"

	"git.linuxforward.com/byop/byop-engine/auth"
	"git.linuxforward.com/byop/byop-engine/cloud"
	"git.linuxforward.com/byop/byop-engine/config"
	"git.linuxforward.com/byop/byop-engine/dbmanager"
	"git.linuxforward.com/byop/byop-engine/handlers"
	mw "git.linuxforward.com/byop/byop-engine/middleware"
	"github.com/gin-gonic/gin"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
)

type App struct {
	entry      *logrus.Entry
	cnf        *config.Config
	ctx        context.Context
	cancelFunc context.CancelFunc
	rtr        *gin.Engine
	// Database
	dbManager dbmanager.DbManager[dbmanager.Entity]
	dbFactory *dbmanager.DbManagerFactory

	// Services
	authService auth.Service
	tokenStore  auth.TokenStore

	// Common Handlers
	authHandler   *handlers.AuthHandler
	clientHandler *handlers.ClientHandler

	// Resource Handlers
	providerHandler   *handlers.ProviderHandler
	deploymentHandler *handlers.DeploymentHandler
	templateHandler   *handlers.TemplateHandler
	ticketHandler     *handlers.TicketHandler
	monitoringHandler *handlers.MonitoringHandler
}

func NewApp(cnf *config.Config) (*App, error) {
	ctx, cancelFunc := context.WithCancel(context.Background())

	app := &App{
		entry:      logrus.WithField("component", "app"),
		cnf:        cnf,
		ctx:        ctx,
		cancelFunc: cancelFunc,
	}

	// Initialize router first
	if cnf.Debug {
		gin.SetMode(gin.DebugMode)
	} else {
		// Set gin to release mode for production
		// This will disable debug logs and enable performance optimizations
		gin.SetMode(gin.ReleaseMode)
	}
	app.rtr = gin.New()
	app.rtr.Use(gin.Recovery())
	app.rtr.Use(mw.Logger)

	// Initialize database connection
	if err := app.initDatabase(); err != nil {
		return nil, errors.Wrap(err, "initialize database")
	}

	// Initialize services and handlers
	if err := app.initServices(); err != nil {
		return nil, errors.Wrap(err, "initialize services")
	}

	if err := app.initHandlers(); err != nil {
		return nil, errors.Wrap(err, "initialize handlers")
	}

	// Set up routes after all handlers are initialized
	app.setupRoutes()

	return app, nil
}

func (a *App) Run() error {

	srv := &http.Server{
		Addr:    fmt.Sprintf(":%s", strconv.Itoa(a.cnf.Server.Port)),
		Handler: a.rtr,
	}

	go func() {
		a.entry.WithField("address", srv.Addr).Info("Starting server...")
		// Handle TLS if configured
		if a.cnf.Server.Tls.Enabled {
			a.entry.Info("Starting server with TLS...")
			err := srv.ListenAndServeTLS(a.cnf.Server.Tls.CertFile, a.cnf.Server.Tls.KeyFile)
			if err != nil && err != http.ErrServerClosed {
				a.entry.WithError(err).Fatal("Failed to start server")
			}
		} else {
			err := srv.ListenAndServe()
			if err != nil && err != http.ErrServerClosed {
				a.entry.WithError(err).Fatal("Failed to start server")
			}
		}
		a.entry.Info("Server stopped")
	}()

	quit := make(chan os.Signal, 1)
	signal.Notify(quit, os.Interrupt, syscall.SIGTERM)
	<-quit

	a.entry.Info("Stopping server...")
	ctxTimeout, cancelFunc := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancelFunc()
	err := srv.Shutdown(ctxTimeout)
	if err != nil {
		return fmt.Errorf("shutdown server: %w", err)
	}

	a.entry.Info("Server stopped successfully")
	return nil
}

func (a *App) initServices() 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")
	}

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

func (a *App) initHandlers() error {
	// Initialize authentication handler
	a.authHandler = handlers.NewAuthHandler(a.authService)

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

	// Create managers for each entity type
	clientDbManager, err := a.dbFactory.CreateClientManager()
	if err != nil {
		return fmt.Errorf("create client db manager: %w", err)
	}

	if err := clientDbManager.Connect(); err != nil {
		return fmt.Errorf("connect to client database: %w", err)
	}

	a.clientHandler = handlers.NewClientHandler(clientDbManager)
	// Initialize other handlers...

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

func (a *App) loadProviders() error {
	for name, config := range a.cnf.Providers {
		provider, ok := cloud.GetProvider(name)
		if !ok {
			return fmt.Errorf("provider %s not found", name)
		}
		err := provider.Initialize(config)
		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
	a.authHandler.RegisterRoutes(v1)

	// 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)

	clients := protected.Group("/clients")
	a.clientHandler.RegisterRoutes(clients)

	// Register other resource routes...

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

func (a *App) initDatabase() error {
	a.dbFactory = dbmanager.NewDbManagerFactory(a.cnf.Database.Type)
	a.entry.Info("Database factory initialized successfully")
	return nil
}
