package main

import (
	"context"
	"log"
	"os"

	"catalogue.midpdsa.com/src/controllers"
	"catalogue.midpdsa.com/src/db/catalogue"
	"catalogue.midpdsa.com/src/modules/auth"
	server "catalogue.midpdsa.com/src/server"
	"github.com/gin-gonic/gin"
	"github.com/jackc/pgx/v5/pgxpool"
)

func main() {
	ctx := context.Background()
	connStr := os.Getenv("MIP_DB")
	if connStr == "" {
		connStr = "postgres://user:password@localhost:5432/mydatabase?sslmode=disable"
	}

	pool, err := pgxpool.New(ctx, connStr)
	if err != nil {
		log.Fatalf("Unable to connect to database: %v\n", err)
	}
	defer pool.Close()

	if err := pool.Ping(ctx); err != nil {
		log.Fatalf("Unable to ping database: %v\n", err)
	}

	queries := catalogue.New(pool)
	authService := auth.NewAuthService(queries)

	seedAdminUser(ctx, authService)

	brakeBrandCtrl := controllers.NewBrakeBrandController(queries)
	brakeBrandSpecsCtrl := controllers.NewBrakeBrandSpecsController(queries)
	brakeStockCtrl := controllers.NewBrakeStockController(queries)
	shockBrandCtrl := controllers.NewShockBrandController(queries)
	shockBrandSpecsCtrl := controllers.NewShockBrandSpecsController(queries)
	shockStockCtrl := controllers.NewShockStockController(queries)
	vehicleBrandCtrl := controllers.NewVehicleBrandController(queries)
	vehicleTypeCtrl := controllers.NewVehicleTypeController(queries)
	vehicleModelYearCtrl := controllers.NewVehicleModelYearController(queries)
	extraTypeProductCharsCtrl := controllers.NewExtraTypeProductCaracteristicsController(queries)
	extraTypeBrandCtrl := controllers.NewExtraTypeBrandController(queries)
	extraTypeProductCtrl := controllers.NewExtraTypeProductController(queries)
	extraProductStockCtrl := controllers.NewExtraProductStockController(queries)
	diskBrandCtrl := controllers.NewDiskBrandController(queries)
	diskBrandSpecsCtrl := controllers.NewDiskBrandSpecsController(queries)
	diskStockCtrl := controllers.NewDiskStockController(queries)
	strutBrandCtrl := controllers.NewStrutBrandController(queries)
	strutBrandSpecsCtrl := controllers.NewStrutBrandSpecsController(queries)
	legBrandCtrl := controllers.NewLegBrandController(queries)
	legBrandSpecsCtrl := controllers.NewLegBrandSpecsController(queries)
	csvImportCtrl := controllers.NewCSVImportController(queries)
	authCtrl := controllers.NewAuthController(authService)
	searchCtrl := controllers.NewSearchController(queries)

	renderer, err := server.NewTemplateRenderer(pool, queries, "templates")
	if err != nil {
		log.Fatalf("Unable to load templates: %v\n", err)
	}

	r := gin.Default()
	r.Static("/assets", "./frontend/assets")

	r.Use(func(c *gin.Context) {
		c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
		c.Writer.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, OPTIONS")
		c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
		if c.Request.Method == "OPTIONS" {
			c.AbortWithStatus(204)
			return
		}
		c.Next()
	})
	r.GET("/health", func(c *gin.Context) {
		c.JSON(200, gin.H{"status": "ok"})
	})

	server.SetupProtectedBrakeBrandsRoutes(r, brakeBrandCtrl)
	server.SetupProtectedBrakeBrandSpecsRoutes(r, brakeBrandSpecsCtrl)
	server.SetupProtectedBrakeStocksRoutes(r, brakeStockCtrl)
	server.SetupProtectedShockBrandsRoutes(r, shockBrandCtrl)
	server.SetupProtectedShockBrandSpecsRoutes(r, shockBrandSpecsCtrl)
	server.SetupProtectedShockStocksRoutes(r, shockStockCtrl)
	server.SetupProtectedVehicleBrandsRoutes(r, vehicleBrandCtrl)
	server.SetupProtectedVehicleTypesRoutes(r, vehicleTypeCtrl)
	server.SetupProtectedVehicleModelYearsRoutes(r, vehicleModelYearCtrl)
	server.SetupProtectedExtraTypeProductCaracteristicsRoutes(r, extraTypeProductCharsCtrl)
	server.SetupProtectedExtraTypeBrandRoutes(r, extraTypeBrandCtrl)
	server.SetupProtectedExtraTypeProductRoutes(r, extraTypeProductCtrl)
	server.SetupProtectedExtraProductStocksRoutes(r, extraProductStockCtrl)
	server.SetupProtectedDiskBrandsRoutes(r, diskBrandCtrl)
	server.SetupProtectedDiskBrandSpecsRoutes(r, diskBrandSpecsCtrl)
	server.SetupProtectedDiskStocksRoutes(r, diskStockCtrl)
	server.SetupProtectedStrutBrandsRoutes(r, strutBrandCtrl)
	server.SetupProtectedStrutBrandSpecsRoutes(r, strutBrandSpecsCtrl)
	server.SetupProtectedLegBrandsRoutes(r, legBrandCtrl)
	server.SetupProtectedLegBrandSpecsRoutes(r, legBrandSpecsCtrl)
	server.SetupCSVImportRoutes(r, csvImportCtrl)
	server.SetupPanelRoutes(r, authCtrl, csvImportCtrl, queries)
	server.SetupPanelCRUDRoutes(
		r,
		shockBrandCtrl,
		brakeBrandCtrl,
		diskBrandCtrl,
		strutBrandCtrl,
		legBrandCtrl,
		brakeBrandSpecsCtrl,
		shockBrandSpecsCtrl,
		diskBrandSpecsCtrl,
		strutBrandSpecsCtrl,
		legBrandSpecsCtrl,
		brakeStockCtrl,
		shockStockCtrl,
		diskStockCtrl,
		vehicleBrandCtrl,
		vehicleTypeCtrl,
		vehicleModelYearCtrl,
		extraTypeProductCharsCtrl,
		extraTypeBrandCtrl,
		extraTypeProductCtrl,
		extraProductStockCtrl,
	)
	server.SetupUsersRoutes(r, authCtrl)
	server.SetupSearchRoutes(r, searchCtrl)
	server.SetupTemplateRoutes(r, renderer)

	port := os.Getenv("PORT")
	if port == "" {
		port = "8080"
	}

	log.Printf("Server starting on port %s", port)
	r.Run(":" + port)
}

func envOr(key, fallback string) string {
	if v := os.Getenv(key); v != "" {
		return v
	}
	return fallback
}

func seedAdminUser(ctx context.Context, authService *auth.AuthService) {
	email := envOr("ADMIN_EMAIL", "admin@midpdsa.com")
	password := envOr("ADMIN_PASSWORD", "wO4R£mZ64h6b")

	if err := authService.EnsureUser(ctx, email, password); err != nil {
		log.Printf("seed admin: failed to ensure user %q: %v", email, err)
		return
	}
	log.Printf("seed admin: ensured user %q exists", email)
}
