package database

import (
	"log"
	"time"

	"gauth-central/config"
	"gauth-central/internal/models"

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

var DB *gorm.DB

func ConnectDB() {
	dsn := config.AppConfig.DBUrl
	if dsn == "" {
		log.Fatal("DB_URL is not set in .env")
	}

	// Configure GORM with optimized settings
	db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{
		Logger:      logger.Default.LogMode(logger.Error), // Only log errors, suppress SLOW SQL warnings
		PrepareStmt: true,                                 // Prepare statements for better performance
		NowFunc: func() time.Time {
			return time.Now().UTC()
		},
	})
	if err != nil {
		log.Fatal("Failed to connect to database:", err)
	}

	log.Println("Connected to Database successfully")
	DB = db

	// Enable UUID extension
	enableUUIDExtension()
}

func enableUUIDExtension() {
	// Enable uuid-ossp extension for uuid_generate_v4()
	err := DB.Exec("CREATE EXTENSION IF NOT EXISTS \"uuid-ossp\"").Error
	if err != nil {
		log.Printf("Warning: Could not enable uuid-ossp extension: %v", err)
	} else {
		log.Println("UUID extension enabled")
	}
}

func Migrate() {
	// Manual migration for user_name column to handle existing data
	migrateUserNameColumn()

	err := DB.AutoMigrate(
		&models.User{},
		&models.SocialAccount{},
		&models.Role{},
		&models.Permission{},
		&models.CorsWhitelist{},
		&models.CorsBlacklist{},
		&models.RateLimitSetting{},
		&models.Contact{},
		&models.Tag{}, // Added Tag model
		&models.PostCategory{},
		&models.PostTag{},
		&models.Post{},
		&models.PostComment{},
		&models.PostCategoryView{},
		&models.Home{},
		&models.About{},
		&models.Service{},
		&models.ServiceTitle{},
		&models.Resume{},
		&models.Education{},
		&models.Experience{},
		&models.Skill{},
		&models.Knowledge{},
		&models.MainMenu{},
		&models.Setting{},
		&models.Banner{},
		&models.SiteSettings{},
	)
	if err != nil {
		log.Fatal("Database Migration Failed:", err)
	}
	log.Println("Database Migration Completed")

	// Migration for email_verified column is disabled after initial setup
	// New users will have email_verified=false by default for email/password registration
	// migrateEmailVerifiedColumn()

	seedRolesAndPermissions()
	seedDefaultSettings()
	// seedDefaultAdmin() - Removed from auto migration
}

func migrateEmailVerifiedColumn() {
	// Fast check using pg_catalog instead of information_schema
	var count int64
	DB.Raw(`
		SELECT COUNT(*) 
		FROM pg_attribute 
		WHERE attrelid = 'users'::regclass 
		AND attname = 'email_verified' 
		AND NOT attisdropped
	`).Scan(&count)

	if count == 0 {
		return
	}

	// Only set existing users (created before email verification feature) as verified
	// Users with no verify token AND created before a certain date are old users
	// For simplicity: set all users without a verification token as verified (one-time migration)
	var usersToVerify int64
	DB.Model(&models.User{}).Where("(email_verify_token IS NULL OR email_verify_token = '') AND email_verified IS NULL").Count(&usersToVerify)

	if usersToVerify > 0 {
		DB.Exec("UPDATE users SET email_verified = true WHERE (email_verify_token IS NULL OR email_verify_token = '') AND email_verified IS NULL")
		log.Printf("Email verification migration: %d existing users marked as verified", usersToVerify)
	}
}

func migrateUserNameColumn() {
	// Fast check using pg_catalog instead of information_schema
	var count int64
	DB.Raw(`
		SELECT COUNT(*) 
		FROM pg_attribute 
		WHERE attrelid = 'users'::regclass 
		AND attname = 'user_name' 
		AND NOT attisdropped
	`).Scan(&count)

	if count == 0 {
		// Column doesn't exist, add it
		log.Println("Adding user_name column...")
		DB.Exec("ALTER TABLE users ADD COLUMN user_name text")

		// Update existing users with default usernames
		DB.Exec("UPDATE users SET user_name = CONCAT('user_', SUBSTRING(id::text, 1, 8)) WHERE user_name IS NULL")

		// Add NOT NULL constraint
		DB.Exec("ALTER TABLE users ALTER COLUMN user_name SET NOT NULL")
		log.Println("user_name column added successfully")
	} else {
		// Column exists, update null values
		log.Println("Updating users with null usernames...")
		DB.Exec("UPDATE users SET user_name = CONCAT('user_', SUBSTRING(id::text, 1, 8)) WHERE user_name IS NULL OR user_name = ''")

		// Check if NOT NULL constraint exists using pg_catalog
		var isNotNull bool
		DB.Raw(`
			SELECT attnotnull 
			FROM pg_attribute 
			WHERE attrelid = 'users'::regclass 
			AND attname = 'user_name'
		`).Scan(&isNotNull)

		if !isNotNull {
			log.Println("Adding NOT NULL constraint to user_name...")
			DB.Exec("ALTER TABLE users ALTER COLUMN user_name SET NOT NULL")
		}
	}
}

func seedRolesAndPermissions() {
	// 1. Define Permissions
	permissions := []models.Permission{
		{Name: "user:read", Description: "Can read user data"},
		{Name: "user:write", Description: "Can modify user data"},
		{Name: "admin:access", Description: "Can access admin panel"},
	}

	for _, p := range permissions {
		DB.FirstOrCreate(&models.Permission{}, models.Permission{Name: p.Name, Description: p.Description})
	}

	// 2. Define Roles
	roles := []string{"admin", "user"}
	for _, r := range roles {
		DB.FirstOrCreate(&models.Role{}, models.Role{Name: r, Description: "Default " + r + " role"})
	}

	// 3. Assign Permissions to Admin Role
	var adminRole models.Role
	DB.Preload("Permissions").Where("name = ?", "admin").First(&adminRole)

	// Fetch all permissions to assign to admin
	var allPermissions []models.Permission
	DB.Find(&allPermissions)

	// Update association (append missing ones)
	DB.Model(&adminRole).Association("Permissions").Replace(allPermissions)

	// 4. Assign Basic Permissions to User Role
	var userRole models.Role
	DB.Preload("Permissions").Where("name = ?", "user").First(&userRole)

	var userPermissions []models.Permission
	DB.Where("name IN ?", []string{"user:read"}).Find(&userPermissions)

	DB.Model(&userRole).Association("Permissions").Replace(userPermissions)

	log.Println("Roles and Permissions seeded")
}

func seedDefaultSettings() {
	// Seed default CORS whitelist
	var whitelistCount int64
	DB.Model(&models.CorsWhitelist{}).Count(&whitelistCount)

	if whitelistCount == 0 {
		defaultWhitelist := []models.CorsWhitelist{
			{
				Origin:      "http://localhost:3000",
				Description: "Default local frontend",
				IsActive:    true,
				CreatedBy:   "system",
			},
			{
				Origin:      "http://localhost:8080",
				Description: "Backend self",
				IsActive:    true,
				CreatedBy:   "system",
			},
		}

		for _, w := range defaultWhitelist {
			DB.Create(&w)
		}
		log.Println("Default CORS whitelist seeded")
	}

	// Seed default rate limit settings
	var rateLimitCount int64
	DB.Model(&models.RateLimitSetting{}).Count(&rateLimitCount)

	if rateLimitCount == 0 {
		defaultRateLimits := []models.RateLimitSetting{
			{
				Name:          "login",
				Description:   "Login endpoint rate limit",
				MaxRequests:   5,
				WindowSeconds: 60, // 1 minute
				IsActive:      true,
			},
			{
				Name:          "register",
				Description:   "Registration endpoint rate limit",
				MaxRequests:   3,
				WindowSeconds: 300, // 5 minutes
				IsActive:      true,
			},
			{
				Name:          "api",
				Description:   "General API rate limit",
				MaxRequests:   100,
				WindowSeconds: 60, // 1 minute
				IsActive:      true,
			},
		}

		for _, r := range defaultRateLimits {
			DB.Create(&r)
		}
		log.Println("Default rate limit settings seeded")
	}
}

// SeedDefaultAdmin creates the default admin user if it doesn't exist
func SeedDefaultAdmin() {
	// Check if admin user already exists (including soft-deleted)
	var adminUser models.User
	err := DB.Unscoped().Where("email = ?", "admin@gauth.local").First(&adminUser).Error

	if err != nil {
		// Admin user doesn't exist, create one
		// Hash default password: "Admin@123"
		hashedPassword, err := bcrypt.GenerateFromPassword([]byte("Admin@123"), bcrypt.DefaultCost)
		if err != nil {
			log.Printf("Failed to hash admin password: %v", err)
			return
		}

		trueBool := true
		adminUser = models.User{
			Email:         "admin@gauth.local",
			UserName:      "admin",
			Password:      string(hashedPassword),
			EmailVerified: &trueBool,
		}

		if err := DB.Create(&adminUser).Error; err != nil {
			log.Printf("Failed to create admin user: %v", err)
			return
		}

		log.Println("✅ Default admin user created:")
		log.Println("   Email: admin@gauth.local")
		log.Println("   Password: Admin@123")
		log.Println("   ⚠️  Please change this password after first login!")
	} else {
		// Admin user exists (possibly soft-deleted)
		if adminUser.DeletedAt.Valid {
			log.Println("Restoring deleted admin user...")
			if err := DB.Model(&adminUser).Unscoped().Update("deleted_at", nil).Error; err != nil {
				log.Printf("Failed to restore admin user: %v", err)
				return
			}
		}
	}

	// Ensure admin role is assigned
	var adminRole models.Role
	if err := DB.Where("name = ?", "admin").First(&adminRole).Error; err != nil {
		log.Printf("Admin role not found: %v", err)
		return
	}

	if err := DB.Model(&adminUser).Association("Roles").Append(&adminRole); err != nil {
		log.Printf("Failed to assign admin role: %v", err)
	}
}
