package app

import (
	"context"
	"fmt"
	"net/http"
	"sync"
	"time"

	"git.linuxforward.com/byop/byop-engine/auth"
	"git.linuxforward.com/byop/byop-engine/clients"
	"git.linuxforward.com/byop/byop-engine/config"
	"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/services"
	"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
	database *dbstore.SQLiteStore

	// Services
	authService auth.Service
	tokenStore  auth.TokenStore

	// Clients
	buildkitClient clients.BuildMachineClient // BuildKit client for build operations
	registryClient clients.RegistryClient     // Docker registry client for pushing images

	// Handlers
	authHandler       *handlers.AuthHandler
	userHandler       *handlers.UserHandler
	clientHandler     *handlers.ClientHandler
	componentHandler  *handlers.ComponentHandler // Renamed from appHandler
	appHandler        *handlers.AppsHandler      // Renamed from templateHandler
	deploymentHandler *handlers.DeploymentHandler
	previewHandler    *handlers.PreviewHandler

	// Preview Service
	previewService services.PreviewService
	builderService *services.Builder
	appImporter    *services.AppImporter

	// Resource Handlers
	providerHandler *handlers.ProviderHandler
	// ticketHandler     *handlers.TicketHandler
	stopped bool
	wg      sync.WaitGroup
	// 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.ReleaseMode)
		// gin.SetMode(gin.DebugMode)
		logrus.SetLevel(logrus.DebugLevel)
	} 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()

	// Disable automatic redirection of trailing slashes
	// This prevents 301 redirects that can cause CORS issues
	app.rtr.RedirectTrailingSlash = false
	app.rtr.RedirectFixedPath = false

	app.rtr.Use(gin.Recovery())
	// app.rtr.Use(mw.Logger)
	// Add CORS middleware to handle cross-origin requests
	app.rtr.Use(mw.CORS())

	// Initialize clients
	app.buildkitClient = clients.NewDockerfileBuilder(cnf.BuildkitHost)
	app.entry.Info("Dockerfile builder client initialized")
	app.registryClient = clients.NewSimpleRegistryClient(app.buildkitClient)
	app.entry.Info("Registry client initialized")

	// Initialize services and handlers
	if err := app.initCommonServices(); 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
}

// Shutdown gracefully shuts down the application and its resources.
func (a *App) Shutdown(ctx context.Context) error {
	a.entry.Info("Initiating graceful shutdown...")

	// Signal all goroutines to stop
	a.cancelFunc() // This will close a.ctx

	// Close services
	if a.previewService != nil {
		a.entry.Info("Closing preview service...")
		a.previewService.Close(ctx) // Assuming Close() is idempotent or handles multiple calls gracefully
		a.entry.Info("Preview service closed.")
	}

	if a.tokenStore != nil {
		if closer, ok := a.tokenStore.(interface{ Close() error }); ok {
			a.entry.Info("Closing token store...")
			if err := closer.Close(); err != nil {
				a.entry.WithError(err).Error("Failed to close token store")
				// Potentially return this error or aggregate errors
			} else {
				a.entry.Info("Token store closed.")
			}
		}
	}

	// Close database connection
	if a.database != nil {
		a.entry.Info("Closing database connection...")
		if err := a.database.Close(); err != nil {
			a.entry.WithError(err).Error("Failed to close database connection")
			// Potentially return this error or aggregate errors
		} else {
			a.entry.Info("Database connection closed.")
		}
	}

	// Wait for any other background goroutines managed by the app to finish
	// a.wg.Wait() // Uncomment if you use sync.WaitGroup for other app-managed goroutines

	a.entry.Info("Graceful shutdown completed.")
	return nil
}

func (a *App) Run() error {

	// The main HTTP server instance for the application.
	// This will be shut down by the logic in main.go.
	srv := &http.Server{
		Addr:    fmt.Sprintf(":%d", a.cnf.Server.Port),
		Handler: a.rtr,
	}

	// This goroutine will block until the server is shut down or an error occurs.
	// The actual shutdown signal is handled in main.go.
	go func() {
		select {
		case <-a.ctx.Done(): // Listen for the app context cancellation
			a.entry.Info("App context cancelled, initiating server shutdown from Run()...")
			// Context for server shutdown, can be different from app context if needed
			shutdownCtx, cancelShutdown := context.WithTimeout(context.Background(), 15*time.Second)
			defer cancelShutdown()
			if err := srv.Shutdown(shutdownCtx); err != nil {
				a.entry.WithError(err).Error("HTTP server shutdown error in Run() after context cancellation")
			}
			return
		}
	}()

	a.entry.WithField("address", srv.Addr).Infof("Starting server on port %d", a.cnf.Server.Port)
	var err error
	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)
	} else {
		a.entry.Info("Starting server without TLS...")
		err = srv.ListenAndServe()
	}

	if err != nil && err != http.ErrServerClosed {
		a.entry.WithError(err).Errorf("Server ListenAndServe error")
		return fmt.Errorf("failed to start server: %w", err)
	}

	a.entry.Info("Server Run() method finished.")
	return nil // http.ErrServerClosed is a normal error on shutdown
}
