package setup

import (
	"context"
	"errors"
	"fmt"

	"golang.org/x/crypto/bcrypt"

	"github.com/usememos/memos/api"
	"github.com/usememos/memos/common"
)

func Execute(
	ctx context.Context,
	store store,
	hostUsername, hostPassword string,
) error {
	s := setupService{store: store}
	return s.Setup(ctx, hostUsername, hostPassword)
}

type store interface {
	FindUserList(ctx context.Context, find *api.UserFind) ([]*api.User, error)
	CreateUser(ctx context.Context, create *api.UserCreate) (*api.User, error)
}

type setupService struct {
	store store
}

func (s setupService) Setup(
	ctx context.Context,
	hostUsername, hostPassword string,
) error {
	if err := s.makeSureHostUserNotExists(ctx); err != nil {
		return err
	}

	if err := s.createUser(ctx, hostUsername, hostPassword); err != nil {
		return fmt.Errorf("create user: %w", err)
	}
	return nil
}

func (s setupService) makeSureHostUserNotExists(ctx context.Context) error {
	hostUserType := api.Host
	existedHostUsers, err := s.store.FindUserList(ctx, &api.UserFind{
		Role: &hostUserType,
	})
	if err != nil {
		return fmt.Errorf("find user list: %w", err)
	}

	if len(existedHostUsers) != 0 {
		return errors.New("host user already exists")
	}

	return nil
}

func (s setupService) createUser(
	ctx context.Context,
	hostUsername, hostPassword string,
) error {
	userCreate := &api.UserCreate{
		Username: hostUsername,
		// The new signup user should be normal user by default.
		Role:     api.Host,
		Nickname: hostUsername,
		Password: hostPassword,
		OpenID:   common.GenUUID(),
	}

	if err := userCreate.Validate(); err != nil {
		return fmt.Errorf("validate: %w", err)
	}

	passwordHash, err := bcrypt.GenerateFromPassword([]byte(hostPassword), bcrypt.DefaultCost)
	if err != nil {
		return fmt.Errorf("hash password: %w", err)
	}

	userCreate.PasswordHash = string(passwordHash)
	if _, err := s.store.CreateUser(ctx, userCreate); err != nil {
		return fmt.Errorf("create user: %w", err)
	}

	return nil
}