package database

import (
	"context"
	"log"
	"time"

	"fotbal-club/internal/config"
	"fotbal-club/internal/models"

	"golang.org/x/crypto/bcrypt"
	"gorm.io/driver/postgres"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
)

var (
	// DB is the global database connection pool
	DB *gorm.DB

	// dbLogger is the logger instance for database operations
	dbLogger = logger.New(
		log.New(log.Writer(), "\r\n", log.LstdFlags),
		logger.Config{
			SlowThreshold:             time.Second,
			LogLevel:                  logger.Warn,
			Colorful:                  true,
			IgnoreRecordNotFoundError: true,
		},
	)
)

// InitDB initializes the database connection with connection pooling
func InitDB() (*gorm.DB, error) {
	var err error

	// Set up database connection with connection pooling
	DB, err = gorm.Open(postgres.Open(config.AppConfig.DatabaseURL), &gorm.Config{
		Logger:                                   dbLogger,
		PrepareStmt:                              true,
		DisableForeignKeyConstraintWhenMigrating: true,
	})

	if err != nil {
		return nil, err
	}

	// Get the underlying sql.DB instance to configure connection pool
	sqlDB, err := DB.DB()
	if err != nil {
		return nil, err
	}

	// Set connection pool parameters
	sqlDB.SetMaxIdleConns(config.AppConfig.MaxIdleConnections)
	sqlDB.SetMaxOpenConns(config.AppConfig.MaxOpenConnections)
	sqlDB.SetConnMaxLifetime(config.AppConfig.ConnMaxLifetime)

	log.Println("Database connection established with connection pooling")
	return DB, nil
}

// GetDB returns the database instance
func GetDB() *gorm.DB {
	return DB
}

// CloseDB closes the database connection
func CloseDB() error {
	sqlDB, err := DB.DB()
	if err != nil {
		log.Printf("Error getting database instance: %v", err)
		return err
	}

	// Close the database connection
	if err := sqlDB.Close(); err != nil {
		log.Printf("Error closing database connection: %v", err)
		return err
	}

	log.Println("Database connection closed")
	return nil
}

// WithTransaction executes a function within a database transaction
func WithTransaction(ctx context.Context, fn func(tx *gorm.DB) error) error {
	tx := DB.Begin()
	if tx.Error != nil {
		return tx.Error
	}

	defer func() {
		if r := recover(); r != nil {
			tx.Rollback()
			panic(r) // re-throw panic after Rollback
		}
	}()

	if err := fn(tx); err != nil {
		if rbErr := tx.Rollback().Error; rbErr != nil {
			return rbErr
		}
		return err
	}

	return tx.Commit().Error
}

// MigrateDB runs database migrations for all models
func MigrateDB(db *gorm.DB) error {
	// Enable UUID extension
	err := db.Exec(`CREATE EXTENSION IF NOT EXISTS "uuid-ossp";`).Error
	if err != nil {
		return err
	}

	// Run migrations for all models
	return db.AutoMigrate(
		&models.User{},
		&models.Article{},
		&models.Category{},
		&models.Team{},
		&models.Player{},
		&models.Sponsor{},
		&models.Settings{},
		&models.MatchOverride{},
		&models.TeamLogoOverride{},
		&models.ContactMessage{},
		&models.ContactCategory{},
		&models.Contact{},
		&models.NewsletterSubscription{},
		&models.VisitorEvent{},
		&models.ArticleTeamLink{},
		&models.ArticleMatchLink{},
		&models.CompetitionAlias{},
		&models.EmailLog{},
		&models.EmailEvent{},
		&models.PasswordReset{},
		&models.AboutPage{},
		// Add event tables so public endpoints don't fail before any writes occur
		&models.Event{},
		&models.EventAttachment{},
		&models.UploadedFile{},
		&models.FileUsage{},
	)
}

// SeedDB populates the database with initial data
func SeedDB(db *gorm.DB) error {
	// Check if we already have data
	var count int64
	if err := db.Model(&models.User{}).Count(&count).Error; err != nil {
		return err
	}

	// If we already have data, don't seed
	if count > 0 {
		log.Println("Database already seeded, skipping...")
		return nil
	}

	// Create admin user
	hashedPassword, err := bcrypt.GenerateFromPassword([]byte("admin123"), bcrypt.DefaultCost)
	if err != nil {
		return err
	}

	adminUser := models.User{
		Email:     "admin@example.com",
		Password:  string(hashedPassword),
		FirstName: "Admin",
		LastName:  "User",
		Role:      "admin",
	}

	if err := db.Create(&adminUser).Error; err != nil {
		return err
	}

	log.Println("Database seeded successfully with admin user")
	log.Printf("Admin credentials: admin@example.com / admin123")
	return nil
}

// HealthCheck performs a simple database query to check if the connection is alive
func HealthCheck() error {
	sqlDB, err := DB.DB()
	if err != nil {
		return err
	}

	return sqlDB.Ping()
}
