package database

import (
	"log"
	"time"

	"golang.org/x/crypto/bcrypt"
)

type User struct {
	ID        int
	Username  string
	Password  string
	Email     string
	PublicKey string `db:"public_key"`
	Admin     bool
	Enabled   bool
	BaseDir   string    `db:"base_dir"`
	CreatedAt time.Time `db:"created_at"`
	UpdatedAt time.Time `db:"updated_at"`
}

func CreateUser(user User) error {
	log.Println(user)
	_, err := db.NamedExec(`
    INSERT INTO users (
      username,
      password,
      email,
      public_key,
      admin,
      enabled,
      base_dir,
      created_at,
      updated_at
    ) VALUES (
      :username,
      :password,
      :email,
      :public_key,
      :admin,
      :enabled,
      :base_dir,
      :created_at,
      :updated_at
    )
  `, user)
	return err
}

func GetUser(id int) (User, error) {
	var user User
	err := db.Get(&user, `
    SELECT
      id,
      username,
      password,
      email,
      admin,
      enabled,
      base_dir,
      created_at,
      updated_at
    FROM users
    WHERE id = ?
  `, id)
	return user, err
}

func GetUserByUsername(username string) (User, error) {
	var user User
	err := db.Get(&user, `
    SELECT
      id,
      username,
      password,
      email,
      public_key,
      admin,
      enabled,
      base_dir,
      created_at,
      updated_at
    FROM users
    WHERE username = ?
  `, username)
	return user, err
}

func UpdateUser(user User) error {
	_, err := db.NamedExec(`
    UPDATE users
    SET
      username = :username,
      password = :password,
      email = :email,
      public_key = :public_key,
      admin = :admin,
      enabled = :enabled,
      base_dir = :base_dir,
      updated_at = :updated_at
    WHERE id = :id
  `, user)
	return err
}

func DeleteUser(id int) error {
	_, err := db.Exec(`
    DELETE FROM users
    WHERE id = ?
  `, id)
	return err
}

func ListUsers() ([]User, error) {
	var users []User
	err := db.Select(&users, `
    SELECT
      id,
      username,
      password,
      email,
      public_key,
      admin,
      enabled,
      base_dir,
      created_at,
      updated_at
    FROM users
  `)
	return users, err
}

// SetPassword sets the password of a user
func (u *User) SetPassword(password string) error {
	byteHash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
	u.Password = string(byteHash)
	if err != nil {
		return err
	}
	return UpdateUser(*u)
}

// CheckPassword checks if the password of a user matches the given password
func CheckUserPassword(username, password string) (bool, error) {
	var dbHash string
	err := db.QueryRow(``, username).Scan(dbHash)
	if err != nil {
		return false, err
	}
	err = bcrypt.CompareHashAndPassword([]byte(dbHash), []byte(password))
	if err != nil {
		log.Printf("Wrong password attempt for user %s", username)
		return false, err
	} else {
		return true, nil
	}
}
