package seed

import (
	"crypto/rand"
	"encoding/hex"
	"fmt"

	"time"

	"github.com/brianvoe/gofakeit/v6"
	"gorm.io/gorm"

	"server/internal/systemUtils"
	users "server/internal/user"
)

// Credential exposes the plaintext password generated for a seeded user.
type Credential struct {
	Email    string
	Password string
}

// SeedUsers generates n fake users and persists them. Returns the created slice.
func SeedUsers(db *gorm.DB, n int) ([]users.User, []Credential, error) {
	if n <= 0 {
		return nil, nil, fmt.Errorf("seed size must be greater than zero")
	}

	gofakeit.Seed(time.Now().UnixNano())

	items := make([]users.User, 0, n)
	creds := make([]Credential, 0, n)
	for i := 0; i < n; i++ {
		now := time.Now().UTC()
		uuid := gofakeit.UUID()
		email := gofakeit.Email()

		pw, err := randomPassword()
		if err != nil {
			return nil, nil, fmt.Errorf("generate password: %w", err)
		}
		passwordHash, err := systemUtils.HashPassword(pw)
		if err != nil {
			return nil, nil, fmt.Errorf("hash seed password: %w", err)
		}

		item := users.User{
			Email:    email,
			Name:     gofakeit.Name(),
			Password: passwordHash,
			Roles:    users.UserRoles{"user"},
			Status:   users.UserStatusActive,
			Types:    users.UserTypes{"internal"},
			UUID:     uuid,
			Details: &users.UserDetails{
				Title:     gofakeit.JobTitle(),
				FirstName: gofakeit.FirstName(),
				LastName:  gofakeit.LastName(),
				Phone:     gofakeit.Phone(),
				Address:   gofakeit.Street(),
				City:      gofakeit.City(),
				ZipCode:   gofakeit.Zip(),
				Country:   gofakeit.Country(),
			},
			Preferences: &users.UserPreferences{
				UseIdle:          gofakeit.Bool(),
				IdleTimeout:      gofakeit.Number(1, 30),
				UseIdlePassword:  gofakeit.Bool(),
				IdlePin:          gofakeit.DigitN(6),
				UseDirectLogin:   gofakeit.Bool(),
				UseQuadcodeLogin: gofakeit.Bool(),
				SendNoticesMail:  gofakeit.Bool(),
				Language:         gofakeit.Language(),
			},
			Avatar:    nil,
			CreatedAt: now,
			UpdatedAt: now,
			ActivatedAt: func() *time.Time {
				ts := now
				return &ts
			}(),
		}

		items = append(items, item)
		creds = append(creds, Credential{
			Email:    email,
			Password: pw,
		})
	}

	if err := db.Create(&items).Error; err != nil {
		return nil, nil, fmt.Errorf("insert users: %w", err)
	}

	return items, creds, nil
}

func randomPassword() (string, error) {
	buf := make([]byte, 12)
	if _, err := rand.Read(buf); err != nil {
		return "", err
	}
	// hex-encoded -> 24 characters.
	return hex.EncodeToString(buf), nil
}
