package store

import (
	"database/sql"
	"fmt"

	sq "github.com/Masterminds/squirrel"

	"git.ctrlz.es/mgdelacroix/craban/model"
)

var userColumns = []string{"id", "name", "mail", "username", "password", "admin"}

type UserStore struct {
	Conn *sql.DB
}

func NewUserStore(s *Store) *UserStore {
	return &UserStore{Conn: s.Conn}
}

func (us *UserStore) Q() sq.StatementBuilderType {
	return sq.StatementBuilder.RunWith(us.Conn)
}

func (us *UserStore) usersFromRows(rows *sql.Rows) ([]*model.User, error) {
	users := []*model.User{}

	for rows.Next() {
		var user model.User

		err := rows.Scan(
			&user.ID,
			&user.Name,
			&user.Mail,
			&user.Username,
			&user.Password,
			&user.Admin,
		)
		if err != nil {
			return nil, err
		}

		users = append(users, &user)
	}

	return users, nil
}

func (us *UserStore) GetByID(id int) (*model.User, error) {
	return us.getUserByCondition(sq.Eq{"id": id})
}

func (us *UserStore) GetByUsername(username string) (*model.User, error) {
	return us.getUserByCondition(sq.Eq{"username": username})
}

func (us *UserStore) getUserByCondition(condition sq.Eq) (*model.User, error) {
	users, err := us.getUsersByCondition(condition)
	if err != nil {
		return nil, err
	}

	return users[0], nil
}

func (us *UserStore) getUsersByCondition(condition sq.Eq) ([]*model.User, error) {
	rows, err := us.Q().Select(userColumns...).
		From("users").
		Where(condition).
		Query()
	if err != nil {
		return nil, fmt.Errorf("cannot get users: %w", err)
	}
	defer rows.Close()

	users, err := us.usersFromRows(rows)
	if err != nil {
		return nil, fmt.Errorf("cannot get users from rows: %w", err)
	}

	if len(users) == 0 {
		return nil, sql.ErrNoRows
	}

	return users, nil
}

func (us *UserStore) Create(user *model.User) (*model.User, error) {
	query := us.Q().Insert("users").
		Columns(userColumns[1:]...).
		Values(user.Name, user.Mail, user.Username, user.Password, user.Admin)

	res, err := query.Exec()
	if err != nil {
		return nil, fmt.Errorf("cannot insert user: %w", err)
	}

	id, err := res.LastInsertId()
	if err != nil {
		return nil, fmt.Errorf("cannot get last insert id for created user: %w", err)
	}

	return us.GetByID(int(id))
}

// ToDo: add pagination and filtering
func (us *UserStore) List() ([]*model.User, error) {
	return us.getUsersByCondition(sq.Eq{})
}

func (us *UserStore) DeleteByUsername(username string) error {
	// ToDo: add transaction
	// ToDo: manage user owned games, probably at app level

	user, err := us.GetByUsername(username)
	if err != nil {
		return fmt.Errorf("cannot get user for deletion: %w", err)
	}

	_, err = us.Q().Delete("users").
		Where(sq.Eq{"username": username}).
		Exec()
	if err != nil {
		return fmt.Errorf("cannot delete user: %w", err)
	}

	_, err = us.Q().Delete("gamemembers").
		Where(sq.Eq{"user_id": user.ID}).
		Exec()
	if err != nil {
		return fmt.Errorf("cannot delete game members when deleting user: %w", err)
	}

	return nil
}
