package repo

import (
	"context"
	"fmt"
	"time"

	"github.com/vingarcia/ksql"
)

var usersTable = ksql.NewTable("users", "id")

// This function doesn't care if db is a transaction or not:
func GetUser(ctx context.Context, db ksql.Provider, userId int) (User, error) {
	var user User
	err := db.QueryOne(ctx, &user, "SELECT * FROM users WHERE id = ?", userId)
	return user, err
}

// This function doesn't care if db is a transaction or not:
func GetUserByEmail(ctx context.Context, db ksql.Provider, email string) (User, error) {
	var user User
	err := db.QueryOne(ctx, &user, "SELECT * FROM users WHERE email = ?", email)
	return user, err
}

// This function doesn't care if db is a transaction or not:
func CreateUser(ctx context.Context, db ksql.Provider, user User) error {
	user.CreatedAt = time.Now()
	return db.Insert(ctx, usersTable, &user)
}

// This function doesn't care if db is a transaction or not:
func UpdateUser(ctx context.Context, db ksql.Provider, user User) error {
	return db.Patch(ctx, usersTable, &user)
}

// This function creates a transaction from the input db, if db was already a transaction
// this operation will just keep working in the same transaction instead of creating a new one.
func ChangeUserEmail(ctx context.Context, db ksql.Provider, userID int, newEmail string) error {
	return db.Transaction(ctx, func(db ksql.Provider) error {
		user, err := GetUser(ctx, db, userID)
		if err != nil {
			return err
		}

		// If there is nothing to do, just return:
		if user.Email == newEmail {
			return nil
		}

		_, err = GetUserByEmail(ctx, db, newEmail)
		if err != ksql.ErrRecordNotFound {
			return fmt.Errorf("can't change user email to '%s': this email is already used by other user", newEmail)
		}
		if err != nil {
			return err
		}

		user.Email = newEmail
		return UpdateUser(ctx, db, user)
	})
}