package web

import (
	"net/http"
	"strconv"

	"golang.org/x/crypto/bcrypt"

	"bitmask.me/skeleton/internal/database"
	ldap "github.com/go-ldap/ldap/v3"
	"github.com/jmoiron/sqlx"
	"github.com/pkg/errors"
)

// ErrNotImplemented is returned whenever a feature is not implemented yet.
var ErrNotImplemented = errors.New("Not implemented")

// User interface is provided by all data types that are returned from a
// User store.
type User interface {
	GetID() string
	GetDisplayName() string
}

// UserRow wraps a user row from the database.
type UserRow struct {
	*database.User
}

// GetDisplayName implements User interface by returning the display name.
func (u UserRow) GetDisplayName() string {
	return u.GetID()
}

// GetID implements the User interface by returning the user ID.
func (u UserRow) GetID() string {
	return strconv.FormatInt(u.ID, 10)
}

// NewLDAPAuthenticator returns a authable function from a LDAP Database.
func NewLDAPAuthenticator(lc *ldap.Conn) func(user, pass string) (User, error) {
	return func(user, pass string) (User, error) {
		return nil, ErrNotImplemented
	}
}

// NewSQLAuthenticator returns a authable function from a Database.
func NewSQLAuthenticator(db *sqlx.DB) func(user, pass string) (User, error) {
	return func(user, pass string) (User, error) {
		// Fetch email used for login
		email, err := database.EmailByAddress(db, user)
		if err != nil {
			return nil, err
		}

		row, err := email.User(db)
		if err != nil {
			return nil, err
		}

		//u.Password
		err = bcrypt.CompareHashAndPassword(row.Password, []byte(pass))
		if err != nil {
			return nil, err
		}

		u := UserRow{row}

		return u, nil

	}
}

// LoginPageHandler renders the login page, and sets session cookies
// on successful authentication.
func (h *Handlers) LoginPageHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method == http.MethodPost {
		type LoginForm struct {
			Login    string
			Password string
		}

		loginForm := LoginForm{
			Login:    r.PostFormValue("login"),
			Password: r.PostFormValue("password"),
		}

		authenticate := NewSQLAuthenticator(h.App.Database())

		user, err := authenticate(loginForm.Login, loginForm.Password)
		if err != nil {
			context := h.commonRenderContext(r)
			context["Errors"] = []string{"Wrong username or password"}
			h.Templates().Get("auth_login.tmpl").Execute(w, context)
			return
		}

		sess := h.Session()
		sess.Put(r.Context(), SessKeyUserID, user.GetID())
		sess.Put(r.Context(), SessKeyUserName, user.GetDisplayName())
		http.Redirect(w, r, "/app", http.StatusFound)
		return
	}

	h.Templates().Get("auth_login.tmpl").Execute(w, h.commonRenderContext(r))
}