// AddOrganizationOwner It is handler for POST /organizations/{globalid}/owners
func (api OrganizationsAPI) AddOrganizationOwner(w http.ResponseWriter, r *http.Request) {
	globalid := mux.Vars(r)["globalid"]

	var s searchMember

	if err := json.NewDecoder(r.Body).Decode(&s); err != nil {
		http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
		return
	}

	orgMgr := organization.NewManager(r)

	org, err := orgMgr.GetByName(globalid)
	if err != nil {
		if err == mgo.ErrNotFound {
			http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
		} else {
			handleServerError(w, "getting organization", err)
		}
		return
	}

	u, err := SearchUser(r, s.SearchString)
	if err != nil {
		log.Error(err)
		http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
		return
	}
	for _, membername := range org.Owners {
		if membername == u.Username {
			http.Error(w, http.StatusText(http.StatusConflict), http.StatusConflict)
			return
		}
	}
	// Create JoinRequest
	invitationMgr := invitations.NewInvitationManager(r)

	orgReq := &invitations.JoinOrganizationInvitation{
		Role:         invitations.RoleOwner,
		Organization: globalid,
		User:         u.Username,
		Status:       invitations.RequestPending,
		Created:      db.DateTime(time.Now()),
	}

	if err := invitationMgr.Save(orgReq); err != nil {
		log.Error("Error inviting owner: ", err.Error())
		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusCreated)

	json.NewEncoder(w).Encode(orgReq)
}
//ProcessRegistrationForm processes the user registration form
func (service *Service) ProcessRegistrationForm(w http.ResponseWriter, request *http.Request) {
	response := struct {
		Redirecturl string `json:"redirecturl"`
		Error       string `json:"error"`
	}{}
	values := struct {
		TwoFAMethod    string `json:"twofamethod"`
		Login          string `json:"login"`
		Email          string `json:"email"`
		Phonenumber    string `json:"phonenumber"`
		TotpCode       string `json:"totpcode"`
		Password       string `json:"password"`
		RedirectParams string `json:"redirectparams"`
	}{}
	if err := json.NewDecoder(request.Body).Decode(&values); err != nil {
		log.Debug("Error decoding the registration request:", err)
		http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
		return
	}

	twoFAMethod := values.TwoFAMethod
	if twoFAMethod != "sms" && twoFAMethod != "totp" {
		log.Info("Invalid 2fa method during registration: ", twoFAMethod)
		http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
		return
	}

	totpsession, err := service.GetSession(request, SessionForRegistration, "totp")
	if err != nil {
		log.Error("ERROR while getting the totp registration session", err)
		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		return
	}
	if totpsession.IsNew {
		log.Debug("New registration session while processing the registration form")
		http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
		return
	}
	totpsecret, ok := totpsession.Values["secret"].(string)
	if !ok {
		log.Error("Unable to convert the stored session totp secret to a string")
		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		return
	}

	valid := user.ValidateUsername(values.Login)
	var phonenumber user.Phonenumber
	if !valid {
		response.Error = "invalid_username_format"
		w.WriteHeader(422)
		json.NewEncoder(w).Encode(response)
		return
	}
	newuser := &user.User{
		Username:       values.Login,
		EmailAddresses: []user.EmailAddress{user.EmailAddress{Label: "main", EmailAddress: values.Email}},
	}
	//validate the username is not taken yet
	userMgr := user.NewManager(request)

	count, err := userMgr.GetPendingRegistrationsCount()
	if err != nil {
		log.Error("Failed to get pending registerations count: ", err)
		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		return
	}
	log.Debug("count", count)
	if count >= MAX_PENDING_REGISTRATION_COUNT {
		log.Warn("Maximum amount of pending registrations reached")
		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		return
	}

	//we now just depend on mongo unique index to avoid duplicates when concurrent requests are made
	userExists, err := userMgr.Exists(newuser.Username)
	if err != nil {
		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		return
	}
	if userExists {
		log.Debug("USER ", newuser.Username, " already registered")
		http.Error(w, http.StatusText(http.StatusConflict), http.StatusConflict)
		return
	}

	if twoFAMethod == "sms" {
		phonenumber = user.Phonenumber{Label: "main", Phonenumber: values.Phonenumber}
		if !phonenumber.IsValid() {
			log.Debug("Invalid phone number")
			w.WriteHeader(422)
			response.Error = "invalid_phonenumber"
			json.NewEncoder(w).Encode(&response)
			return
		}
		newuser.Phonenumbers = []user.Phonenumber{phonenumber}
		// Remove account after 3 days if it still doesn't have a verified phone by then
		duration := time.Duration(time.Hour * 24 * 3)
		expiresAt := time.Now()
		expiresAt = expiresAt.Add(duration)
		newuser.Expire = db.DateTime(expiresAt)
	} else {
		token := totp.TokenFromSecret(totpsecret)
		if !token.Validate(values.TotpCode) {
			log.Debug("Invalid totp code")
			w.WriteHeader(422)
			response.Error = "invalid_totpcode"
			json.NewEncoder(w).Encode(&response)
			return
		}
	}

	userMgr.Save(newuser)
	passwdMgr := password.NewManager(request)
	err = passwdMgr.Save(newuser.Username, values.Password)
	if err != nil {
		log.Error(err)
		if err.Error() != "internal_error" {
			w.WriteHeader(422)
			response.Error = "invalid_password"
			json.NewEncoder(w).Encode(&response)
		} else {
			http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		}
		return
	}

	if twoFAMethod == "sms" {
		validationkey, err := service.phonenumberValidationService.RequestValidation(request, newuser.Username, phonenumber, fmt.Sprintf("https://%s/phonevalidation", request.Host))
		if err != nil {
			log.Error(err)
			http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
			return
		}
		registrationSession, err := service.GetSession(request, SessionForRegistration, "registrationdetails")
		registrationSession.Values["username"] = newuser.Username
		registrationSession.Values["phonenumbervalidationkey"] = validationkey
		registrationSession.Values["redirectparams"] = values.RedirectParams

		sessions.Save(request, w)
		response.Redirecturl = fmt.Sprintf("https://%s/register#/smsconfirmation", request.Host)
		json.NewEncoder(w).Encode(&response)
		return
	}

	totpMgr := totp.NewManager(request)
	totpMgr.Save(newuser.Username, totpsecret)
	log.Debugf("Registered %s", newuser.Username)
	service.loginUser(w, request, newuser.Username)
}
// AddOrganizationMember Assign a member to organization
// It is handler for POST /organizations/{globalid}/members
func (api OrganizationsAPI) AddOrganizationMember(w http.ResponseWriter, r *http.Request) {
	globalid := mux.Vars(r)["globalid"]

	var s searchMember

	if err := json.NewDecoder(r.Body).Decode(&s); err != nil {
		http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
		return
	}

	orgMgr := organization.NewManager(r)

	org, err := orgMgr.GetByName(globalid)
	if err != nil {
		if err == mgo.ErrNotFound {
			http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
		} else {
			handleServerError(w, "getting organization", err)
		}
		return
	}

	// Check if user exists
	u, err := SearchUser(r, s.SearchString)
	if err != nil {
		if err != mgo.ErrNotFound {
			log.Error(err)
			http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
			return
		}
		http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
		return
	}
	for _, membername := range org.Members {
		if membername == u.Username {
			http.Error(w, http.StatusText(http.StatusConflict), http.StatusConflict)
			return
		}
	}

	for _, membername := range org.Owners {
		if membername == u.Username {
			http.Error(w, http.StatusText(http.StatusConflict), http.StatusConflict)
			return
		}
	}

	// Create JoinRequest
	invitationMgr := invitations.NewInvitationManager(r)
	count, err := invitationMgr.CountByOrganization(globalid)
	if err != nil {
		log.Error(err)
		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		return
	}
	if count >= MAX_AMOUNT_INVITATIONS_PER_ORGANIZATION {
		log.Error("Reached invitation limit for organization ", globalid)
		writeErrorResponse(w, 422, "max_amount_of_invitations_reached")
		return
	}
	orgReq := &invitations.JoinOrganizationInvitation{
		Role:         invitations.RoleMember,
		Organization: globalid,
		User:         u.Username,
		Status:       invitations.RequestPending,
		Created:      db.DateTime(time.Now()),
	}

	if err := invitationMgr.Save(orgReq); err != nil {
		log.Error("Error inviting member: ", err.Error())
		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusCreated)

	json.NewEncoder(w).Encode(orgReq)
}