<?php
/**
 * Social Connection Handler
 *
 * @package um_ext\um_social_login\core\Social_Login_Connect
 */

namespace um_ext\um_social_login\core;

use phpDocumentor\Reflection\DocBlock\Tags\Var_;

// Include Composer's autoloader.
require_once UM_SOCIAL_LOGIN_PATH . 'vendor/autoload.php';

use Hybridauth\Hybridauth;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Class Social_Login_Connect
 *
 * @package um_ext\um_social_login\core
 */
class Social_Login_Connect {

	/**
	 * Network Providers
	 *
	 * @var $networks
	 */
	public $networks;

	/**
	 * Form ID
	 *
	 * @var $form_id
	 */
	public $form_id;

	/**
	 * User Profile
	 *
	 * @var $user_profile
	 */
	public $user_profile;

	/**
	 * Current Provider
	 *
	 * @var $current_provider
	 */
	public $current_provider;

	/**
	 *  HybridAuth class
	 *
	 * @var $hybridauth
	 */
	public $hybridauth;

	/**
	 * HybridAuth Response
	 *
	 * @var $oauth_response
	 */
	public $oauth_response;

	/**
	 *  SSO Action
	 *
	 *  @var $do_action
	 */
	public $do_action = '';

	/**
	 * A provider for the overlay if needed.
	 *
	 * @since 2.5.3
	 *
	 * @var string
	 */
	public $if_overlay_provider;

	/**
	 * Storage Session
	 *
	 * @var $session
	 */
	public $session = null;

	/**
	 * Has Session storage loaded
	 *
	 * @var string $session_has_init
	 */
	public $session_has_init = 0;

	/**
	 * Init
	 */
	public function __construct() {

		//phpcs:disable WordPress.Security.NonceVerification
		if ( isset( $_REQUEST['um_form_id'] ) && isset( $_REQUEST['message'] ) ) {
			return;
		}
		//phpcs:enable WordPress.Security.NonceVerification

		if ( wp_doing_cron() ) {
			return;
		}

		if ( is_admin() ) {
			return;
		}

		if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {
			return;
		}

		if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
			return;
		}

		// Fix compatibility issue with WooCommerce custom ajax endpoint.
		if ( defined( 'WC_DOING_AJAX' ) && \WC_DOING_AJAX ) {
			return;
		}

		add_action( 'template_redirect', array( &$this, 'init' ), 1 ); // UM Form init's priority is set to 2.

		add_action( 'wp_footer', array( &$this, 'load_overlay' ), 10, 1 );
	}

	/**
	 * Init
	 */
	public function init() {

		++$this->session_has_init;

		$um_sso_session = UM()->Social_Login_API()->hybridauth()->session();

		if ( ! $um_sso_session ) {
			$response = array(
				'Error:' => 'Session is disabled',
				'type:'  => 'Social Login session error ',
			);
			UM()->Social_Login_API()->hybridauth()->log_error( implode( "\n", $response ) );

			return;
		}

		//phpcs:disable WordPress.Security.NonceVerification
		if ( isset( $_REQUEST['provider'] ) ) {

			$provider = sanitize_key( $_REQUEST['provider'] );

			if ( ! defined( 'UM_SSO_CHILD_WINDOW' ) ) {
				define( 'UM_SSO_CHILD_WINDOW', $provider );
			}

			if ( ! defined( 'UM_SSO_WINDOW' ) ) {
				define( 'UM_SSO_WINDOW', 'child' );
			}
		} elseif ( isset( $_REQUEST['return_provider'] ) ) {

			$provider = sanitize_key( $_REQUEST['return_provider'] );

			if ( ! defined( 'UM_SSO_PARENT_WINDOW' ) ) {
				define( 'UM_SSO_PARENT_WINDOW', $provider );
			}

			if ( ! defined( 'UM_SSO_WINDOW' ) ) {
				define( 'UM_SSO_WINDOW', 'parent' );
			}
		} else {
			$provider = $um_sso_session->get( 'sso_last_auth_provider' );
		}

		if ( empty( $provider ) || is_admin() || isset( $_REQUEST['err'] ) ) {
			unset( $_SESSION['HYBRIDAUTH::STORAGE'] );
			return;
		}
		//phpcs:enable WordPress.Security.NonceVerification

		if ( is_user_logged_in() ) {
			do_action( 'um_social_do_redirect_after_login', $provider );
		}

		$this->oauth_response = UM()->Social_Login_API()->hybridauth()->connect_user( $provider, '', $um_sso_session, $this->session_has_init );
		$this->hybridauth     = isset( $this->oauth_response['hybridauth'] ) ? $this->oauth_response['hybridauth'] : '';
		//phpcs:disable WordPress.Security.NonceVerification
		// Disconnect from adapters.
		if ( ! defined( 'UM_SSO_CHILD_WINDOW' ) && ! isset( $_REQUEST['provider'] ) && ! isset( $_REQUEST['return_provider'] ) && ! empty( $this->hybridauth ) ) {
			if ( method_exists( $this->hybridauth, 'disconnect' ) ) {
				$this->hybridauth->disconnect();
				return;
			}
		}

		if ( method_exists( $this->hybridauth, 'getAccessToken' ) && empty( $this->hybridauth->getAccessToken() ) && ! empty( $um_sso_session->get( 'um_sso_has_authenticated' ) ) ) {
			$um_sso_session->delete( 'um_sso_has_authenticated' );
		}

		if ( isset( $_REQUEST['provider'] ) && ! empty( $_REQUEST['provider'] ) || isset( $_REQUEST['return_provider'] ) && ! empty( $_REQUEST['return_provider'] ) || isset( $_REQUEST['code'] ) ) {
			if ( isset( $this->oauth_response['has_errors'] ) && defined( 'UM_SSO_CHILD_WINDOW' ) ) {
				do_action( 'um_social_oauth_window_process_error', $provider, $this->oauth_response['returnUrl'], $this->oauth_response, $this->hybridauth );
				do_action( "um_social_oauth_window_process_error__{$provider}", $this->oauth_response['returnUrl'], $this->oauth_response, $this->hybridauth );
			}

			if ( isset( $_REQUEST['oauthWindow'] ) && defined( 'UM_SSO_CHILD_WINDOW' ) ) {

				if ( isset( $this->oauth_response['userProfile'] ) ) {

					do_action( 'um_social_doing_oauth_window_process', $provider, $this->oauth_response['returnUrl'], $this->oauth_response, $this->hybridauth );
					do_action( "um_social_doing_oauth_window_process__{$provider}", $this->oauth_response['returnUrl'], $this->oauth_response, $this->hybridauth );
				}
			} elseif ( isset( $this->oauth_response['userProfile'] ) ) {
				$this->user_profile = $this->oauth_response['userProfile'];

				if ( defined( 'UM_SSO_CHILD_WINDOW' ) ) { // Authenticate process in Child Window.

					do_action( "um_social_do_oauth_window_process__{$provider}", $this->oauth_response['returnUrl'], $this->oauth_response, $this->hybridauth );
					do_action( 'um_social_do_oauth_window_process', $provider, $this->oauth_response['returnUrl'], $this->oauth_response, $this->hybridauth );
				} elseif ( defined( 'UM_SSO_PARENT_WINDOW' ) ) {
					do_action( 'um_social_do_authenticated_process', $provider, $this->oauth_response['returnUrl'], $this->oauth_response, $this->hybridauth );
				}

				$has_linked = $this->has_account_linked( $provider, $this->oauth_response['userProfile'] );

				do_action( 'um_social_doing_shortcode', $provider, $has_linked, $this->user_profile, $this, $this->oauth_response['returnUrl'], $this->hybridauth );
				do_action( "um_social_doing_shortcode__{$provider}", $has_linked, $this->user_profile, $this, $this->oauth_response['returnUrl'], $this->hybridauth );

				if ( ! is_user_logged_in() ) {

					$has_linked = $this->has_account_linked( $provider, $this->oauth_response['userProfile'] );

					// Login.
					if ( ( um_is_core_page( 'login' ) && empty( $this->do_action ) ) || in_array( $this->do_action, array( 'login', 'login_register' ), true ) ) {

						if ( $has_linked ) {
							do_action( 'um_social_do_login', $provider, $has_linked, $this->user_profile, $this, $this->oauth_response['returnUrl'], $this->hybridauth );
							do_action( "um_social_do_login__{$provider}", $has_linked, $this->user_profile, $this, $this->oauth_response['returnUrl'], $this->hybridauth );
						} else {
							do_action( 'um_social_do_login_error', $provider, $this->user_profile, $this->oauth_response['returnUrl'], $this->hybridauth );
							do_action( "um_social_do_login_error__{$provider}", $this->user_profile, $this->oauth_response['returnUrl'], $this->hybridauth );
						}
						$um_sso_session->delete( 'sso_last_auth_provider' );
						$um_sso_session->delete( 'um_sso_has_authenticated' );
						exit;
						// Register.
					} elseif ( ( um_is_core_page( 'register' ) && empty( $this->do_action ) ) || in_array( $this->do_action, array( 'register', 'login_register' ), true ) || ( ! um_is_core_page( 'login' ) && defined( 'UM_SSO_PARENT_WINDOW' ) && ( $um_sso_session->get( 'sso_last_auth_provider' ) || isset( $_REQUEST['form_id'] ) ) ) ) {

						$has_linked = $this->has_account_linked( $provider, $this->oauth_response['userProfile'] );

						do_action( 'um_social_do_register_authenticated_process', $provider, $this->oauth_response['returnUrl'], $this->user_profile, $this, $has_linked, $this->hybridauth );

						if ( $has_linked ) {
							do_action( 'um_social_do_register_error', $provider, $this->user_profile, $this, $this->oauth_response['returnUrl'], $this->hybridauth );
							do_action( "um_social_do_register_error__{$provider}", $this->user_profile, $this, $this->oauth_response['returnUrl'], $this->hybridauth );
						} else {
							$um_sso_session = UM()->Social_Login_API()->hybridauth()->session();
							$um_sso_session->set( 'oauth_response', $this->oauth_response );

							$this->if_overlay_provider = $provider;

							do_action( 'um_social_doing_register', $provider, $this->user_profile, $this, $this->oauth_response['returnUrl'], $this->hybridauth );
							do_action( "um_social_doing_register__{$provider}", $this->user_profile, $this, $this->oauth_response['returnUrl'], $this->hybridauth );
						}
					}
				} elseif ( is_user_logged_in() || ( 'link_account' === $this->do_action ) ) { // Social Account or Custom Page.

					if ( um_is_core_page( 'login' ) || um_is_core_page( 'register' ) ) {
						return;
					}

					$has_linked = $this->has_account_linked( $provider, $this->oauth_response['userProfile'] );

					if ( ! $has_linked && ! isset( $_REQUEST['err'] ) ) {

						do_action( 'um_social_do_link_user', $provider, $this->user_profile, $this->oauth_response['returnUrl'], $has_linked, $this->hybridauth );
						do_action( "um_social_do_link_user__{$provider}", $this->user_profile, $this->oauth_response['returnUrl'], $has_linked, $this->hybridauth );

					} elseif ( $has_linked && ! isset( $_REQUEST['err'] ) ) {

						$um_sso_session->delete( 'sso_last_auth_provider' );

						do_action( 'um_social_do_link_user_error', $provider, $this->user_profile, $this->oauth_response['returnUrl'], $has_linked, $this->hybridauth );
						do_action( "um_social_do_link_user_error__{$provider}", $this->user_profile, $this->oauth_response['returnUrl'], $has_linked, $this->hybridauth );

					}
				}
			}
		} elseif ( isset( $_SESSION['um_social_profile'] ) ) {
				unset( $_SESSION['um_social_profile'] );
		}
		//phpcs:enable WordPress.Security.NonceVerification
	}


	/**
	 * Has account linked to provider
	 *
	 * @param string $provider Network Provider slug.
	 * @param object $obj_user_profile User Profile.
	 *
	 * @return boolean
	 */
	public function has_account_linked( $provider, $obj_user_profile ) {

		global $wpdb;

		if ( isset( $obj_user_profile->identifier ) && ! empty( $obj_user_profile->identifier ) ) {
			$user = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->usermeta} WHERE meta_key = %s AND  meta_value = %s", "_uid_{$provider}", $obj_user_profile->identifier ) ); //phpcs:ignore
		}

		$automatic_login_email_exists = apply_filters( 'um_sso_automatic_login_with_email', true );
		if ( ! is_user_logged_in() && $automatic_login_email_exists && ! $user && isset( $obj_user_profile->email ) && ! empty( $obj_user_profile->email ) ) {

			$user = $wpdb->get_row( $wpdb->prepare( "SELECT ID as user_id FROM {$wpdb->users} WHERE user_email = %s ", $obj_user_profile->email ) ); //phpcs:ignore

			$profile = array( 'email_exists' => $obj_user_profile->email );

			$email_exists = UM()->Social_Login_API()->user_connect()->email_exists( $profile, $provider );

			UM()->Social_Login_API()->user_connect()->save_user_meta(
				$email_exists,
				array(
					"_uid_{$provider}" => $obj_user_profile->identifier,
				),
				$provider
			);
		}

		if ( isset( $user->user_id ) ) {
			return $user->user_id;
		}

		return false;
	}

	/**
	 * Is connected
	 *
	 * @param integer $user_id User ID.
	 * @param string  $provider Provider slug.
	 *
	 * @return bool
	 */
	public function is_connected( $user_id, $provider ) {
		$connection = get_user_meta( $user_id, '_uid_' . $provider, true );
		if ( $connection ) {
			return true;
		}
		return false;
	}


	/**
	 * Load overlay if needed.
	 *
	 * @since 2.5.3
	 */
	public function load_overlay() {
		if ( ! empty( $this->if_overlay_provider ) ) {
			$provider = $this->if_overlay_provider;
			$this->sync_fields( null, $provider );
			$this->show_overlay();
			$this->load_overlay_assets();
			// Avoid duplicate loading of overlay. It was an issue prior to 2.x.
			remove_action( 'wp_footer', array( &$this, 'load_overlay' ), 10, 1 );
		}
	}


	/**
	 * Load overlay assets
	 */
	public function load_overlay_assets() {
		wp_enqueue_script( 'um-social-login' );
		wp_enqueue_style( 'um-social-login' );
	}

	/**
	 * Get assigned register form to overlay
	 *
	 * @return integer
	 */
	public function form_id() {
		$assigned_form_id = absint( get_option( 'um_social_login_form_installed' ) );

		return apply_filters( 'um_social_login_assigned_form_id', $assigned_form_id );
	}

	/**
	 * Show overlay
	 */
	public function show_overlay() {

		remove_action( 'um_before_register_fields', 'um_social_login_add_buttons', 10 );

		wp_enqueue_script( 'um-social-login' );
		wp_enqueue_style( 'um-social-login' );

		do_action( 'um_social_login_before_show_overlay' );

		add_filter( 'um_ultimatemember_shortcode_disable_singleton', '__return_false' );

		$this->form_id = $this->form_id();

		$step_process = absint( $this->get_enabled_step_process( $this->form_id ) );

		if ( 1 === $step_process || '' === $step_process ) {
			$tpl = 'form';
		} else {
			$tpl = 'pre-loader-form';
		}

		$current_url = UM()->Social_Login_API()->hybridauth()->get_current_url();
		UM()->get_template(
			"{$tpl}.php",
			UM_SOCIAL_LOGIN_PLUGIN,
			array(
				'current_url'    => $current_url,
				'um_sso_form_id' => $this->form_id,
			),
			true
		);
	}


	/**
	 * Sync fields
	 *
	 * @param integer $um_sso_form_id Form ID.
	 * @param string  $provider Provider Slug.
	 */
	public function sync_fields( $um_sso_form_id = null, $provider = '' ) {

		if ( ! $um_sso_form_id ) {
			$um_sso_form_id = $this->form_id();
		}

		$fields   = UM()->query()->get_attr( 'custom_fields', $um_sso_form_id );
		$profile  = $this->user_profile;
		$provider = isset( $_REQUEST['return_provider'] ) ? sanitize_key( $_REQUEST['return_provider'] ) : ''; //phpcs:ignore WordPress.Security.NonceVerification

		foreach ( $fields as $key => $field ) {

			if ( ! empty( $field['sso_sync_value'] ) ) {

				if ( 'extend' === $field['sso_sync_value'] ) {
					$_SESSION['um_social_profile'][ $key ] = apply_filters( "um_social_profile__custom_data_{$key}", '', $profile, $um_sso_form_id, $fields );
				} elseif ( isset( $profile->{$field['sso_sync_value']} ) ) {

					$_SESSION['um_social_profile'][ $key ] = $profile->{$field['sso_sync_value']};

					if ( in_array( $field['sso_sync_value'], array( 'identifier', 'photoURL', 'displayName', 'profileURL' ), true ) ) {
						if ( 'displayName' === $field['sso_sync_value'] ) {
							$_SESSION['um_social_profile'][ "_uid_{$provider}" ] = $field['sso_sync_value'];
						} elseif ( 'displayName' === $field['sso_sync_value'] ) {
							$_SESSION['um_social_profile']['handle'] = $field['sso_sync_value'];
						} elseif ( 'profileURL' === $field['sso_sync_value'] ) {
							$_SESSION['um_social_profile']['link'] = $field['sso_sync_value'];
						} elseif ( 'photoURL' === $field['sso_sync_value'] ) {
							$_SESSION['um_social_profile']['photo_url'] = $field['sso_sync_value'];
						}
					}
				} else {
					$_SESSION['um_social_profile'][ $key ] = apply_filters( 'um_sso_returned_raw_data', $key, isset( $profile->{$field['sso_sync_value']} ) ? $profile->{$field['sso_sync_value']} : null, $field, $profile, $provider );
				}
			}
		}

		if ( isset( $_REQUEST['form_id'] ) ) { //phpcs:ignore WordPress.Security.NonceVerification
			unset( $_SESSION['um_social_profile'] );
		}
	}

	/**
	 * Get step process option
	 *
	 * @param integer $post_id Post ID.
	 *
	 * @return integer
	 */
	public function get_enabled_step_process( $post_id = null ) {
		return get_post_meta( $post_id, '_um_register_show_social_2steps', true );
	}

	/**
	 *  Get show flash screen option
	 *
	 * @param integer $post_id Post ID.
	 * @return integer
	 */
	public function get_show_flash_screen( $post_id = null ) {
		return get_post_meta( $post_id, '_um_register_show_flash_screen', true );
	}

	/**
	 * One step matched email
	 *
	 * @param integer $post_id Post ID.
	 * @return integer
	 */
	public function get_one_step_matched_email( $post_id = null ) {
		return get_post_meta( $post_id, '_um_register_1step_link_matched_email', true );
	}


	/**
	 * Link user to provider
	 *
	 * @param integer $user_id User ID.
	 * @param array   $profile Profile Data.
	 * @param string  $provider Provider Slug.
	 */
	public function save_user_meta( $user_id = null, $profile = array(), $provider = '' ) {

		if ( null === $user_id ) {
			$user_id = get_current_user_id();
		}

		if ( $user_id <= 0 ) {
			return;
		}

		foreach ( $profile as $key => $value ) {
			if ( strstr( $key, '_uid_' ) ) {
				update_user_meta( $user_id, $key, $value );
			} elseif ( strstr( $key, '_save_' ) ) {
				$key = str_replace( '_save_', '', $key );
				if ( 'synced_profile_photo' !== $key ) {
					update_user_meta( $user_id, $key, $value );
				}
			} else {
				update_user_meta( $user_id, '_um_sso_' . $provider . '_' . $key, $value );
			}
		}

		update_user_meta( $user_id, '_um_sso_' . $provider . '_date_connected', gmdate( 'Y-m-d H:i:s' ) );

		do_action( 'um_social_login_after_connect', $provider, $user_id );
		do_action( "um_social_login_after_{$provider}_connect", $user_id );
	}

	/**
	 * Check user status
	 *
	 * @param  integer $user_id User ID.
	 *
	 * @return array
	 */
	public function check_user_status( $user_id ) {
		$status = UM()->common()->users()->get_status( $user_id );

		if ( 'approved' !== $status && is_user_logged_in() ) {
			wp_logout();
		}

		if ( 'awaiting_email_confirmation' === $status ) {
			um_fetch_user( $user_id );

			$checkmail_url    = um_user( 'checkmail_url' );
			$checkmail_action = um_user( 'checkmail_action' );

			um_reset_user();

			if ( ! empty( $checkmail_url ) && 'redirect_url' === $checkmail_action ) {
				return array(
					'error'      => true,
					'error_code' => 'awaiting_email_confirmation',
					'url'        => $checkmail_url,
				);
			}
		}

		switch ( $status ) {
			// If user can't log in to site...
			case 'awaiting_admin_review':
			case 'awaiting_email_confirmation':
			case 'inactive':
			case 'rejected':
				$error = get_query_var( 'err' );
				if ( empty( $error ) ) {
					return array(
						'error'      => true,
						'error_code' => $status,
					);
				}
				break;
		}

		return array( 'error' => false );
	}


	/**
	 * Check that user exists but not connected yet
	 *
	 * @param array  $profile Profile Email.
	 * @param string $provider Network provider.
	 *
	 * @return false|int
	 */
	public function email_exists( $profile, $provider ) {
		$provider;
		if ( isset( $profile['email_exists'] ) && email_exists( $profile['email_exists'] ) ) {
			return email_exists( $profile['email_exists'] );
		}
		return 0;
	}

	/**
	 * Check if email or username exists
	 *
	 * @param  array  $profile Profile data.
	 * @param  string $provider Network Provider.
	 *
	 * @return bool
	 */
	public function user_exists( $profile, $provider ) {
		$provider;
		if ( isset( $profile['email_exists'] ) && email_exists( $profile['email_exists'] ) ) {
			return email_exists( $profile['email_exists'] );
		}
		if ( isset( $profile['username_exists'] ) && username_exists( $profile['username_exists'] ) ) {
			return username_exists( $profile['username_exists'] );
		}
		return 0;
	}
}
