package web

import (
	"context"
	"log"
	"os"
	"os/signal"
	"strconv"
	"syscall"
	"time"

	"git.ma-al.com/goc_daniel/b2b/app/config"
	"git.ma-al.com/goc_daniel/b2b/app/delivery/middleware"
	"git.ma-al.com/goc_daniel/b2b/app/delivery/web/api"
	"git.ma-al.com/goc_daniel/b2b/app/delivery/web/api/public"
	"git.ma-al.com/goc_daniel/b2b/app/delivery/web/api/restricted"
	"git.ma-al.com/goc_daniel/b2b/app/delivery/web/general"

	"github.com/gofiber/fiber/v3"
	"github.com/gofiber/fiber/v3/middleware/logger"
	"github.com/gofiber/fiber/v3/middleware/recover"
)

// Server represents the web server
type Server struct {
	app        *fiber.App
	cfg        *config.Config
	api        fiber.Router
	public     fiber.Router
	restricted fiber.Router
}

// App returns the fiber app
func (s *Server) App() *fiber.App {
	return s.app
}

// Cfg returns the config
func (s *Server) Cfg() *config.Config {
	return s.cfg
}

// New creates a new server instance
func New() *Server {
	return &Server{
		app: fiber.New(fiber.Config{
			ErrorHandler: customErrorHandler,
		}),
		cfg: config.Get(),
	}
}

// Setup configures the server with routes and middleware
func (s *Server) Setup() error {
	// Global middleware
	s.app.Use(recover.New())
	s.app.Use(logger.New())

	// CORS middleware
	s.app.Use(middleware.CORSMiddleware())

	// Language middleware - discovers client's language and stores in context
	s.app.Use(middleware.LanguageMiddleware())

	// initialize healthcheck
	general.InitHealth(s.App(), s.Cfg())

	// serve favicon
	general.Favicon(s.app, s.cfg)

	// initialize swagger endpoints
	general.InitSwagger(s.App())

	// API routes
	s.api = s.app.Group("/api/v1")
	s.public = s.api.Group("/public")
	s.restricted = s.api.Group("/restricted")
	s.restricted.Use(middleware.AuthMiddleware())

	// initialize language endpoints (general)
	api.NewLangHandler().InitLanguage(s.api, s.cfg)

	// Settings endpoint (general)
	api.NewSettingsHandler().InitSettings(s.api, s.cfg)

	// Auth routes (public)
	auth := s.public.Group("/auth")
	public.AuthHandlerRoutes(auth)

	menuRouting := s.public.Group("/menu")
	public.RoutingHandlerRoutes(menuRouting)

	// product translation routes (restricted)
	productTranslation := s.restricted.Group("/product-translation")
	restricted.ProductTranslationHandlerRoutes(productTranslation)

	// lists of things routes (restricted)
	list := s.restricted.Group("/list")
	restricted.ListHandlerRoutes(list)

	// locale selector (restricted)
	// this is basically for changing user's selected language and country
	localeSelector := s.restricted.Group("/langs-and-countries")
	restricted.LocaleSelectorHandlerRoutes(localeSelector)

	// menu (restricted)
	menu := s.restricted.Group("/menu")
	restricted.MenuHandlerRoutes(menu)

	// meili search (restricted)
	meiliSearch := s.restricted.Group("/search")
	restricted.MeiliSearchHandlerRoutes(meiliSearch)

	// carts (restricted)
	carts := s.restricted.Group("/carts")
	restricted.CartsHandlerRoutes(carts)

	s.api.All("*", func(c fiber.Ctx) error {
		return c.SendStatus(fiber.StatusNotFound)
	})

	// // Restricted routes example
	// restricted := s.api.Group("/restricted")
	// restricted.Use(middleware.AuthMiddleware())
	// restricted.Get("/dashboard", func(c fiber.Ctx) error {
	// 	user := middleware.GetUser(c)
	// 	return c.JSON(fiber.Map{
	// 		"message": "Welcome to the protected area",
	// 		"user":    user,
	// 	})
	// })

	// // Admin routes example
	// admin := s.api.Group("/admin")
	// admin.Use(middleware.AuthMiddleware())
	// admin.Use(middleware.RequireAdmin())
	// admin.Get("/users", func(c fiber.Ctx) error {
	// 	return c.JSON(fiber.Map{
	// 		"message": "Admin area - user management",
	// 	})
	// })

	//  keep this at the end because its wilderange
	general.InitBo(s.App())

	return nil
}

// Run starts the server
func (s *Server) Run() error {
	// Run database migrations
	// if err := db.RunMigrations(); err != nil {
	// 	log.Printf("⚠️ Database migrations failed: %v", err)
	// } else {
	// 	log.Println("✓ Database migrations completed")
	// }

	// // Seed admin user
	// if err := db.SeedAdminUser("admin@example.com", "admin123"); err != nil {
	// 	log.Printf("⚠️ Admin user seeding failed: %v", err)
	// }

	addr := s.cfg.Server.Host + ":" + strconv.Itoa(s.cfg.Server.Port)
	log.Printf("Starting server on %s", addr)
	log.Printf("Swagger UI available at http://%s/swagger/index.html", addr)
	log.Printf("OpenAPI JSON available at http://%s/openapi.json", addr)

	go func() {
		if err := s.app.Listen(":3000"); err != nil {
			log.Println("Server stopped:", err)
		}
	}()

	// Wait for shutdown signal
	quit := make(chan os.Signal, 1)

	signal.Notify(
		quit,
		syscall.SIGINT,  // Ctrl+C
		syscall.SIGTERM, // docker stop
	)

	<-quit

	log.Println("Shutting down server...")

	// graceful shutdown with timeout
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()

	if err := s.app.ShutdownWithContext(ctx); err != nil {
		log.Fatal("Shutdown error:", err)
	}

	log.Println("Server exited cleanly")
	return nil
}

// customErrorHandler handles errors
func customErrorHandler(c fiber.Ctx, err error) error {
	code := fiber.StatusInternalServerError

	if e, ok := err.(*fiber.Error); ok {
		code = e.Code
	}

	return c.Status(code).JSON(fiber.Map{
		"error": err.Error(),
	})
}
