package main

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

	"payment/internal/config"
	"payment/internal/database"
	"payment/internal/handlers"
	"payment/internal/middleware"
	"payment/internal/services"
	"payment/internal/utils"

	"github.com/gin-gonic/gin"
	"github.com/go-redis/redis/v8"
	"github.com/sirupsen/logrus"
)

func main() {
	// Load configuration
	cfg := config.Load()

	// Setup logging
	setupLogging(cfg)

	logrus.Info("Starting Payment Service...")

	// Initialize database
	db, err := database.Initialize(cfg)
	if err != nil {
		logrus.Fatalf("Failed to initialize database: %v", err)
	}

	// Run migrations
	if err := database.Migrate(db); err != nil {
		logrus.Fatalf("Failed to run migrations: %v", err)
	}

	// Initialize Redis
	rdb := redis.NewClient(&redis.Options{
		Addr:     cfg.Redis.Addr,
		Password: cfg.Redis.Password,
		DB:       cfg.Redis.DB,
	})

	// Test Redis connection
	ctx := context.Background()
	if err := rdb.Ping(ctx).Err(); err != nil {
		logrus.Fatalf("Failed to connect to Redis: %v", err)
	}

	// Initialize services
	paymentService := services.NewPaymentService(db, rdb, cfg)

	// Initialize handlers
	paymentHandler := handlers.NewPaymentHandler(paymentService, db)
	healthHandler := handlers.NewHealthHandler(db, rdb)

	// Setup router
	router := setupRouter(cfg, paymentHandler, healthHandler)

	// Start server
	server := &utils.Server{
		Addr:    cfg.Server.Addr,
		Handler: router,
	}

	// Graceful shutdown
	go func() {
		if err := server.ListenAndServe(); err != nil && err != utils.ErrServerClosed {
			logrus.Fatalf("Failed to start server: %v", err)
		}
	}()

	logrus.Infof("Server started on %s", cfg.Server.Addr)

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

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

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

	if err := server.Shutdown(ctx); err != nil {
		logrus.Errorf("Server forced to shutdown: %v", err)
	}

	// Close database connection
	sqlDB, err := db.DB()
	if err == nil {
		if err := sqlDB.Close(); err != nil {
			logrus.Errorf("Failed to close database: %v", err)
		}
	}

	// Close Redis connection
	if err := rdb.Close(); err != nil {
		logrus.Errorf("Failed to close Redis: %v", err)
	}

	logrus.Info("Server exited")
}

func setupLogging(cfg *config.Config) {
	level, err := logrus.ParseLevel(cfg.Log.Level)
	if err != nil {
		level = logrus.InfoLevel
	}
	logrus.SetLevel(level)

	if cfg.Log.Format == "json" {
		logrus.SetFormatter(&logrus.JSONFormatter{})
	} else {
		logrus.SetFormatter(&logrus.TextFormatter{
			FullTimestamp: true,
		})
	}
}

func setupRouter(cfg *config.Config, paymentHandler *handlers.PaymentHandler, healthHandler *handlers.HealthHandler) *gin.Engine {
	gin.SetMode(cfg.Server.Mode)

	router := gin.New()

	// Middleware
	router.Use(gin.Recovery())
	router.Use(middleware.Logger())
	router.Use(middleware.CORS())
	router.Use(middleware.RateLimit(cfg.RateLimit))
	router.Use(middleware.Metrics())

	// Health check endpoint
	router.GET("/health", healthHandler.HealthCheck)
	router.GET("/metrics", healthHandler.Metrics)

	// API v1 routes
	v1 := router.Group("/api/v1")
	{
		// Payment routes
		payments := v1.Group("/payments")
		{
			// Public payment endpoints
			payments.POST("/initiate", paymentHandler.InitiatePayment)
			payments.POST("/status", paymentHandler.CheckPaymentStatus)
			payments.GET("/transactions", paymentHandler.GetTransactions)
			payments.GET("/transactions/:id", paymentHandler.GetTransaction)
		}

		// Callback routes (no auth required for M-Pesa callbacks)
		callbacks := v1.Group("/callbacks")
		{
			callbacks.POST("/mpesa", paymentHandler.MpesaCallback)
		}
	}

	return router
}
