package server

import (
	"fmt"
	"log/slog"
	"os"
	"os/signal"
	"syscall"
	"time"

	"github.com/ditatompel/xmr-remote-nodes/frontend"
	"github.com/ditatompel/xmr-remote-nodes/internal/config"
	"github.com/ditatompel/xmr-remote-nodes/internal/cron"
	"github.com/ditatompel/xmr-remote-nodes/internal/database"
	"github.com/ditatompel/xmr-remote-nodes/internal/handler"

	"github.com/gofiber/fiber/v2"
	"github.com/gofiber/fiber/v2/middleware/cors"
	"github.com/gofiber/fiber/v2/middleware/filesystem"
	"github.com/gofiber/fiber/v2/middleware/logger"
	"github.com/gofiber/fiber/v2/middleware/recover"
	"github.com/spf13/cobra"
)

var serveCmd = &cobra.Command{
	Use:   "serve",
	Short: "Serve the WebUI and APIs",
	Long:  `This command will run HTTP server for APIs and WebUI.`,
	Run: func(_ *cobra.Command, _ []string) {
		serve()
	},
}

func serve() {
	appCfg := config.AppCfg()
	if err := database.ConnectDB(); err != nil {
		slog.Error(fmt.Sprintf("[DB] %s", err.Error()))
		os.Exit(1)
	}

	// signal channel to capture system calls
	sigCh := make(chan os.Signal, 1)
	signal.Notify(sigCh, syscall.SIGTERM, syscall.SIGINT, syscall.SIGQUIT)

	stopCron := make(chan struct{})
	if !fiber.IsChild() {
		// run db migrations
		if err := database.MigrateDb(database.GetDB()); err != nil {
			slog.Error(fmt.Sprintf("[DB] %s", err.Error()))
			os.Exit(1)
		}

		// run cron process
		cronRepo := cron.New()
		go cronRepo.RunCronProcess(stopCron)
	}

	// Define Fiber config & app.
	app := fiber.New(fiber.Config{
		Prefork:     appCfg.Prefork,
		ProxyHeader: appCfg.ProxyHeader,
		AppName:     "XMR Nodes Aggregator",
	})

	// recover
	app.Use(recover.New(recover.Config{EnableStackTrace: true}))

	// logger middleware
	if appCfg.LogLevel == "DEBUG" {
		app.Use(logger.New(logger.Config{
			Format:     "${time} DEBUG [HTTP] ${status} - ${latency} ${method} ${path} ${queryParams} ${ip} ${ua}\n",
			TimeFormat: "2006/01/02 15:04:05",
		}))
	}

	app.Use(cors.New(cors.Config{
		AllowOrigins:     appCfg.AllowOrigin,
		AllowHeaders:     "Origin, Content-Type, Accept",
		AllowCredentials: true,
	}))

	handler.V1Api(app)
	app.Use("/", filesystem.New(filesystem.Config{
		Root: frontend.SvelteKitHandler(),
		// NotFoundFile: "index.html",
	}))

	// go routine to capture system calls
	go func() {
		<-sigCh
		close(stopCron) // stop cron goroutine
		slog.Info("Shutting down HTTP server...")
		_ = app.Shutdown()

		// give time for graceful shutdown
		time.Sleep(1 * time.Second)
	}()

	// start http server
	serverAddr := fmt.Sprintf("%s:%d", appCfg.Host, appCfg.Port)
	if err := app.Listen(serverAddr); err != nil {
		slog.Error(fmt.Sprintf("[HTTP] Server is not running! error: %v", err))
	}
}
