package api

import (
  "errors"
  "golang.org/x/crypto/bcrypt"
  "log"

  "github.com/gofiber/fiber/v2"
  _ "github.com/mattn/go-sqlite3"
  sq "github.com/Masterminds/squirrel"
  "github.com/gofiber/fiber/v2/middleware/session"

  "MY/webapp/data"
  "MY/webapp/config"
  . "MY/webapp/common"
)

func GetApiLogout(c *fiber.Ctx) error {
  err := LogoutUser(c)
  if err != nil { return IfErrNil(err, c) }

  return c.Redirect("/")
}

func PostApiRegister(c *fiber.Ctx) error {
  user, err := ReceivePost[data.User](c)
  if err != nil { return IfErrNil(err, c) }

  err = SetUserPassword(user)
  if err != nil { return IfErrNil(err, c) }

  sql, args, err := sq.Insert("user").
    Columns("username", "email", "password").
    Values(user.Username, user.Email, user.Password).ToSql()

  _, err = data.Exec(err, sql, args...)
  if err != nil { return IfErrNil(err, c) }

  return c.Redirect("/login/")
}

func PostApiLogin(c *fiber.Ctx) error {
  var user data.User

  login, err := ReceivePost[data.Login](c)
  if(err != nil) { return IfErrNil(err, c) }

  pass_good, err := LoginUser(&user, login)
  if err != nil { return IfErrNil(err, c) }

  if pass_good {
    sess, err := STORE.Get(c)
    if err != nil { return IfErrNil(err, c) }

    sess.Set("user_id", user.Id)
    sess.Set("authenticated", true)
    sess.Set("admin", IsAdmin(&user))
    err = sess.Save()
    if err != nil { return IfErrNil(err, c) }

    return c.Redirect("/")
  } else {
    return c.Redirect("/login/")
  }
}

func IsAdmin(user *data.User) bool {
  return user.Username == config.Settings.Admin
}

func CheckAuthed(c *fiber.Ctx, needs_admin bool) (*session.Session, error) {
  sess, err := STORE.Get(c)
  if err != nil { return sess, err }

  // BUG: this has to come from the databse, just temporary
  admin := sess.Get("admin") == true
  authed := sess.Get("authenticated") == true

  log.Printf("session admin=%v, session authed=%v, needs_admin = %v",
    sess.Get("admin"), sess.Get("authenticated"), needs_admin)

  if needs_admin {
    authed = admin && authed
    log.Printf("after needs_admin block: authed=%v", authed)
  }

  if authed {
    log.Println("user is authed, return nil and sess")
    return sess, nil
  } else {
    log.Println("user is NOT authed, return error")
    return sess, errors.New("Authentication, permission failure")
  }
}

func LogoutUser(c *fiber.Ctx) error {
  sess, err := STORE.Get(c)
  if err != nil { return err }

  err = sess.Destroy()
  if err != nil { return err }

  err = sess.Save()
  return err
}

func LoginUser(result *data.User, login *data.Login) (bool, error) {
  sql, args, err := sq.Select("username, password").
      From("user").Where("username=?", login.Username).ToSql()

  if err != nil { return false, err }

  err = data.DB.Get(result, sql, args...)
  if err != nil { return false, err }

  pass_good := bcrypt.CompareHashAndPassword([]byte(result.Password), []byte(login.Password))
  if pass_good != nil { return false, pass_good }

  return login.Username == result.Username && pass_good == nil, nil
}

func SetUserPassword(user *data.User) error {
  hashed, err := bcrypt.GenerateFromPassword([]byte(user.Password), 12)
  if err != nil { return err }

  user.Password = string(hashed)
  return nil
}
