package bootstrap

import (
	"fmt"
	"time"
	"tm/pkg/authorization"
	"tm/pkg/config"
	"tm/pkg/logger"
	"tm/pkg/mongo"
	"tm/pkg/redis"

	"github.com/labstack/echo/v4"
	"github.com/labstack/echo/v4/middleware"
	echoSwagger "github.com/swaggo/echo-swagger"
)

// Init Application Configuration
func InitConfig() Config {
	conf, err := config.LoadConfig(".", &Config{})
	if err != nil {
		panic(fmt.Sprintf("Failed to load config: %v", err))
	}

	return *conf
}

// Init Logger Service
func InitLogger(conf config.LoggingConfig) logger.Logger {
	return logger.NewLogger(&logger.Config{
		Level:  conf.Level,
		Format: conf.Format,
		Output: conf.Output,
		File: logger.FileConfig{
			Path:       conf.File.Path,
			MaxSize:    conf.File.MaxSize,
			MaxBackups: conf.File.MaxBackups,
			MaxAge:     conf.File.MaxAge,
			Compress:   conf.File.Compress,
		},
	})
}

// Init MongoDB Connection Manager
func InitMongoDB(conf config.MongoConfig, log logger.Logger) *mongo.ConnectionManager {
	// Convert infra.MongoConfig to mongo.ConnectionConfig
	connectionConfig := mongo.ConnectionConfig{
		URI:                    conf.URI,
		Database:               conf.Name,
		MaxPoolSize:            uint64(conf.MaxPoolSize),
		MinPoolSize:            5,                // Default minimum pool size
		MaxConnIdleTime:        30 * time.Minute, // Default idle time
		ConnectTimeout:         conf.Timeout,
		ServerSelectionTimeout: 5 * time.Second, // Default server selection timeout
	}

	// Create MongoDB connection manager
	connectionManager, err := mongo.NewConnectionManager(connectionConfig, log)
	if err != nil {
		log.Error("Failed to initialize MongoDB connection", map[string]interface{}{
			"error": err.Error(),
			"uri":   conf.URI,
			"db":    conf.Name,
		})
		panic(fmt.Sprintf("Failed to initialize MongoDB connection: %v", err))
	}

	log.Info("MongoDB connection manager initialized successfully", map[string]interface{}{
		"database":  conf.Name,
		"uri":       conf.URI,
		"pool_size": conf.MaxPoolSize,
	})

	return connectionManager
}

// initHTTPServer initializes the Echo HTTP server with middleware
func InitHTTPServer(conf Config, log logger.Logger) *echo.Echo {
	e := echo.New()

	// Configure Echo
	// e.HideBanner = true
	// e.HidePort = true

	// Add middleware
	e.Use(middleware.Recover())
	e.Use(middleware.RequestID())
	e.Use(middleware.Logger())
	e.Use(middleware.CORSWithConfig(middleware.CORSConfig{
		AllowOrigins: []string{"*"},
		AllowMethods: []string{echo.GET, echo.POST, echo.PUT, echo.DELETE, echo.OPTIONS},
	}))

	// Add custom middleware for logging
	e.Use(func(next echo.HandlerFunc) echo.HandlerFunc {
		return func(c echo.Context) error {
			start := time.Now()

			// Process request
			err := next(c)

			// Log request details
			duration := time.Since(start)
			log.Info("HTTP request processed", map[string]interface{}{
				"method":     c.Request().Method,
				"path":       c.Request().URL.Path,
				"status":     c.Response().Status,
				"duration":   duration.String(),
				"user_agent": c.Request().UserAgent(),
				"remote_ip":  c.RealIP(),
			})

			return err
		}
	})

	// Add health check endpoint
	e.GET("/admin/v1/health", healthHandler)

	// Add Swagger documentation endpoint
	e.GET("/swagger/*", echoSwagger.WrapHandler)

	log.Info("HTTP server initialized successfully", map[string]interface{}{
		"middleware": []string{"recover", "request_id", "logger", "cors", "custom_logging"},
	})

	return e
}

// Init Redis Connection Manager
func InitRedis(conf config.RedisConfig, log logger.Logger) redis.Client {
	connectionConfig := &redis.Config{
		Host:     conf.Host,
		Port:     conf.Port,
		Password: conf.Password,
		DB:       conf.DB,
		PoolSize: conf.PoolSize,
	}

	redisClient, err := redis.NewClient(connectionConfig, log)
	if err != nil {
		log.Error("Failed to initialize Redis connection", map[string]interface{}{
			"error": err.Error(),
			"host":  conf.Host,
			"port":  conf.Port,
		})
		panic(fmt.Sprintf("Failed to initialize Redis connection: %v", err))
	}

	log.Info("Redis connection manager initialized successfully", map[string]interface{}{
		"host":      conf.Host,
		"port":      conf.Port,
		"pool_size": conf.PoolSize,
	})

	return redisClient
}

// Init Authorization Service
func InitAuthorizationService(conf JWTConfig, redisClient redis.Client, log logger.Logger) authorization.AuthorizationService {
	authConfig := &authorization.AuthorizationConfig{
		AccessTokenSecret:  conf.AccessSecret,
		RefreshTokenSecret: conf.RefreshSecret,
		AccessTokenExpiry:  time.Duration(conf.AccessExpiresIn) * time.Second,
		RefreshTokenExpiry: time.Duration(conf.RefreshExpiresIn) * time.Second,
		Issuer:             "tender-management-system",
		Audience:           "tender-management-users",
	}

	authService := authorization.NewAuthorizationService(authConfig, log, redisClient)

	log.Info("Authorization service initialized successfully", map[string]interface{}{
		"access_expires_in":  conf.AccessExpiresIn,
		"refresh_expires_in": conf.RefreshExpiresIn,
	})

	return authService
}
