package main

import (
	"context"
	"log"
	"net/http"
	"os"
	"os/signal"
	"strconv"
	"strings"
	"syscall"
	"time"

	"fotbal-club/internal/config"
	"fotbal-club/internal/controllers"
	"fotbal-club/internal/models"
	"fotbal-club/internal/routes"
	"fotbal-club/internal/services"
	db "fotbal-club/pkg/database"
	"fotbal-club/pkg/email"
	"fotbal-club/pkg/logger"

	"github.com/gin-contrib/gzip"
	"github.com/gin-gonic/gin"
	"github.com/prometheus/client_golang/prometheus/promhttp"
)

func main() {
	// Load configuration
	config.LoadConfig()

	// Set logger level based on DEBUG flag
	if config.AppConfig != nil && config.AppConfig.Debug {
		logger.SetLevel(logger.LevelDebug)
		log.Println("Logger level set to DEBUG")
	} else {
		logger.SetLevel(logger.LevelInfo)
	}

	// Initialize database
	dbInstance, err := db.InitDB()
	if err != nil {
		log.Fatalf("Failed to connect to database: %v", err)
	}

	// Check if we should run migrations
	runMigrations, _ := strconv.ParseBool(os.Getenv("RUN_MIGRATIONS"))
	if runMigrations {
		if err := db.MigrateDB(dbInstance); err != nil {
			log.Fatalf("Failed to run database migrations: %v", err)
		}
	}

	// Best-effort: ensure schemas exist and are up-to-date (adds new columns)
	if err := dbInstance.AutoMigrate(
		&models.Settings{},
		&models.Article{},
		&models.ScoreboardState{},
		&models.CompetitionAlias{},
		&models.Player{},
		&models.ContactCategory{},
		&models.Contact{},
		&models.ContactMessage{},
		&models.NewsletterSubscription{},
		&models.Sponsor{},
		&models.Clothing{},
		&models.Poll{},
		&models.PollOption{},
		&models.PollVote{},
		&models.NavigationItem{},
		&models.SocialLink{},
		&models.PageElementConfig{},
	); err != nil {
		log.Printf("Warning: AutoMigrate failed: %v", err)
	}
	var settings models.Settings
	_ = dbInstance.First(&settings).Error // ignore not found
	if config.AppConfig != nil {
		if settings.ID != 0 {
			config.AppConfig.NewsletterEnabled = settings.NewsletterEnabled
			log.Printf("[startup] NewsletterEnabled=%v (from settings)", settings.NewsletterEnabled)
		} else {
			log.Printf("[startup] NewsletterEnabled=%v (from env default)", config.AppConfig.NewsletterEnabled)
		}
	}

	// Check if we should seed the database
	seedDatabase, _ := strconv.ParseBool(os.Getenv("SEED_DATABASE"))
	if seedDatabase {
		log.Println("Seeding database...")
		if err := db.SeedDB(dbInstance); err != nil {
			log.Printf("Warning: Failed to seed database: %v", err)
		}
	}

	// Initialize Gin router
	r := gin.Default()
	
	// Set max multipart memory to match upload size limit (default is 32MB)
	r.MaxMultipartMemory = config.AppConfig.MaxUploadSize

	// Enable gzip compression for responses
	r.Use(gzip.Gzip(gzip.DefaultCompression))

	// Security headers & CORS
	r.Use(func(c *gin.Context) {
		// Security headers
		c.Writer.Header().Set("X-Content-Type-Options", "nosniff")
		c.Writer.Header().Set("X-Frame-Options", "SAMEORIGIN")
		c.Writer.Header().Set("Referrer-Policy", "no-referrer-when-downgrade")
		// Add HSTS when using HTTPS (including behind a proxy)
		if c.Request.TLS != nil || c.Request.Header.Get("X-Forwarded-Proto") == "https" {
			c.Writer.Header().Set("Strict-Transport-Security", "max-age=31536000; includeSubDomains; preload")
		}
		// Content Security Policy from configuration (override via CONTENT_SECURITY_POLICY)
		if config.AppConfig.ContentSecurityPolicy != "" {
			c.Writer.Header().Set("Content-Security-Policy", config.AppConfig.ContentSecurityPolicy)
		}

		// CORS: reflect the Origin only if it is allowed. In development, also allow localhost/127.0.0.1 any port.
		origin := c.Request.Header.Get("Origin")
		allowed := false
		for _, ao := range config.AppConfig.AllowedOrigins {
			if ao == origin {
				allowed = true
				break
			}
		}
		if !allowed && origin != "" && config.AppConfig.AppEnv != "production" {
			// Relaxed rule for local dev
			if strings.HasPrefix(origin, "http://localhost:") || strings.HasPrefix(origin, "http://127.0.0.1:") || strings.HasPrefix(origin, "https://localhost:") || strings.HasPrefix(origin, "https://127.0.0.1:") {
				allowed = true
			}
		}
		if allowed && origin != "" {
			c.Writer.Header().Set("Access-Control-Allow-Origin", origin)
			c.Writer.Header().Set("Vary", "Origin")
			c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
		}
		c.Writer.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS")
		c.Writer.Header().Set("Access-Control-Allow-Headers", "Origin, Content-Type, Accept, Authorization, Cache-Control, X-Requested-With")

		if c.Request.Method == "OPTIONS" {
			c.AbortWithStatus(204)
			return
		}

		c.Next()
	})

	// Setup API routes
	api := r.Group("/api/v1")

	// Setup other routes
	routes.SetupRoutes(api, dbInstance)
	routes.SetupRootRoutes(r, dbInstance)

	// Prometheus metrics endpoint
	r.GET("/metrics", gin.WrapH(promhttp.Handler()))

	// Create an email service instance for background jobs (uses DB-backed SMTP settings)
	emailSvc := email.NewEmailService(config.AppConfig, dbInstance)

	// Expose cached JSON files for the frontend at /cache/*
	r.Static("/cache", "./cache")

	// Serve static assets and uploads
	// Map /dist to ./static to expose files like /dist/img/logo-club-empty.svg
	r.Static("/dist", "./static")
	r.Static("/uploads", "./uploads")

	// Ensure gallery flat files exist at startup (best effort)
	_ = services.RegenerateFlatGalleryFiles()

	// Start background prefetcher (fetches public endpoints to cache JSON every 30 minutes)
	// It targets this server's own public API. Allow override via PREFETCH_TARGET (useful in container/proxy setups).
	prefetchTarget := os.Getenv("PREFETCH_TARGET")
	if prefetchTarget == "" {
		prefetchTarget = "http://127.0.0.1:" + config.AppConfig.Port + "/api/v1"
	}
	services.StartPrefetcher(prefetchTarget)

	// Start newsletter scheduler (automated emails - legacy weekly)
	services.StartNewsletterScheduler(dbInstance, emailSvc)

	// Start comprehensive newsletter automation (weekly, match alerts, blog notifications, results)
	newsletterAutomation := services.NewNewsletterAutomation(dbInstance, emailSvc)
	newsletterAutomation.Start()

	// Store newsletter automation instance for use in controllers
	controllers.SetNewsletterAutomation(newsletterAutomation)

	// Start server with graceful shutdown
	port := config.AppConfig.Port
	// Fail fast in production if JWT secret is left as default
	if config.AppConfig.AppEnv == "production" && config.AppConfig.JWTSecret == "default-secret-key-change-in-production" {
		log.Fatalf("SECURITY: JWT_SECRET is using the default value in production. Please set a strong secret.")
	}

	srv := &http.Server{
		Addr:    ":" + port,
		Handler: r,
	}

	// DB handle for closing on shutdown
	sqlDB, err := dbInstance.DB()
	if err != nil {
		log.Printf("Error getting database connection: %v", err)
	}

	// Start server in background
	go func() {
		log.Printf("Server starting on port %s\n", port)
		if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			log.Fatalf("Failed to start server: %v", err)
		}
	}()

	// Wait for interrupt signal to gracefully shutdown the server
	quit := make(chan os.Signal, 1)
	signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
	<-quit
	log.Println("Shutdown signal received, shutting down server...")

	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()
	if err := srv.Shutdown(ctx); err != nil {
		log.Printf("Server forced to shutdown: %v", err)
	}

	if sqlDB != nil {
		if err := sqlDB.Close(); err != nil {
			log.Printf("Error closing database connection: %v", err)
		}
	}
}
