package provision

import (
	"context"
	"errors"
	"fmt"
	"strings"
	"time"

	"gitea.hostxtra.co.uk/mrhid6/vantage/shared/models"
	"github.com/google/uuid"
	"go.mongodb.org/mongo-driver/v2/mongo"
	"golang.org/x/crypto/bcrypt"
)

// BcryptCost is the work factor for every password hash Vantage writes.
// Changing it changes nothing about existing hashes, which carry their own cost.
const BcryptCost = 12

// ErrEmailTaken is returned when the unique index on users.email rejects an insert.
var ErrEmailTaken = errors.New("email already registered")

// CreateUser hashes password and inserts the user. An empty password leaves the
// hash empty, which is how OIDC users are stored.
func CreateUser(ctx context.Context, db *mongo.Database, instanceID, email, password, role, authSource string) (*models.User, error) {
	var hash string
	if password != "" {
		b, err := bcrypt.GenerateFromPassword([]byte(password), BcryptCost)
		if err != nil {
			return nil, err
		}
		hash = string(b)
	}
	return CreateUserWithHash(ctx, db, instanceID, email, hash, role, authSource)
}

// CreateUserWithHash inserts a user whose password was already hashed
// elsewhere. sitesvc hashes at signup and only holds the hash by the time the
// verification link is opened.
func CreateUserWithHash(ctx context.Context, db *mongo.Database, instanceID, email, passwordHash, role, authSource string) (*models.User, error) {
	email = strings.ToLower(strings.TrimSpace(email))
	if email == "" {
		return nil, fmt.Errorf("email required")
	}
	if !models.ValidRole(role) {
		return nil, fmt.Errorf("invalid role %q", role)
	}

	u := &models.User{
		UserID:       uuid.NewString(),
		InstanceID:   instanceID,
		Email:        email,
		PasswordHash: passwordHash,
		Role:         role,
		AuthSource:   authSource,
		CreatedAt:    time.Now().UTC(),
	}
	if _, err := db.Collection("users").InsertOne(ctx, u); err != nil {
		if mongo.IsDuplicateKeyError(err) {
			return nil, ErrEmailTaken
		}
		return nil, err
	}
	return u, nil
}
