<?php
namespace SabaiApps\Directories\Component\FrontendSubmit\Controller;

use SabaiApps\Directories\Component\FrontendSubmit\FrontendSubmitComponent;
use SabaiApps\Directories\Context;
use SabaiApps\Directories\Component\Form;
use SabaiApps\Directories\Exception;
use SabaiApps\Framework\User\AbstractIdentity;

class LoginOrRegister extends Form\Controller {

	protected $_bundle, $_entity, $_action, $_params = array(), $_template = 'frontendsubmit_login_register_form', $_isRegistrationPage = false;

	protected function _doGetFormSettings( Context $context, array &$storage ) {
		if ( $redirect_to = $context->getRequest()->asStr( 'redirect_to', false ) ) {
			$redirect_url = $this->Url( $redirect_to );
			$url_params   = $redirect_url->params;
			if ( ! empty( $url_params['redirect_bundle'] ) ) {
				if ( ! empty( $url_params['redirect_action'] ) // action param required to check if routable
					&& ( $this->_bundle = $this->Entity_Bundle( $url_params['redirect_bundle'] ) ) // make sure requested bundle is valid
				) {
					$this->_action = $url_params['redirect_action'];
					if ( ! empty( $url_params['redirect_entity'] ) ) {
						if ( ! $this->_entity = $this->Entity_Entity( $this->_bundle->entitytype_name, $url_params['redirect_entity'] ) ) {
							$this->_bundle = $this->_entity = $this->_action = null;
						}
					}
				}
			} elseif ( ! empty( $url_params['redirect_bundle_type'] )
				&& isset( $url_params['redirect_action'] )
				&& $url_params['redirect_action'] === 'add'
				&& $this->FrontendSubmit_Submission_bundles( $url_params['redirect_bundle_type'] )
			) {
				// Guest user has at least one bundle that is submittable
				$this->_bundle = $url_params['redirect_bundle_type'];
				$this->_action = $url_params['redirect_action'];
			}
			unset( $url_params['redirect_bundle'], $url_params['redirect_entity'], $url_params['redirect_bundle_type'], $url_params['redirect_action'] );
			$this->_params = $url_params;
		}

		// Init form
		$form = array(
			'#class'    => 'drts-frontendsubmit-login-register-form',
			'#build_id' => false,
			'#errors'   => array(),
			'#js_ready' => 'DRTS.FrontendSubmit.loginRegisterForm("#__FORM_ID__", {});',
		);
		$this->getPlatform()->addJsFile( 'frontendsubmit-login-register-form.min.js', 'frontendsubmit-login-register-form', 'drts', 'directories-pro' );

		// Process OAuth if requested
		if ( $show_login_form = $this->_isShowLoginForm() ) {
			if ( $context->getRequest()->asBool( 'oauth' ) ) {
				try {
					$this->_oauth( $context, $redirect_to );
				} catch ( \Exception $e ) {
					$form['#errors'][''] = $e->getMessage();
				}
			}
		}

		if ( $show_register_form = $this->_isShowRegisterForm() ) {
			$show_register_form_separate_page = ! $this->_isRegistrationPage && $this->getComponent( 'FrontendSubmit' )->getConfig( 'register', 'show_separate_page' );
		} else {
			$show_register_form_separate_page = false;
		}
		if ( ! $show_login_form
			&& ! $this->getPlatform()->isLoginFormRequired()
		) {
			if ( ! $this->getPlatform()->isUserRegisterable()
				|| ! $show_register_form
			) {
				if ( empty( $redirect_to )
					|| ! isset( $this->_bundle )
					|| ! isset( $this->_action )
				) {
					// No form to show, so redirect to platform login
					$context->setRedirect( $this->getPlatform()->getLoginUrl( $this->_getRedirectUrl( $redirect_to ) ) );
					return;
				}
			}
		}

		if ( $show_login_form
			|| $this->getPlatform()->isLoginFormRequired()
		) {
			$login_config     = $this->getComponent( 'FrontendSubmit' )->getConfig( 'login' );
			$login_input_type = 'textfield';
			if ( ! empty( $login_config['allow_email'] ) ) {
				// E-mail address allowed
				if ( isset( $login_config['allow_username'] )
					&& empty( $login_config['allow_username'] )
				) {
					// Username not allowed
					$login_label      = __( 'E-mail Address', 'directories-pro' );
					$login_input_type = 'email';
				} else {
					$login_label = __( 'Username or E-mail Address', 'directories-pro' );
				}
			} else {
				$login_label = __( 'Username', 'directories-pro' );
			}
			$form['login'] = array(
				'#tree'       => true,
				'#weight'     => 1,
				'#submit_for' => 'login',
				'username'    => array(
					'#type'     => $login_input_type,
					'#title'    => $login_label,
					'#weight'   => 1,
					'#required' => true,
				),
				'password'    => array(
					'#type'     => 'password',
					'#title'    => __( 'Password', 'directories-pro' ),
					'#weight'   => 3,
					'#required' => true,
				),
				'remember'    => array(
					'#type'   => 'checkbox',
					'#title'  => __( 'Remember Me', 'directories-pro' ),
					'#switch' => false,
					'#weight' => 98,
				),
				'login'       => array(
					'#weight' => 99,
					'#class'  => DRTS_BS_PREFIX . 'd-flex ' . DRTS_BS_PREFIX . 'flex-row ' . DRTS_BS_PREFIX . 'align-items-center',
					'#group'  => true,
					'submit'  => array(
						'#type'      => 'submit',
						'#btn_label' => __( 'Login', 'directories-pro' ),
						'#btn_color' => 'primary',
						'#btn_size'  => 'lg',
						'#submit'    => array(
							9 => array( array( array( $this, '_loginUser' ), array( $context ) ) ), // 9 is weight
						),
						'#submit_id' => 'login',
						'#value'     => 'login',
						'#weight'    => 1,
					),
				),
			);
			if ( ! isset( $login_config['lost_pass_link'] )
				|| ! empty( $login_config['lost_pass_link'] )
			) {
				$login_links                     = array(
					(string) $this->LinkTo(
						__( 'Lost your password?', 'directories-pro' ),
						'/' . $this->getComponent( 'FrontendSubmit' )->getSlug( 'login' ) . '/lost_password',
						array(
							'container' => $this->Filter( 'frontendsubmit_lost_password_form_container', 'modal' ),
							'modalSize' => 'lg',
						)
					),
				);
				$form['login']['login']['links'] = array(
					'#type'   => 'markup',
					'#value'  => '<span class="' . DRTS_BS_PREFIX . 'mx-2">' . implode( '&nbsp|&nbsp', $login_links ) . '</span>',
					'#weight' => 3,
				);
			}

			if ( $this->FrontendSubmit_Oauth_isEnabled() ) {
				$form['login']['oauth'] = array(
					'#type'   => 'markup',
					'#markup' => '<div class="drts-frontendsubmit-login-oauth ' . DRTS_BS_PREFIX . 'mt-5">' . $this->FrontendSubmit_Oauth_buttons( $context->getRequest()->getParams() ) . '</div>',
					'#weight' => 100,
				);
			}

			// Allow modifying login form
			$form['login'] = $this->Filter( 'frontendsubmit_login_form', $form['login'] );
		} else {
			$form['login'] = array(
				'#type'   => 'markup',
				'#value'  => '&nbsp;<a class="' . DRTS_BS_PREFIX . 'btn ' . DRTS_BS_PREFIX . 'btn-primary" href="' . $this->getPlatform()->getLoginUrl( $this->_getRedirectUrl( $redirect_to ) ) . '">'
					. $this->H( __( 'Login to continue', 'directories-pro' ) ) . '</a>',
				'#weight' => 1,
			);
		}

		if ( $this->getPlatform()->isUserRegisterable() ) {
			if ( $show_register_form ) {
				if ( ! $show_register_form_separate_page ) {
					$form['register'] = array(
						'#tree'            => true,
						'#weight'          => 2,
						'#submit_for'      => 'register',
						'username'         => array(
							'#type'     => 'textfield',
							'#title'    => __( 'Username', 'directories-pro' ),
							'#weight'   => FrontendSubmitComponent::REGISTER_FORM_FIELD_WEIGHT_USER_NAME,
							'#required' => true,
						),
						'email'            => array(
							'#type'     => 'email',
							'#title'    => __( 'E-mail Address', 'directories-pro' ),
							'#weight'   => FrontendSubmitComponent::REGISTER_FORM_FIELD_WEIGHT_EMAIL,
							'#required' => true,
						),
						'password'         => array(
							'#type'     => 'password',
							'#title'    => __( 'Password', 'directories-pro' ),
							'#weight'   => FrontendSubmitComponent::REGISTER_FORM_FIELD_WEIGHT_PASSWORD,
							'#required' => true,
						),
						'password_confirm' => array(
							'#type'     => 'password',
							'#title'    => __( 'Confirm Password', 'directories-pro' ),
							'#weight'   => FrontendSubmitComponent::REGISTER_FORM_FIELD_WEIGHT_PASSWORD,
							'#required' => true,
						),
						'register'         => array(
							'#weight' => FrontendSubmitComponent::REGISTER_FORM_FIELD_WEIGHT_SUBMIT,
							'#class'  => DRTS_BS_PREFIX . 'd-flex ' . DRTS_BS_PREFIX . 'flex-row ' . DRTS_BS_PREFIX . 'align-items-center',
							'#group'  => true,
							'submit'  => array(
								'#type'      => 'submit',
								'#btn_label' => __( 'Register', 'directories-pro' ),
								'#btn_color' => 'primary',
								'#btn_size'  => 'lg',
								'#submit'    => array(
									9 => array( array( array( $this, '_registerUser' ), array( $context ) ) ), // 9 is weight
								),
								'#submit_id' => 'register',
								'#value'     => 'register',
								'#weight'    => 1,
							),
						),
					);
					if ( $this->getComponent( 'FrontendSubmit' )->getConfig( 'register', 'privacy' )
						&& ( $privacy_policy_link = $this->getPlatform()->getPrivacyPolicyLink() )
					) {
						$form['register']['privacy_policy'] = array(
							'#weight'                 => FrontendSubmitComponent::REGISTER_FORM_FIELD_WEIGHT_PRIVACY_POLICY,
							'#type'                   => 'checkbox',
							'#switch'                 => false,
							'#title_no_escape'        => true,
							'#title'                  => $this->_getPrivacyPolicyCheckboxLabel( $privacy_policy_link, 'register' ),
							'#required'               => true,
							'#required_error_message' => __( 'You must agree to continue.', 'directories-pro' ),
						);
					}

					// Allow modifying register form
					$form['register'] = $this->Filter( 'frontendsubmit_register_form', $form['register'] );

				} elseif ( $this->getComponent( 'FrontendSubmit' )->getConfig( 'register', 'show_separate_page_link' ) ) {
						$form['register'] = array(
							'#type'   => 'markup',
							'#value'  => '&nbsp;<a class="' . DRTS_BS_PREFIX . 'btn ' . DRTS_BS_PREFIX . 'btn-primary" href="' . $this->Url( '/' . $this->getComponent( 'FrontendSubmit' )->getSlug( 'login' ) . '/register', isset( $redirect_to ) ? array( 'redirect_to' => $redirect_to ) : array() ) . '">'
								. $this->H( __( 'Register an account', 'directories-pro' ) ) . '</a>',
							'#weight' => 1,
						);
				}
			} elseif ( $this->getComponent( 'FrontendSubmit' )->getConfig( 'register', 'show_link' ) ) {
					$form['register'] = array(
						'#type'   => 'markup',
						'#value'  => '&nbsp;<a class="' . DRTS_BS_PREFIX . 'btn ' . DRTS_BS_PREFIX . 'btn-primary" href="' . $this->getPlatform()->getRegisterUrl( $this->_getRedirectUrl( $redirect_to ) ) . '">'
							. $this->H( __( 'Register an account', 'directories-pro' ) ) . '</a>',
						'#weight' => 1,
					);
			}
		} elseif ( $show_register_form ) {
				$form['register'] = array(
					'#type'   => 'item',
					'#markup' => '<div class="' . DRTS_BS_PREFIX . 'alert ' . DRTS_BS_PREFIX . 'alert-warning">'
						. __( 'User registration is currently not allowed.', 'directories-pro' ) . '</div>',
				);
		}

		$context->label_login    = __( 'Login', 'directories-pro' );
		$context->label_register = __( 'Register', 'directories-pro' );

		if ( ! empty( $redirect_to ) ) {
			$form['redirect_to'] = array(
				'#type'  => 'hidden',
				'#value' => $redirect_to,
			);
			if ( isset( $this->_bundle )
				&& isset( $this->_action )
				&& $this->Filter(
					'frontendsubmit_guest_allowed',
					true,
					array( $this->_bundle, $this->_action )
				)
				&& ( is_string( $this->_bundle ) // should have at least one bundle allowed if string (bundle type) given
					|| $this->_action !== 'add' // currently can only check for add action
					|| $this->HasPermission( 'entity_create_' . $this->_bundle->name )
				)
			) {
				$form['guest'] = array(
					'#tree'       => true,
					'#weight'     => 3,
					'#submit_for' => 'guest',
					'continue'    => array(
						'#type'      => 'submit',
						'#btn_label' => __( 'Continue as guest', 'directories-pro' ) . '',
						'#btn_color' => 'primary',
						'#btn_size'  => 'lg',
						'#value'     => 'continue',
						'#weight'    => 99,
						'#submit'    => array(
							9 => array( array( array( $this, '_continue' ), array( $context ) ) ), // 9 is weight
						),
						'#submit_id' => 'guest',
					),
				);
				$guest_config  = $this->getComponent( 'FrontendSubmit' )->getConfig( 'guest' );
				if ( ! empty( $guest_config['collect_name'] ) ) {
					$form['guest']['name'] = array(
						'#type'          => 'textfield',
						'#title'         => __( 'Your Name', 'directories-pro' ),
						'#weight'        => 1,
						'#default_value' => null,
						'#max_length'    => 255,
						'#required'      => ! empty( $guest_config['require_name'] ),
					);
				}
				if ( ! empty( $guest_config['collect_email'] ) ) {
					$form['guest']['email'] = array(
						'#type'          => 'email',
						'#title'         => __( 'E-mail Address', 'directories-pro' ),
						'#weight'        => 3,
						'#default_value' => ( $email = $this->Dashboard_GuestAccess_isGranted() ) ? $email : null,
						'#check_mx'      => ! empty( $guest_config['check_mx'] ),
						'#check_exists'  => ! empty( $guest_config['check_exists'] ),
						'#max_length'    => 255,
						'#required'      => ! empty( $guest_config['require_email'] ),
					);
				}
				if ( ! empty( $guest_config['collect_url'] ) ) {
					$form['guest']['url'] = array(
						'#type'          => 'url',
						'#title'         => __( 'Website URL', 'directories-pro' ),
						'#weight'        => 5,
						'#default_value' => null,
						'#max_length'    => 255,
						'#required'      => ! empty( $guest_config['require_url'] ),
					);
				}
				if ( ! empty( $guest_config['collect_privacy'] )
					&& ( $privacy_policy_link = $this->getPlatform()->getPrivacyPolicyLink() )
				) {
					$form['guest']['privacy_policy'] = array(
						'#weight'                 => 95,
						'#type'                   => 'checkbox',
						'#switch'                 => false,
						'#title_no_escape'        => true,
						'#title'                  => $this->_getPrivacyPolicyCheckboxLabel( $privacy_policy_link, 'guest' ),
						'#required'               => true,
						'#required_error_message' => __( 'You must agree to continue.', 'directories-pro' ),
					);
				}
				$context->label_guest = __( 'Continue as guest', 'directories-pro' );
			} elseif ( $this->_application->Dashboard_GuestAccess_isEnabled() ) {
					$form['guest']        = array(
						'#tree'                  => true,
						'#weight'                => 3,
						'#submit_for'            => 'dashboard_guest_access',
						'dashboard_guest_access' => array(
							'#type'      => 'submit',
							'#btn_label' => __( 'Request guest access', 'directories-pro' ) . '',
							'#btn_color' => 'primary',
							'#btn_size'  => 'lg',
							'#value'     => 'dashboard_guest_access',
							'#weight'    => 99,
							'#submit'    => array(
								9 => array( array( array( $this, '_requestGuestAccess' ), array( $context ) ) ), // 9 is weight
							),
							'#submit_id' => 'dashboard_guest_access',
						),
					) + $this->Dashboard_GuestAccess_requestActivationForm();
					$context->label_guest = __( 'Request guest access', 'directories-pro' );
			}
		}

		$context->addTemplate( $this->_template );

		return $form;
	}

	protected function _isShowLoginForm() {
		return $this->getComponent( 'FrontendSubmit' )->isLoginFormEnabled();
	}

	protected function _isShowRegisterForm() {
		return $this->getComponent( 'FrontendSubmit' )->isRegisterFormEnabled();
	}

	public function _requestGuestAccess( Form\Form $form, Context $context ) {
		$this->Dashboard_GuestAccess_requestActivation( $form->values['guest'] );
		$context->setSuccess( $this->Url( '/' . $this->getComponent( 'FrontendSubmit' )->getSlug( 'login' ), empty( $form->values['redirect_to'] ) ? array() : array( 'redirect_to' => $form->values['redirect_to'] ) ) )
			->addFlash( __( 'Guest access URL has been sent to your e-mail address.', 'directories-pro' ), 'success', null, 'top' );
	}


	protected function _getPrivacyPolicyCheckboxLabel( $link, $type ) {
		if ( ! $label = $this->Filter( 'frontendsubmit_privacy_policy_check_label', '', array( $link, $type ) ) ) {
			$label = sprintf( $this->H( __( 'I have read and agree to the %s', 'directories-pro' ) ), $link );
		}
		return $label;
	}

	public function _registerUser( Form\Form $form, Context $context ) {
		if ( $form->values['register']['password'] !== $form->values['register']['password_confirm'] ) {
			$form->setError( __( 'Passwords do not match.', 'directories-pro' ) );
			return;
		}

		try {
			$identity = $this->_doRegisterUser(
				$form->values['register']['username'],
				$form->values['register']['email'],
				$form->values['register']['password'],
				$form->values['register']
			);
		} catch ( Exception\RuntimeException $e ) {
			$form->setError( strip_tags( $e->getMessage() ), 'register' );
			return;
		}

		$redirect_to = empty( $form->values['redirect_to'] ) ? null : $form->values['redirect_to'];

		if ( $this->getComponent( 'FrontendSubmit' )->getConfig( 'register', 'verify_email' )
			&& $this->FrontendSubmit_VerifyAccount_isRequiredByEmail( $identity->email )
		) {
			$register_success_msg = __( 'Your account has been created successfully.', 'directories-pro' );
			if ( $register_success_msg = $this->Filter( 'frontendsubmit_register_success_message', $register_success_msg, array( $identity ) ) ) {
				$context->addFlash( $register_success_msg, 'success', null, 'top' );
			}
			$context->setSuccess( $this->Filter( 'frontendsubmit_register_need_verify_url', '/' . $this->getComponent( 'FrontendSubmit' )->getSlug( 'login' ), array( $identity ) ) );
			try {
				$this->FrontendSubmit_VerifyAccount_unverify( $identity );
				if ( $register_need_verify_msg = $this->Filter( 'frontendsubmit_register_need_verify_message', __( 'Please check your email address to verify your account.', 'directories-pro' ), array( $identity ) ) ) {
					$context->addFlash( $register_need_verify_msg, 'success', null, 'top' );
				}
			} catch ( Exception\IException $e ) {
				$context->addFlash( $e->getMessage(), 'danger', null, 'top' );
			}
			if ( $redirect_url = $this->_getRedirectUrl( $redirect_to, array(), 'register', $identity, false ) ) {
				$this->getPlatform()->setEntityMeta(
					'user',
					$identity->id,
					'frontendsubmit_verify_account_redirect',
					serialize(
						is_string( $redirect_url ) ? $redirect_url : array(
							'route'  => $redirect_url->route,
							'params' => $redirect_url->params,
						)
					)
				);
			}
			return;
		}

		// Redirect to login page if setting current user failed for some reason
		if ( ! $this->getPlatform()->setCurrentUser( $identity->id ) ) {
			$redirect_url = '/' . $this->getComponent( 'FrontendSubmit' )->getSlug( 'login' );
		} else {
			$redirect_url = $this->_getRedirectUrl( $redirect_to, array(), 'register', $identity );
		}

		$context->setSuccess( $redirect_url );
	}

	protected function _doRegisterUser( $username, $email, $password = null, array $params = array(), $changeUsernameIfExists = false ) {
		if ( $changeUsernameIfExists ) {
			while ( $this->getPlatform()->userExists( $username ) ) {
				$username .= '_' . rand( 1, 999 );
			}
		}
		$user_id = $this->getPlatform()->registerUser( $username, $email, $password, $params );
		if ( ! $identity = $this->UserIdentity( $user_id ) ) {
			throw new Exception\RuntimeException( sprintf( __( 'User with user ID %d does not exist.', 'directories-pro' ), $user_id ) );
		}
		$this->Action( 'frontendsubmit_register', array( $identity, $params ) );
		return $identity;
	}

	public function _loginUser( Form\Form $form, Context $context ) {
		$login_name   = $form->values['login']['username'];
		$login_config = $this->getComponent( 'FrontendSubmit' )->getConfig( 'login' );
		if ( ! empty( $login_config['allow_email'] ) ) {
			// E-mail address allowed
			if ( isset( $login_config['allow_username'] )
				&& empty( $login_config['allow_username'] )
			) {
				// Only e-mail address is allowed
				if ( ! filter_var( $login_name, FILTER_VALIDATE_EMAIL ) ) {
					$form->setError( __( 'Please enter a valid e-mail address.', 'directories-pro' ), 'login[username]' );
					return;
				}
			}
		} else {
			// Only username allowed
			if ( filter_var( $login_name, FILTER_VALIDATE_EMAIL ) ) {
				$form->setError( __( 'Please enter a valid username.', 'directories-pro' ), 'login[username]' );
				return;
			}
		}
		try {
			$user_id = $this->getPlatform()->loginUser(
				$login_name,
				$form->values['login']['password'],
				! empty( $form->values['login']['remember'] ),
				$form->values['login']
			);
			if ( ! $identity = $this->UserIdentity( $user_id ) ) {
				throw new Exception\RuntimeException( sprintf( __( 'User with user ID %d does not exist.', 'directories-pro' ), $user_id ) );
			}
			$this->Action( 'frontendsubmit_login', array( $identity, $form->values ) );
		} catch ( Exception\RuntimeException $e ) {
			$form->setError( strip_tags( $e->getMessage() ), 'login' );
			return;
		}

		if ( $this->getComponent( 'FrontendSubmit' )->getConfig( 'register', 'verify_email' )
			&& $this->FrontendSubmit_VerifyAccount_isRequired( $identity->id )
		) {
			$this->getPlatform()->logoutUser();
			$form->setError(
				sprintf(
					__( 'Your account requires verification. Please check your email address to verify your account or click <a href="%s">here</a> to resend a verification email.', 'directories-pro' ),
					$this->FrontendSubmit_VerifyAccount_resendKeyUrl( $identity )
				)
			);
			return;
		}

		$context->setSuccess( $this->_getRedirectUrl( empty( $form->values['redirect_to'] ) ? null : $form->values['redirect_to'], array(), 'login', $identity ) );
	}

	public function _continue( Form\Form $form, Context $context ) {
		$guest_info = array();
		foreach ( array( 'name', 'email', 'url' ) as $key ) {
			if ( isset( $form->values['guest'][ $key ] ) ) {
				$guest_info[ $key ] = $form->values['guest'][ $key ];
			}
		}
		$redirect_url = $this->_getRedirectUrl(
			empty( $form->values['redirect_to'] ) ? null : $form->values['redirect_to'],
			array( '_guest' => empty( $guest_info ) ? 1 : $guest_info ),
			'guest'
		);
		$context->setSuccess( $redirect_url );
	}

	protected function _oauth( Context $context, $redirectTo ) {
		if ( ! $this->FrontendSubmit_Oauth_isEnabled()
			|| ( ! $provider_name = $context->getRequest()->asStr( 'provider' ) )
		) {
			$context->setNotFoundError();
			return;
		}

		// Get provider and scopes
		$redirect_url = (string) $this->Url(
			$context->getRoute(),
			array(
				'oauth'    => 1,
				'provider' => $provider_name,
			),
			'',
			'&'
		);
		try {
			list($provider, $scopes) = $this->FrontendSubmit_Oauth_provider( $provider_name, $redirect_url );
		} catch ( Exception\InvalidArgumentException $e ) {
			$context->setNotFoundError();
			return;
		}

		// Redirect to provider if no code
		if ( ! $code = $context->getRequest()->asStr( 'code' ) ) {
			$redirect_url = $provider->getAuthorizationUrl( array( 'scope' => $scopes ) ); // must get authorization URL first to get state
			$this->getPlatform()->setSessionVar( 'frontendsubmit_oauth_state', $provider->getState() )
				->setSessionVar( 'frontendsubmit_oauth_redirect_url', (string) $this->_getRedirectUrl( $redirectTo ) );
			$context->setRedirect( $redirect_url );
			return;
		}

		// Validate state
		if ( ( ! $state = $context->getRequest()->asStr( 'state' ) )
			|| $state !== $this->getPlatform()->getSessionVar( 'frontendsubmit_oauth_state' )
		) {
			$context->setForbiddenError();
			return;
		}

		// Get resource owner
		$token          = $provider->getAccessToken( 'authorization_code', array( 'code' => $code ) );
		$resource_owner = $provider->getResourceOwner( $token );

		// Fetch or register user
		$meta_value = $provider_name . '-' . $resource_owner->getId();
		if ( ! $users = $this->getPlatform()->getUsersByMeta( 'frontendsubmit_oauth_user_id', $meta_value, 1 ) ) {
			$user_data = $this->FrontendSubmit_Oauth_getUserData( $provider_name, $resource_owner );
			$identity  = $this->_doRegisterUser( $user_data['name'], $user_data['email'], null, array(), true );
			$this->getPlatform()->setEntityMeta( 'user', $identity->id, 'frontendsubmit_oauth_user_id', $meta_value );
		} else {
			$identity = array_shift( $users );
		}

		// Redirect to login page if setting current user failed for some reason
		$redirect_url = $this->getPlatform()->getSessionVar( 'frontendsubmit_oauth_redirect_url' ); // need to get this before setting current user
		if ( ! $this->getPlatform()->setCurrentUser( $identity->id ) ) {
			throw new Exception\RuntimeException( __( 'An unexpected failure occurred while logging in.', 'directories-pro' ) );
		}

		$context->setRedirect( $redirect_url ?: $this->_getDefaultRedirectUrl() );
	}

	protected function _getRedirectUrl( $url, array $params = array(), $type = 'login', ?AbstractIdentity $identity = null, $returnDefault = true ) {
		if ( isset( $this->_bundle )
			&& isset( $this->_action )
		) {
			if ( ! isset( $this->_entity ) ) {
				switch ( $this->_action ) {
					case 'add':
						if ( is_string( $this->_bundle ) ) {
							if ( ( ! $component_name = $this->Entity_BundleTypes( $this->_bundle ) )
								|| ! $this->isComponentLoaded( $component_name )
							) {
								break;
							}

							$bundle_type = $this->_bundle;
						} else {
							$params['bundle'] = $this->_bundle->name;
							$bundle_type      = $this->_bundle->type;
						}
						$url = $this->Url( '/' . $this->FrontendSubmit_AddEntitySlug( $bundle_type ), $params + $this->_params, '', '&' );
				}
			} else {
				switch ( $this->_action ) {
					case 'add':
						$url = $this->Url( '/' . $this->FrontendSubmit_AddEntitySlug( $this->_bundle ), $params + $this->_params, '', '&' );
				}
			}
		}

		if ( ( ! $url = $this->Filter( 'frontendsubmit_after_login_register_url', $url, array( $type, $identity ) ) )
			&& $returnDefault
		) {
			$url = $this->_getDefaultRedirectUrl( $type, $identity );
		}

		return $url;
	}

	protected function _getDefaultRedirectUrl( $type = 'login', ?AbstractIdentity $identity = null ) {
		if ( $type === 'login'
			&& isset( $identity )
			&& $this->getPlatform()->isAdministrator( $identity->id )
		) {
			$url = rtrim( $this->getPlatform()->getSiteAdminUrl(), '/' ) . '/';
		} elseif ( $this->isComponentLoaded( 'Dashboard' )
			&& ( $dashboard_slug = $this->getComponent( 'Dashboard' )->getSlug( 'dashboard' ) )
		) {
			$url = (string) $this->Url( '/' . $dashboard_slug );
		} else {
			$url = rtrim( $this->getPlatform()->getSiteUrl(), '/' ) . '/';
		}

		return $url;
	}
}
