package main

import (
	"fmt"
	"log"
	"os"

	"github.com/gin-gonic/gin"
	"github.com/joho/godotenv"
	"ticketing-backend/internal/config"
	"ticketing-backend/internal/database"
	"ticketing-backend/internal/handlers"
	"ticketing-backend/internal/middleware"
	"ticketing-backend/internal/services"
	"ticketing-backend/internal/utils"
)

func main() {
	// Load .env file if exists
	_ = godotenv.Load()

	cfg := config.Load()

	// Initialize JWT
	utils.InitJWT(cfg.JWTSecret)

	// Initialize database
	pool, err := database.GetPool(cfg.DatabaseURL)
	if err != nil {
		log.Fatalf("Failed to connect to database: %v", err)
	}
	defer database.Close()

	log.Println("Database connection established")

	// Initialize services
	authService := services.NewAuthService(pool)
	orderService := services.NewOrderService(pool)
	paymentService := services.NewPaymentService(pool, cfg.MidtransServerKey, cfg.MidtransClientKey, cfg.MidtransIsProduction, cfg.AppSettings)
	promoService := services.NewPromoService(pool)
	queueService := services.NewQueueService(pool, cfg.AppSettings)
	ticketService := services.NewTicketService(pool)
	emailService := services.NewEmailService(
		cfg.SMTPHost, cfg.SMTPPort, cfg.SMTPUser, cfg.SMTPPass,
		cfg.SMTPFromEmail, cfg.SMTPFromName,
	)
	_ = emailService // Available for future use

	// Initialize handlers (all receive AppSettings for file-based config)
	authHandler := handlers.NewAuthHandler(pool, authService, cfg.JWTSecret, cfg.AppSettings)
	eventHandler := handlers.NewEventHandler(pool)
	orderHandler := handlers.NewOrderHandler(pool, orderService, promoService)
	paymentHandler := handlers.NewPaymentHandler(pool, paymentService, orderService, ticketService)
	queueHandler := handlers.NewQueueHandler(pool, queueService)
	ticketHandler := handlers.NewTicketHandler(pool, ticketService)
	promoHandler := handlers.NewPromoHandler(pool, promoService)
	adminHandler := handlers.NewAdminHandler(pool, cfg.AppSettings)
	superAdminHandler := handlers.NewSuperAdminHandler(pool, cfg.AppSettings)
	uploadHandler := handlers.NewUploadHandler(pool, cfg.UploadDir)
	cronHandler := handlers.NewCronHandler(pool, orderService, queueService)
	walikelasHandler := handlers.NewWaliKelasHandler(pool)

	// Initialize Gin router
	r := gin.Default()
	r.SetTrustedProxies(nil)

	// Global middleware
	r.Use(middleware.CORSMiddleware(cfg.AllowedOrigins))

	// ===== Public Routes =====
	api := r.Group("/api")
	{
		// Auth
		auth := api.Group("/auth")
		{
			auth.POST("/login", authHandler.Login)
			auth.POST("/register", authHandler.Register)
			auth.POST("/logout", authHandler.Logout)
			auth.POST("/refresh", authHandler.Refresh)
			auth.POST("/forgot-password", authHandler.ForgotPassword)
			auth.POST("/reset-password", authHandler.ResetPassword)
			auth.GET("/verify-email", authHandler.VerifyEmail)
			auth.GET("/login-methods", authHandler.LoginMethods)
			auth.GET("/me", middleware.AuthMiddleware(cfg.JWTSecret), authHandler.Me)
			auth.GET("/google", authHandler.GoogleAuth)
			auth.GET("/google/callback", authHandler.GoogleCallback)
		}

		// Events (public)
		api.GET("/events", func(c *gin.Context) {
			token, _ := c.Cookie("accessToken")
			if token != "" {
				if claims, err := utils.DecodeJWT(token); err == nil {
					c.Set("authPayload", map[string]interface{}{
						"userId":  claims["userId"],
						"email":   claims["email"],
						"role":    claims["role"],
						"classId": claims["classId"],
					})
				}
			}
			eventHandler.ListEvents(c)
		})
		api.GET("/events/:eventId", eventHandler.GetEvent)
		api.GET("/events/:eventId/images", eventHandler.GetEventImages)

		// Visitor Orders (require auth)
		ordersAuth := api.Group("/orders", middleware.AuthMiddleware(cfg.JWTSecret))
		{
			ordersAuth.GET("/", eventHandler.ListUserOrders)
			ordersAuth.POST("/create-lock", orderHandler.CreateLock)
			ordersAuth.POST("/convert", orderHandler.ConvertLockToOrder)
			ordersAuth.GET("/:orderId", orderHandler.GetOrder)
			ordersAuth.POST("/:orderId/cancel", orderHandler.CancelOrder)
			ordersAuth.GET("/lock-info", orderHandler.GetLockInfo)
		}

		// Payments
		payments := api.Group("/payments")
		{
			payments.POST("/create", middleware.AuthMiddleware(cfg.JWTSecret), paymentHandler.CreatePayment)
			payments.GET("/status", middleware.AuthMiddleware(cfg.JWTSecret), paymentHandler.GetPaymentStatus)
			payments.POST("/webhook", paymentHandler.Webhook)
		}

		// Queue
		queue := api.Group("/queue", middleware.AuthMiddleware(cfg.JWTSecret))
		{
			queue.POST("/enter", queueHandler.Enter)
			queue.GET("/status", queueHandler.Status)
			queue.POST("/activate", queueHandler.Activate)
			queue.GET("/test", queueHandler.Test)
			queue.POST("/test/simulate", queueHandler.Simulate)
		}

		// Tickets
		tickets := api.Group("/tickets")
		{
			tickets.GET("/list", middleware.AuthMiddleware(cfg.JWTSecret), ticketHandler.ListTickets)
			tickets.POST("/generate", middleware.AuthMiddleware(cfg.JWTSecret), ticketHandler.GenerateTickets)
			tickets.GET("/validate", middleware.AuthMiddleware(cfg.JWTSecret), ticketHandler.ValidateTicket)
		}

		// Promos
		promos := api.Group("/promos")
		{
			promos.POST("/validate", middleware.AuthMiddleware(cfg.JWTSecret), promoHandler.ValidatePromo)
			promos.GET("/domain", middleware.AuthMiddleware(cfg.JWTSecret), promoHandler.CheckDomainPromo)
		}

		// Visitor checkout
		visitor := api.Group("/visitor", middleware.AuthMiddleware(cfg.JWTSecret))
		{
			visitor.GET("/checkout", orderHandler.GetOrder)
			visitor.POST("/checkout", orderHandler.ConvertLockToOrder)
		}

		// Upload
		api.POST("/upload", middleware.AuthMiddleware(cfg.JWTSecret), uploadHandler.Upload)
		api.GET("/uploads/*path", uploadHandler.ServeUpload)

		// ===== Admin Routes =====
		admin := api.Group("/admin", middleware.AuthMiddleware(cfg.JWTSecret), middleware.RoleMiddleware("admin_event", "super_admin"))
		{
			admin.GET("/events", adminHandler.ListEvents)
			admin.POST("/events", adminHandler.CreateEvent)
			admin.GET("/events/:eventId", adminHandler.GetEvent)
			admin.PUT("/events/:eventId", adminHandler.UpdateEvent)
			admin.POST("/events/:eventId/duplicate", adminHandler.DuplicateEvent)

			admin.GET("/events/:eventId/ticket-classes", adminHandler.ManageTicketClasses)
			admin.POST("/events/:eventId/ticket-classes", adminHandler.ManageTicketClasses)
			admin.PUT("/events/:eventId/ticket-classes/:ticketClassId", adminHandler.UpdateTicketClass)
			admin.DELETE("/events/:eventId/ticket-classes/:ticketClassId", adminHandler.DeleteTicketClass)

			admin.GET("/events/:eventId/locks", adminHandler.ListLocks)
			admin.POST("/events/:eventId/locks/:lockId/release", adminHandler.ReleaseLock)

			admin.GET("/orders", adminHandler.ListOrders)
			admin.GET("/orders/:orderId", adminHandler.GetOrder)

			admin.GET("/attendees", adminHandler.ListAllAttendees)
			admin.GET("/events/:eventId/attendees", adminHandler.ListAttendees)

			admin.GET("/tickets", adminHandler.ListTickets)
			admin.GET("/tickets/:id", ticketHandler.ValidateTicket)

			admin.GET("/payments", adminHandler.ListPayments)
			admin.POST("/payments/:paymentId/mark-paid", paymentHandler.MarkPaid)

			admin.GET("/promos", adminHandler.ListPromos)
			admin.POST("/promos", adminHandler.CreatePromo)
			admin.PUT("/promos/:promoId", adminHandler.UpdatePromo)
			admin.DELETE("/promos/:promoId", adminHandler.DeletePromo)

			admin.GET("/domain-promos", adminHandler.ListDomainPromos)
			admin.POST("/domain-promos", adminHandler.CreateDomainPromo)

			admin.GET("/users", adminHandler.ListUsers)
			admin.GET("/users/:userId", adminHandler.GetUser)

			admin.GET("/reports/download", adminHandler.GetReports)

			admin.GET("/settings", adminHandler.GetSettings)
			admin.PUT("/settings", adminHandler.UpdateSettings)
			admin.GET("/settings/env-status", superAdminHandler.GetEnvStatus)
			admin.GET("/settings/toggles", adminHandler.GetSettings)
			admin.PUT("/settings/toggles", adminHandler.UpdateSettings)
		}

		// ===== Super Admin Routes =====
		superAdmin := api.Group("/super-admin", middleware.AuthMiddleware(cfg.JWTSecret), middleware.RoleMiddleware("super_admin"))
		{
			superAdmin.GET("/dashboard-stats", superAdminHandler.DashboardStats)
			superAdmin.GET("/events", superAdminHandler.ListAllEvents)
			superAdmin.GET("/events/:eventId", adminHandler.GetEvent)
			superAdmin.PUT("/events/:eventId", adminHandler.UpdateEvent)
			superAdmin.GET("/orders", superAdminHandler.ListAllOrders)
			superAdmin.GET("/orders/:orderId", superAdminHandler.GetOrder)
			superAdmin.GET("/payments", superAdminHandler.ListAllPayments)
			superAdmin.POST("/payments/:paymentId/mark-paid", paymentHandler.MarkPaid)
			superAdmin.GET("/tickets", superAdminHandler.ListAllTickets)
			superAdmin.GET("/attendees", superAdminHandler.ListAllAttendees)
			superAdmin.GET("/users", superAdminHandler.ListAllUsers)
			superAdmin.POST("/users/bulk", superAdminHandler.BulkCreateUsers)
			superAdmin.GET("/users/:userId", superAdminHandler.GetUser)
			superAdmin.DELETE("/users/:userId", superAdminHandler.DeleteUser)
			superAdmin.POST("/users/import", superAdminHandler.ImportUsers)
			superAdmin.GET("/classes", superAdminHandler.ManageClasses)
			superAdmin.POST("/classes", superAdminHandler.ManageClasses)
			superAdmin.PUT("/classes/:classId", superAdminHandler.UpdateClass)
			superAdmin.GET("/logs", superAdminHandler.ListAllLogs)
			superAdmin.GET("/settings", adminHandler.GetSettings)
			superAdmin.PUT("/settings", adminHandler.UpdateSettings)
		}

		// ===== Wali Kelas Routes =====
		waliKelas := api.Group("/wali-kelas", middleware.AuthMiddleware(cfg.JWTSecret), middleware.RoleMiddleware("wali_kelas", "super_admin"))
		{
			waliKelas.GET("/classes", walikelasHandler.ListClasses)
			waliKelas.GET("/events", walikelasHandler.ListEvents)
			waliKelas.GET("/events/:eventId", walikelasHandler.GetEvent)
			waliKelas.GET("/students", walikelasHandler.ListStudents)
			waliKelas.GET("/orders", walikelasHandler.ListOrders)
			waliKelas.POST("/orders/import", walikelasHandler.ImportOrders)
			waliKelas.GET("/orders/import/template", walikelasHandler.DownloadTemplate)
		}

		// ===== Cron Routes =====
		cron := api.Group("/cron")
		{
			cron.POST("/cleanup-expired", cronHandler.CleanupExpired)
		}
	}

	// Health check
	r.GET("/health", func(c *gin.Context) {
		dbOk := database.HealthCheck()
		if !dbOk {
			c.JSON(503, gin.H{"status": "unhealthy", "database": "disconnected"})
			return
		}
		c.JSON(200, gin.H{"status": "healthy", "database": "connected"})
	})

	// Start server
	port := cfg.Port
	if envPort := os.Getenv("PORT"); envPort != "" {
		port = envPort
	}

	log.Printf("Server starting on port %s", port)
	if err := r.Run(fmt.Sprintf(":%s", port)); err != nil {
		log.Fatalf("Failed to start server: %v", err)
	}
}
