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/config"
	"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/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
	dbManager dbmanager.DbManager

	// Services
	authService auth.Service
	tokenStore  auth.TokenStore

	// Modules
	authHandler  *handlers.AuthHandler
	userModule   *UserModule
	clientModule *ClientModule

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

type UserModule struct {
	Store   *dbstore.UserStore
	Service *services.UserService
	Handler *handlers.UserHandler
}

type ClientModule struct {
	Store   *dbstore.ClientStore
	Service *services.ClientService
	Handler *handlers.ClientHandler
}

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 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
}

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 on port " + strconv.Itoa(a.cnf.Server.Port))
		// 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
}
