package main

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

	"github.com/gin-gonic/gin"
	"go.uber.org/zap"

	"github.com/RyanCopley/skybridge/user/internal/config"
	"github.com/RyanCopley/skybridge/user/internal/database"
	"github.com/RyanCopley/skybridge/user/internal/handlers"
	"github.com/RyanCopley/skybridge/user/internal/middleware"
	"github.com/RyanCopley/skybridge/user/internal/repository/postgres"
	"github.com/RyanCopley/skybridge/user/internal/services"
)

func main() {
	// Initialize configuration
	cfg := config.NewConfig()
	if err := cfg.Validate(); err != nil {
		log.Fatal("Configuration validation failed:", err)
	}

	// Initialize logger
	logger := initLogger(cfg)
	defer logger.Sync()

	logger.Info("Starting User Management Service",
		zap.String("version", cfg.GetString("APP_VERSION")),
		zap.String("environment", cfg.GetString("APP_ENV")),
	)

	// Initialize database
	logger.Info("Connecting to database", 
		zap.String("dsn", cfg.GetDatabaseDSNForLogging()))
	
	db, err := database.NewPostgresProvider(
		cfg.GetDatabaseDSN(),
		cfg.GetInt("DB_MAX_OPEN_CONNS"),
		cfg.GetInt("DB_MAX_IDLE_CONNS"),
		cfg.GetString("DB_CONN_MAX_LIFETIME"),
	)
	if err != nil {
		logger.Fatal("Failed to initialize database", 
			zap.String("dsn", cfg.GetDatabaseDSNForLogging()),
			zap.Error(err))
	}
	
	logger.Info("Database connection established successfully")

	// Initialize repositories
	userRepo := postgres.NewUserRepository(db)
	profileRepo := postgres.NewUserProfileRepository(db)

	// Initialize services
	userService := services.NewUserService(userRepo, profileRepo, nil, logger)

	// Initialize handlers
	healthHandler := handlers.NewHealthHandler(db, logger)
	userHandler := handlers.NewUserHandler(userService, logger)

	// Set up router
	router := setupRouter(cfg, logger, healthHandler, userHandler)

	// Create HTTP server
	srv := &http.Server{
		Addr:         cfg.GetServerAddress(),
		Handler:      router,
		ReadTimeout:  cfg.GetDuration("SERVER_READ_TIMEOUT"),
		WriteTimeout: cfg.GetDuration("SERVER_WRITE_TIMEOUT"),
		IdleTimeout:  cfg.GetDuration("SERVER_IDLE_TIMEOUT"),
	}

	// Start server in goroutine
	go func() {
		logger.Info("Starting HTTP server", zap.String("address", srv.Addr))
		if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			logger.Fatal("Failed to start server", zap.Error(err))
		}
	}()

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

	logger.Info("Shutting down server...")

	// Give outstanding requests time to complete
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()

	// Shutdown server
	if err := srv.Shutdown(ctx); err != nil {
		logger.Error("Server forced to shutdown", zap.Error(err))
	}

	logger.Info("Server exited")
}

func initLogger(cfg config.ConfigProvider) *zap.Logger {
	var logger *zap.Logger
	var err error

	if cfg.IsProduction() {
		logger, err = zap.NewProduction()
	} else {
		logger, err = zap.NewDevelopment()
	}

	if err != nil {
		log.Fatal("Failed to initialize logger:", err)
	}

	return logger
}

func setupRouter(cfg config.ConfigProvider, logger *zap.Logger, healthHandler *handlers.HealthHandler, userHandler *handlers.UserHandler) *gin.Engine {
	// Set Gin mode based on environment
	if cfg.IsProduction() {
		gin.SetMode(gin.ReleaseMode)
	}

	router := gin.New()

	// Add middleware
	router.Use(middleware.Logger(logger))
	router.Use(middleware.Recovery(logger))
	router.Use(middleware.CORS())

	// Health check endpoints (no authentication required)
	router.GET("/health", healthHandler.Health)
	router.GET("/ready", healthHandler.Ready)

	// API routes
	api := router.Group("/api")
	{
		// Protected routes (require authentication)
		protected := api.Group("/")
		protected.Use(middleware.Authentication(cfg, logger))
		{
			// User management
			protected.GET("/users", userHandler.List)
			protected.POST("/users", userHandler.Create)
			protected.GET("/users/:id", userHandler.GetByID)
			protected.PUT("/users/:id", userHandler.Update)
			protected.DELETE("/users/:id", userHandler.Delete)
			
			// User lookup endpoints
			protected.GET("/users/email/:email", userHandler.GetByEmail)
			protected.GET("/users/exists/:email", userHandler.ExistsByEmail)

			// Documentation endpoint
			protected.GET("/docs", func(c *gin.Context) {
				c.JSON(http.StatusOK, gin.H{
					"service": "User Management Service",
					"version": cfg.GetString("APP_VERSION"),
					"documentation": "User service API endpoints",
					"endpoints": map[string]interface{}{
						"users": []string{
							"GET /api/users",
							"POST /api/users",
							"GET /api/users/:id",
							"PUT /api/users/:id",
							"DELETE /api/users/:id",
							"GET /api/users/email/:email",
							"GET /api/users/exists/:email",
						},
					},
				})
			})
		}
	}

	return router
}