//Root is used to show the login page of the app
//when a user browses to the page (usually just the domain minus any path), the user is checked for a session
//if a session exists, the app attempts to auto-login the user
//otherwise a user is shown the log in prompt
//this also handles the "first run" of the app in which no users exist yet...it forces creation of the "super admin"
func Root(w http.ResponseWriter, r *http.Request) {
	//check that session store was initialized correctly
	if err := sessionutils.CheckSession(); err != nil {
		notificationPage(w, "panel-danger", sessionInitError, err, "btn-default", "/", "Go Back")
		return
	}

	//check that stripe private key and statement desecriptor were read correctly
	if err := card.CheckStripe(); err != nil {
		notificationPage(w, "panel-danger", sessionInitError, err, "btn-default", "/", "Go Back")
		return
	}

	//check if the admin user exists
	//redirect user to create admin if it does not exist
	err := users.DoesAdminExist(r)
	if err == users.ErrAdminDoesNotExist {
		http.Redirect(w, r, "/setup/", http.StatusFound)
		return
	} else if err != nil {
		notificationPage(w, "panel-danger", adminInitError, err, "btn-default", "/", "Go Back")
		return
	}

	//check if user is already signed in
	//if user is already logged in, redirect to /main/ page
	session := sessionutils.Get(r)
	if session.IsNew == false {
		uId := session.Values["user_id"].(int64)
		c := appengine.NewContext(r)
		u, err := users.Find(c, uId)
		if err != nil {
			sessionutils.Destroy(w, r)
			notificationPage(w, "panel-danger", "Autologin Error", "There was an issue looking up your user account. Please go back and try logging in.", "btn-default", "/", "Go Back")
			return
		}

		//user data was found
		//check if user is allowed access
		if users.AllowedAccess(u) == false {
			sessionutils.Destroy(w, r)
			notificationPage(w, "panel-danger", "Autologin Error", "You are not allowed access. Please contact an administrator.", "btn-default", "/", "Go Back")
		}

		//user account is found an allowed access
		//redirect user
		http.Redirect(w, r, "/main/", http.StatusFound)
		return
	}

	//load the login page
	templates.Load(w, "root", nil)
	return
}
//Auth checks if a user is logged in and is allowed access to the app
//this is done on every page load and every endpoint
func Auth(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		//get user data from session
		session := sessionutils.Get(r)

		//session data does not exist yet
		//this is a new session
		//redirect user to log in page
		if session.IsNew {
			http.Redirect(w, r, "/", http.StatusFound)
			return
		}

		//check if user id is in session
		//it *should* be!
		//otherwise show user a notice and force user to log in again
		userId, ok := session.Values["user_id"].(int64)
		if ok == false {
			sessionutils.Destroy(w, r)
			notificationPage(w, "panel-danger", "Session Expired", "Your session has expired. Please log back in or contact an administrator if this problem persists.", "btn-default", "/", "Log In")
			return
		}

		//look up user in memcache and/or datastore
		c := appengine.NewContext(r)
		data, err := users.Find(c, userId)
		if err != nil {
			sessionutils.Destroy(w, r)
			notificationPage(w, "panel-danger", "Application Error", "The app encountered an error in the middleware while trying to authenticate you as a legitimate user. Please try logging in again or contact an administrator.", "btn-default", "/", "Log In")
			return
		}

		//check if user is allowed access to the app
		//this is a setting the app's administrators can toggle for each user
		if users.AllowedAccess(data) == false {
			sessionutils.Destroy(w, r)
			http.Redirect(w, r, "/", http.StatusFound)
			return
		}

		//user is allowed access
		//extend expiration of session cookie to allow user to stay "logged in"
		sessionutils.ExtendExpiration(session, w, r)

		//move to next middleware or handler
		next.ServeHTTP(w, r)
	})
}
//Logout handles logging out of the app
//this removes the session data so a user must log back in before using the app
func Logout(w http.ResponseWriter, r *http.Request) {
	//destroy session
	sessionutils.Destroy(w, r)

	//redirect to root page
	http.Redirect(w, r, "/", http.StatusFound)
	return
}
//Login verifies a username and password combo
//this makes sure the user exists, that the password is correct, and that the user is active
//if user is allowed access, their data is saved to the session and they are redirected into the app
func Login(w http.ResponseWriter, r *http.Request) {
	//get form values
	username := r.FormValue("username")
	password := r.FormValue("password")

	//get user data
	c := appengine.NewContext(r)
	id, data, err := exists(c, username)
	if err == ErrUserDoesNotExist {
		notificationPage(w, "panel-danger", "Cannot Log In", "The username you provided does not exist.", "btn-default", "/", "Try Again")
		return
	}

	//is user allowed access
	if AllowedAccess(data) == false {
		notificationPage(w, "panel-danger", "Cannot Log In", "You are not allowed access. Please contact an administrator.", "btn-default", "/", "Go Back")
		return
	}

	//validate password
	_, err = pwds.Verify(password, data.Password)
	if err != nil {
		notificationPage(w, "panel-danger", "Cannot Log In", "The password you provided is invalid.", "btn-default", "/", "Try Again")
		return
	}

	//user validated
	//save session data
	session := sessionutils.Get(r)
	if session.IsNew == false {
		sessionutils.Destroy(w, r)
		session = sessionutils.Get(r)
	}
	sessionutils.AddValue(session, "username", username)
	sessionutils.AddValue(session, "user_id", id)
	sessionutils.Save(session, w, r)

	//show user main page
	http.Redirect(w, r, "/main/", http.StatusFound)
	return
}