package database

import (
	"log"
	"strings"
	"time"

	"github.com/tapper/backend/models"
	"golang.org/x/crypto/bcrypt"
)

func Seed(ownerEmail string, portStart, portEnd int) {
	seedPortPool(portStart, portEnd)
	seedEloConfig()
	seedOwner(ownerEmail)
}

// SeedDemoUsers creates accounts that make the stage demo self-contained:
// an owner for the admin dashboard tour, and two Steam-linked players for
// the matchmaking demo. Only invoked when DEMO_MODE=true.
func SeedDemoUsers() {
	now := time.Now().UTC()
	demos := []struct {
		Username string
		Email    string
		Password string
		Role     string
		SteamID  string
	}{
		{"owner", "owner@tapper.local", "ownerpass1234", "owner", ""},
		{"demo1", "demo1@tapper.local", "demopass1234", "user", "76561198000000001"},
		{"demo2", "demo2@tapper.local", "demopass1234", "user", "76561198000000002"},
	}

	for _, d := range demos {
		var existing models.User
		if err := DB.Where("email = ?", d.Email).First(&existing).Error; err == nil {
			continue
		}

		hashBytes, err := bcrypt.GenerateFromPassword([]byte(d.Password), 12)
		if err != nil {
			log.Printf("[seed] demo: hash failed for %s: %v", d.Email, err)
			continue
		}
		hash := string(hashBytes)

		user := models.User{
			Username:     d.Username,
			Email:        d.Email,
			PasswordHash: hash,
			Role:         d.Role,
			Tier:         "normal",
			MMR:          1000,
		}
		if d.SteamID != "" {
			steamID := d.SteamID
			linkedAt := now
			user.SteamID = &steamID
			user.SteamLinkedAt = &linkedAt
		}

		if err := DB.Create(&user).Error; err != nil {
			// Both api instances race on the same seed; whichever loses hits a
			// unique constraint. The user already exists, no action needed.
			if strings.Contains(err.Error(), "duplicate key") {
				continue
			}
			log.Printf("[seed] demo: create failed for %s: %v", d.Email, err)
			continue
		}
		log.Printf("[seed] demo: created %s user %s (%s)", d.Role, d.Username, d.Email)
	}
}

func seedPortPool(portStart, portEnd int) {
	var count int64
	DB.Model(&models.PortPool{}).Count(&count)
	if count > 0 {
		return
	}

	ports := make([]models.PortPool, 0, portEnd-portStart+1)
	for p := portStart; p <= portEnd; p++ {
		ports = append(ports, models.PortPool{
			Port:      p,
			Allocated: false,
		})
	}

	if err := DB.CreateInBatches(ports, 50).Error; err != nil {
		log.Printf("[seed] Failed to seed port pool: %v", err)
		return
	}
	log.Printf("[seed] Seeded port pool: %d-%d (%d ports)", portStart, portEnd, len(ports))
}

func seedEloConfig() {
	var cfg models.EloConfig
	result := DB.First(&cfg, 1)
	if result.Error == nil {
		return
	}

	cfg = models.EloConfig{
		ID:                   1,
		KFactorBase:          32,
		KFactorNewPlayer:     48,
		NewPlayerThreshold:   20,
		KFactorHighMMR:       24,
		HighMMRThreshold:     2200,
		PlacementMatches:     10,
		PlacementKFactor:     64,
		MinMMR:               100,
		MaxMMR:               3500,
		DefaultMMR:           1000,
		RoundScoreWeight:     0.15,
		IndividualPerfWeight: 0.10,
		StreakBonus:          0.05,
		StreakCap:            5,
		DecayEnabled:         true,
		DecayDays:            14,
		DecayPerDay:          5,
		DecayFloor:           800,
	}

	if err := DB.Create(&cfg).Error; err != nil {
		log.Printf("[seed] Failed to seed Elo config: %v", err)
		return
	}
	log.Println("[seed] Seeded default Elo configuration")
}

func seedOwner(ownerEmail string) {
	if ownerEmail == "" {
		return
	}

	var user models.User
	result := DB.Where("email = ?", ownerEmail).First(&user)
	if result.Error != nil {
		log.Printf("[seed] Owner email %s not found yet, will be promoted on registration", ownerEmail)
		return
	}

	if user.Role != "owner" {
		now := time.Now().UTC()
		DB.Model(&user).Updates(map[string]interface{}{
			"role":       "owner",
			"updated_at": now,
		})
		log.Printf("[seed] Promoted user %s (%s) to owner", user.Username, user.Email)
	}
}
