package webserver

import (
	"context"
	"flag"
	"fmt"
	"narasi-ahli-be/app/database"
	"narasi-ahli-be/app/database/seeds"
	md "narasi-ahli-be/app/middleware"
	articlesService "narasi-ahli-be/app/module/articles/service"
	"narasi-ahli-be/app/router"
	"narasi-ahli-be/config/config"
	"narasi-ahli-be/utils/response"
	"os"
	"runtime"
	"strings"
	"time"

	"github.com/go-co-op/gocron"
	"github.com/gofiber/fiber/v2"
	"github.com/gofiber/fiber/v2/middleware/recover"
	futils "github.com/gofiber/fiber/v2/utils"
	"github.com/rs/zerolog"
	"go.uber.org/fx"
)

// NewFiber : initialize the webserver
func NewFiber(cfg *config.Config) *fiber.App {
	// setup
	app := fiber.New(fiber.Config{
		ServerHeader:          cfg.App.Name,
		AppName:               cfg.App.Name,
		Prefork:               cfg.App.Prefork,
		ErrorHandler:          response.ErrorHandler,
		IdleTimeout:           cfg.App.IdleTimeout * time.Second,
		EnablePrintRoutes:     cfg.App.PrintRoutes,
		BodyLimit:             cfg.App.BodyLimit,
		DisableStartupMessage: true,
		ReadBufferSize:        8192,
	})
	
	// ==============================
	  app.Use(recover.New(recover.Config{
        EnableStackTrace: true,
    }))

	// pass production config to check it
	response.IsProduction = cfg.App.Production

	return app
}

// Start : function to start webserver
func Start(lifecycle fx.Lifecycle, cfg *config.Config, fiber *fiber.App, router *router.Router, middlewares *md.Middleware, db *database.Database, log zerolog.Logger) {
	lifecycle.Append(
		fx.Hook{
			OnStart: func(ctx context.Context) error {

				// Connect database
				db.ConnectDatabase()

				// Register middlewares
				middlewares.Register(db)

				// Register routes
				router.Register()

				// Custom Startup Messages
				host, port := config.ParseAddress(cfg.App.Port)
				if host == "" {
					if fiber.Config().Network == "tcp6" {
						host = "[::1]"
					} else {
						host = "0.0.0.0"
					}
				}

				// ASCII Art
				ascii, err := os.ReadFile("./storage/ascii_art.txt")
				if err != nil {
					log.Debug().Err(err).Msg("An unknown error occurred when to print ASCII art!")
				}

				for _, line := range strings.Split(futils.UnsafeString(ascii), "\n") {
					log.Info().Msg(line)
				}

				// Information message
				log.Info().Msg(fiber.Config().AppName + " is running at the moment!")

				// Debug informations
				if !cfg.App.Production {
					prefork := "Enabled"
					procs := runtime.GOMAXPROCS(0)
					if !cfg.App.Prefork {
						procs = 1
						prefork = "Disabled"
					}

					log.Debug().Msgf("Version: %s", "-")
					log.Debug().Msgf("Host: %s", host)
					log.Debug().Msgf("Port: %s", port)
					log.Debug().Msgf("Prefork: %s", prefork)
					log.Debug().Msgf("Handlers: %d", fiber.HandlersCount())
					log.Debug().Msgf("Processes: %d", procs)
					log.Debug().Msgf("PID: %d", os.Getpid())
				}

				// Listen the app (with TLS Support)
				//if cfg.App.TLS.Enable {
				//	log.Debug().Msg("TLS support was enabled.")
				//
				//	if err := fiber.ListenTLS(cfg.App.Port, cfg.App.TLS.CertFile, cfg.App.TLS.KeyFile); err != nil {
				//		log.Error().Err(err).Msg("An unknown error occurred when to run server!")
				//	}
				//}

				go func() {
					if err := fiber.Listen(cfg.App.Port); err != nil {
        				log.Fatal().Err(err).Msg("Fiber failed to start")
					}
				}()

				migrateFlag := flag.Bool("migrate", db.Cfg.DB.Postgres.Migrate, "migrate the database")
				seedFlag := flag.Bool("seed", db.Cfg.DB.Postgres.Seed, "seed the database")
				flag.Parse()

				// read flag -migrate to migrate the database
				if *migrateFlag {
					db.MigrateModels()
				}
				// read flag -seed to seed the database
				if *seedFlag {

					// init seed models
					masterStatusSeeder := seeds.MasterStatusesSeeder{}
					masterApprovalStatusSeeder := seeds.MasterApprovalStatusesSeeder{}
					activityLogsSeeder := seeds.ActivityLogsSeeder{}
					allSeeders := []database.Seeder{masterStatusSeeder, masterApprovalStatusSeeder, activityLogsSeeder}

					db.SeedModels(allSeeders)
				}

				return nil
			},
			OnStop: func(ctx context.Context) error {
				log.Info().Msg("Shutting down the app...")
				if err := fiber.Shutdown(); err != nil {
					log.Panic().Err(err).Msg("")
				}

				log.Info().Msg("Running cleanup tasks...")
				log.Info().Msg("1- Shutdown the database")
				db.ShutdownDatabase()
				log.Info().Msgf("%s was successful shutdown.", cfg.App.Name)
				log.Info().Msg("\u001b[96msee you again👋\u001b[0m")

				return nil
			},
		},
	)
}

func RunScheduling(lifecycle fx.Lifecycle, articleService articlesService.ArticlesService) *gocron.Scheduler {
	scheduler := gocron.NewScheduler(time.UTC)

	// Tambahkan tugas yang berjalan setiap 10 detik
	// scheduler.Every(1).Minutes().SingletonMode().Do(func() {
	// 	fmt.Println("Run Scheduler", time.Now())
	// 	err := articleService.ExecuteScheduling()
	// 	if err != nil {
	// 		fmt.Println("Scheduler Got An Error", time.Now())
	// 	}
	// })

	// Menambahkan lifecycle hooks di Fx
	lifecycle.Append(fx.Hook{
		OnStart: func(ctx context.Context) error {
			fmt.Println("Memulai scheduler...")
			scheduler.StartAsync() // Mulai scheduler saat aplikasi dimulai
			return nil
		},
		OnStop: func(ctx context.Context) error {
			fmt.Println("Menghentikan scheduler...")
			scheduler.Stop() // Hentikan scheduler saat aplikasi dihentikan
			return nil
		},
	})

	return scheduler
}
