package bootstrap

import (
	"fmt"
	"time"
	ai_summarizer "tm/pkg/ai_summarizer"
	"tm/pkg/authorization"
	"tm/pkg/config"
	"tm/pkg/filestore"
	"tm/pkg/gorules"
	"tm/pkg/hcaptcha"
	"tm/pkg/logger"
	"tm/pkg/mongo"
	"tm/pkg/notification"
	"tm/pkg/ollama"
	"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, echo.PATCH},
		AllowHeaders: []string{
			echo.HeaderOrigin,
			echo.HeaderContentType,
			echo.HeaderAccept,
			echo.HeaderAuthorization,
		},
	}))

	// 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
}

// Init Notification Service
func InitNotificationService(conf config.NotificationConfig, log logger.Logger) notification.SDK {
	notificationConfig := &notification.Config{
		BaseURL:       conf.BaseURL,
		Timeout:       conf.Timeout,
		RetryAttempts: conf.RetryAttempts,
		RetryDelay:    conf.RetryDelay,
		EnableLogging: conf.EnableLogging,
		UserAgent:     conf.UserAgent,
	}

	notificationSDK, err := notification.New(notificationConfig, log)
	if err != nil {
		log.Error("Failed to initialize Notification SDK", map[string]interface{}{
			"error": err.Error(),
		})
		fmt.Printf("Failed to initialize Notification SDK: %v\n", err.Error())
	}

	log.Info("Notification SDK initialized successfully", map[string]interface{}{
		"base_url":       conf.BaseURL,
		"timeout":        conf.Timeout,
		"retry_attempts": conf.RetryAttempts,
		"retry_delay":    conf.RetryDelay,
		"enable_logging": conf.EnableLogging,
		"user_agent":     conf.UserAgent,
	})

	return *notificationSDK
}

// Init Ollama Service
func InitOllamaService(conf config.OllamaConfig, log logger.Logger) ollama.SDK {
	ollamaConfig := &ollama.Config{
		BaseURL:            conf.BaseURL,
		Timeout:            conf.Timeout,
		RetryAttempts:      conf.RetryAttempts,
		RetryDelay:         conf.RetryDelay,
		EnableLogging:      conf.EnableLogging,
		UserAgent:          conf.UserAgent,
		DefaultModel:       conf.DefaultModel,
		DefaultTemperature: conf.DefaultTemperature,
		DefaultTopP:        conf.DefaultTopP,
		DefaultMaxTokens:   conf.DefaultMaxTokens,
		EnableStreaming:    conf.EnableStreaming,
	}

	ollamaSDK, err := ollama.New(ollamaConfig, log)
	if err != nil {
		log.Error("Failed to initialize Ollama SDK", map[string]interface{}{
			"error": err.Error(),
		})
	}

	log.Info("Ollama SDK initialized successfully", map[string]interface{}{
		"base_url":       conf.BaseURL,
		"timeout":        conf.Timeout,
		"retry_attempts": conf.RetryAttempts,
		"retry_delay":    conf.RetryDelay,
		"enable_logging": conf.EnableLogging,
		"user_agent":     conf.UserAgent,
	})

	return *ollamaSDK
}

// InitHCaptchaService initializes the hCaptcha verifier
func InitHCaptchaService(conf config.HCaptchaConfig, log logger.Logger) hcaptcha.Verifier {
	hcaptchaConfig := &hcaptcha.Config{
		SecretKey: conf.SecretKey,
		VerifyURL: conf.VerifyURL,
		Timeout:   conf.Timeout,
	}

	hcaptchaVerifier := hcaptcha.NewVerifier(hcaptchaConfig, log)

	log.Info("hCaptcha verifier initialized successfully", map[string]interface{}{
		"verify_url": conf.VerifyURL,
		"timeout":    conf.Timeout,
	})

	return hcaptchaVerifier
}

// InitFileStore initializes the GridFS file store service
func InitFileStore(mongoManager *mongo.ConnectionManager, log logger.Logger) (*filestore.GridFSService, error) {
	fileStoreService, err := filestore.NewGridFSService(mongoManager, log)
	if err != nil {
		log.Error("Failed to initialize file store service", map[string]interface{}{
			"error": err.Error(),
		})
		return nil, err
	}

	log.Info("File store service initialized successfully", map[string]interface{}{
		"storage_type": "MongoDB GridFS",
	})

	return fileStoreService, nil
}

// InitAISummarizerClient initializes the AI summarizer HTTP client for on-demand summarization
func InitAISummarizerClient(conf AISummarizerConfig, mongoManager *mongo.ConnectionManager, log logger.Logger) *ai_summarizer.Client {
	if conf.APIBaseURL == "" {
		log.Warn("AI Summarizer API base URL not configured, on-demand summarization will be unavailable", map[string]interface{}{})
		return nil
	}

	translationCounter := mongo.NewCounter(mongoManager)
	cfg := &ai_summarizer.Config{
		APIBaseURL:              conf.APIBaseURL,
		APITimeout:              conf.APITimeout,
		APIRetryCount:           conf.APIRetryCount,
		APIRetryDelay:           conf.APIRetryDelay,
		MinioEndpoint:           conf.MinioEndpoint,
		MinioAccessKey:          conf.MinioAccessKey,
		MinioSecretKey:          conf.MinioSecretKey,
		MinioUseSSL:             conf.MinioUseSSL,
		MinioRegion:             conf.MinioRegion,
		MinioBucket:             conf.MinioBucket,
		OnSuccessfulTranslation: mongo.AITranslationSuccessCallback(translationCounter),
	}

	client, err := ai_summarizer.NewClient(cfg, log)
	if err != nil {
		log.Error("Failed to initialize AI Summarizer client", map[string]interface{}{
			"error": err.Error(),
		})
		return nil
	}

	log.Info("AI Summarizer HTTP client initialized successfully", map[string]interface{}{
		"base_url": conf.APIBaseURL,
		"timeout":  conf.APITimeout,
	})

	return client
}

// InitAISummarizerStorage initializes the AI summarizer MinIO storage client
func InitAISummarizerStorage(conf AISummarizerConfig, log logger.Logger) *ai_summarizer.StorageClient {
	if conf.MinioEndpoint == "" {
		log.Warn("AI Summarizer MinIO endpoint not configured, storage-based summaries will be unavailable", map[string]interface{}{})
		return nil
	}

	cfg := &ai_summarizer.Config{
		APIBaseURL:     conf.APIBaseURL,
		APITimeout:     conf.APITimeout,
		APIRetryCount:  conf.APIRetryCount,
		APIRetryDelay:  conf.APIRetryDelay,
		MinioEndpoint:  conf.MinioEndpoint,
		MinioAccessKey: conf.MinioAccessKey,
		MinioSecretKey: conf.MinioSecretKey,
		MinioUseSSL:    conf.MinioUseSSL,
		MinioRegion:    conf.MinioRegion,
		MinioBucket:    conf.MinioBucket,
	}

	storage, err := ai_summarizer.NewStorageClient(cfg, log)
	if err != nil {
		log.Error("Failed to initialize AI Summarizer storage client", map[string]interface{}{
			"error": err.Error(),
		})
		return nil
	}

	log.Info("AI Summarizer storage client initialized successfully", map[string]interface{}{
		"endpoint": conf.MinioEndpoint,
		"bucket":   conf.MinioBucket,
	})

	return storage
}

// InitGoRulesClient initializes the GoRules SDK client.
func InitGoRulesClient(_ GoRulesConfig, log logger.Logger) gorules.Client {
	// GoRules SDK temporarily disabled — gorulessdk private repo unreachable from server.
	// Kanban falls back to built-in bid workflow columns when client is nil.
	log.Warn("GoRules client disabled; Kanban uses built-in bid workflow columns", map[string]interface{}{})
	return nil

	/*
		if conf.BaseURL == "" || conf.Token == "" || conf.RuleID == "" {
			log.Warn("GoRules client not configured, falling back to static Kanban statuses", map[string]interface{}{})
			return nil
		}

		timeout := conf.Timeout
		if timeout <= 0 {
			timeout = 10 * time.Second
		}

		client, err := gorules.New(gorules.Config{
			BaseURL: conf.BaseURL,
			Token:   conf.Token,
			Timeout: timeout,
		}, log)
		if err != nil {
			log.Error("Failed to initialize GoRules client", map[string]interface{}{
				"error": err.Error(),
			})
			return nil
		}

		log.Info("GoRules client initialized successfully", map[string]interface{}{
			"base_url": conf.BaseURL,
			"timeout":  timeout,
			"rule_id":  conf.RuleID,
		})

		return client
	*/
}
