package main

import (
	"crypto/rand"
	"encoding/hex"
	"errors"
	"strings"
	"time"
)

func mustRandToken(n int) string {
	b := make([]byte, n)
	if _, err := rand.Read(b); err != nil {
		panic(err)
	}
	return hex.EncodeToString(b)
}

func now() time.Time { return time.Now().UTC() }

func defaultUsername() string {
	// io-game style: adjective-animal-xxxx
	adjectives := []string{"swift", "brave", "mighty", "cheeky", "sneaky", "zippy", "bouncy", "crispy", "fuzzy", "spicy", "snappy", "jazzy", "spry", "bold", "witty"}
	animals := []string{"otter", "panda", "falcon", "lynx", "badger", "tiger", "koala", "yak", "gecko", "eagle", "mamba", "fox", "yak", "whale", "rhino"}
	a := adjectives[int(time.Now().UnixNano())%len(adjectives)]
	an := animals[int(time.Now().UnixNano()/17)%len(animals)]
	return a + "-" + an + "-" + strings.ToLower(mustRandToken(2))
}

func slugify(s string) string {
	s = strings.ToLower(s)
	s = strings.TrimSpace(s)
	s = strings.ReplaceAll(s, " ", "-")
	// keep alnum and - only
	var b strings.Builder
	for _, r := range s {
		if (r >= 'a' && r <= 'z') || (r >= '0' && r <= '9') || r == '-' {
			b.WriteRune(r)
		}
	}
	out := b.String()
	if out == "" {
		out = "user-" + mustRandToken(2)
	}
	return out
}

func ensureUniqueUsernameAndSlug(u *User) error {
	// try to keep username/slug unique by appending short token if needed
	baseU := u.Username
	baseS := slugify(u.Username)
	for i := 0; i < 5; i++ {
		candU := baseU
		candS := baseS
		if i > 0 {
			suffix := "-" + mustRandToken(1)
			candU = baseU + suffix
			candS = baseS + suffix
		}
		var cnt int64
		db.Model(&User{}).Where("username = ?", candU).Count(&cnt)
		if cnt == 0 {
			db.Model(&User{}).Where("slug = ?", candS).Count(&cnt)
			if cnt == 0 {
				u.Username = candU
				u.Slug = candS
				return nil
			}
		}
	}
	return errors.New("cannot create unique username/slug")
}

func userByEmail(email string) (*User, error) {
	var u User
	tx := db.Where("email = ?", strings.ToLower(email)).First(&u)
	if tx.Error != nil {
		return nil, tx.Error
	}
	return &u, nil
}

func userBySlug(slug string) (*User, error) {
	var u User
	tx := db.Where("slug = ?", slug).First(&u)
	if tx.Error != nil {
		return nil, tx.Error
	}
	return &u, nil
}

func atoiSafe(s string) int {
	var n int
	for _, r := range s {
		if r < '0' || r > '9' {
			return 0
		}
	}
	_, _ = fmtSscanf(s, "%d", &n)
	return n
}

// minimal sscanf to avoid fmt import just for one call
func fmtSscanf(s, _ string, a *int) (int, error) {
	// Only supports "%d"
	n := 0
	for i := 0; i < len(s); i++ {
		c := s[i]
		if c < '0' || c > '9' {
			break
		}
		n = n*10 + int(c-'0')
	}
	*a = n
	return 1, nil
}

func stripAfterDot(s string) string {
	if idx := strings.Index(s, "."); idx != -1 {
		return s[:idx]
	}
	return s
}
