<?php

namespace Gianism\Service;

use Gianism\Cron\Daily;

/**
 * Google client
 *
 * @package Gianism
 * @since 2.0.0
 * @author Takahashi Fumiki
 *
 * @property-read \Google_Client $api
 *
 */
class Google extends AbstractService {

	/**
	 * @depreacted  Google stop Google plus.
	 * @var null Google Plus Client.
	 */
	public $plus = null;

	/**
	 * URL prefix to prepend
	 *
	 * @var string
	 */
	public $url_prefix = 'google-auth';

	/**
	 * Verbose service name
	 *
	 * @var string
	 */
	public $verbose_service_name = 'Google';

	/**
	 * @var bool
	 */
	public $ggl_enabled = false;

	/**
	 * @var string
	 */
	public $ggl_consumer_key = '';

	/**
	 * @var string
	 */
	public $ggl_consumer_secret = '';

	/**
	 * @var bool
	 */
	public $ggl_use_analytics = true;

	/**
	 * @var bool
	 */
	public $ggl_workspace_mode = false;

	/**
	 * @var string
	 */
	public $ggl_workspace_allowed_domains = '';

	/**
	 * @var string
	 */
	public $umeta_account = '_wpg_google_account';

	/**
	 * @var string
	 */
	public $umeta_plus = '_wpg_google_plus_id';

	/**
	 * Oauth client store
	 *
	 * @var \Google_Client
	 */
	private $_api = null;

	/**
	 * Plus client
	 *
	 * @var \Google_Service_Plus
	 */
	private $_plus = null;

	/**
	 * Option to retrieve
	 *
	 * @var array
	 */
	protected $option_keys = [
		'ggl_enabled'                   => false,
		'ggl_consumer_key'              => '',
		'ggl_consumer_secret'           => '',
		'ggl_use_analytics'             => true,
		'ggl_workspace_mode'            => false,
		'ggl_workspace_allowed_domains' => '',
	];

	/**
	 * Constructor
	 *
	 * @param array $argument
	 */
	protected function __construct( array $argument = [] ) {
		parent::__construct( $argument );
		// Filter rewrite name
		add_filter(
			'gianism_filter_service_prefix',
			function ( $prefix ) {
				if ( 'google-auth' === $prefix ) {
					$prefix = 'google';
				}
				return $prefix;
			}
		);
	}

	/**
	 * Handle callback request
	 *
	 * @global \wpdb $wpdb
	 *
	 * @param string $action
	 *
	 * @return void
	 */
	protected function handle_default( $action ) {
		// Get common values
		$redirect_url = $this->session->get( 'redirect_to' );
		$code         = $this->input->request( 'code' );
		switch ( $action ) {
			case 'login': // Let user login
				try {
					// Authenticate and get token
					$token   = $this->api->fetchAccessTokenWithAuthCode( $code );
					$profile = $this->get_profile();
					// Check email validity
					if ( ! isset( $profile['email'] ) || ! is_email( $profile['email'] ) ) {
						throw new \Exception( $this->mail_fail_string() );
					}
					$email = $profile['email'];
					/**
					 * Filter to allow or deny Google login.
					 *
					 * Return false to reject the login.
					 *
					 * @since 5.4.0
					 * @param bool                              $allowed Whether login is allowed. Default true.
					 * @param string                            $email   User's email address.
					 * @param \Google_Service_Oauth2_Userinfoplus $profile User's profile data from Google.
					 */
					$allowed = apply_filters( 'gianism_google_login_allowed', true, $email, $profile );
					if ( ! $allowed ) {
						throw new \Exception( __( 'You are not allowed to login to this site.', 'wp-gianism' ) );
					}
					$plus_id = isset( $profile['id'] ) ? $profile['id'] : 0;
					$user_id = $this->get_meta_owner( $this->umeta_account, $email );
					if ( ! $user_id ) {
						// Test
						$this->test_user_can_register();
						// Check email
						if ( email_exists( $email ) ) {
							throw new \Exception( $this->duplicate_account_string() );
						}
						// Create user name
						$user_name = $this->valid_username_from_mail( $email );
						/**
						 * @see Facebook
						 */
						$user_name = apply_filters( 'gianism_register_name', $user_name, $this->service, $profile );
						// Create user
						$user_id = wp_create_user( $user_name, wp_generate_password(), $email );
						if ( is_wp_error( $user_id ) ) {
							throw new \Exception( $this->registration_error_string() );
						}
						// Update user meta
						update_user_meta( $user_id, $this->umeta_account, $email );
						if ( $plus_id ) {
							update_user_meta( $user_id, $this->umeta_plus, $plus_id );
						}
						update_user_meta( $user_id, 'nickname', $profile['name'] );
						$this->db->update(
							$this->db->users,
							array(
								'display_name' => $profile['name'],
							),
							array(
								'ID' => $user_id,
							),
							array( '%s' ),
							array( '%d' )
						);
						$this->user_password_unknown( $user_id );
						$this->hook_connect( $user_id, $profile, true );
						$this->welcome( $profile['name'] );
					}
					// Make user logged in
					$this->set_auth_cookie( $user_id );
					$redirect_url = $this->filter_redirect( $redirect_url, 'login' );
				} catch ( \Exception $e ) {
					$this->auth_fail( $e->getMessage() );
					$redirect_url = wp_login_url( $redirect_url, true );
					$redirect_url = $this->filter_redirect( $redirect_url, 'login-failure' );
				}
				wp_redirect( $redirect_url );
				exit;
				break;
			case 'connect': // Connect account
				// Connection finished. Let's redirect.
				if ( ! $redirect_url ) {
					$redirect_url = admin_url( 'profile.php' );
				}
				try {
					// Authenticate and get token
					$token   = $this->api->fetchAccessTokenWithAuthCode( $code );
					$profile = $this->get_profile();
					// Check email validity
					if ( ! isset( $profile['email'] ) || ! is_email( $profile['email'] ) ) {
						throw new \Exception( $this->mail_fail_string() );
					}
					// Check if other user has these as meta_value
					$email = $profile['email'];
					/** This filter is documented in app/Gianism/Service/Google.php */
					$allowed = apply_filters( 'gianism_google_login_allowed', true, $email, $profile );
					if ( ! $allowed ) {
						throw new \Exception( __( 'You are not allowed to connect this account.', 'wp-gianism' ) );
					}
					$email_owner = $this->get_meta_owner( $this->umeta_account, $email );
					if ( $email_owner && ( get_current_user_id() !== $email_owner ) ) {
						throw new \Exception( $this->duplicate_account_string() );
					}
					// Now let's save user data
					update_user_meta( get_current_user_id(), $this->umeta_account, $email );
					if ( isset( $profile['id'] ) && $profile['id'] ) {
						update_user_meta( get_current_user_id(), $this->umeta_plus, $profile['id'] );
					}
					// Fires hook
					$this->hook_connect( get_current_user_id(), $profile );
					// Save message
					$this->welcome( $profile['name'] );
					// Apply filter
					$redirect_url = $this->filter_redirect( $redirect_url, 'connect' );
				} catch ( \Exception $e ) {
					$this->auth_fail( $e->getMessage() );
					// Apply filter
					$redirect_url = $this->filter_redirect( $redirect_url, 'connect-failure' );
				}
				// Connection finished. Let's redirect.
				if ( ! $redirect_url ) {
					$redirect_url = admin_url( 'profile.php' );
				}
				wp_redirect( $redirect_url );
				exit;
				break;
			default:
				/**
				 * @see Facebook
				 */
				do_action(
					'gianism_extra_action',
					$this->service_name,
					$action,
					[
						'redirect_to' => $redirect_url,
						'code'        => $code,
					]
				);
				$this->input->wp_die( sprintf( $this->_( 'Sorry, but wrong access. Please go back to <a href="%s">%s</a>.' ), home_url( '/' ), get_bloginfo( 'name' ) ), 500, false );
				break;
		}
	}


	/**
	 * Returns Profile
	 *
	 * @depreacted Google stop Google Plus
	 * @return \Google_Service_Oauth2_Userinfoplus
	 */
	private function get_profile() {
		$oauth = new \Google_Service_Oauth2( $this->api );
		return $oauth->userinfo->get();
	}

	/**
	 * Detect if user is connected to this service
	 *
	 * @param int $user_id
	 *
	 * @return bool
	 */
	public function is_connected( $user_id ) {
		return (bool) get_user_meta( $user_id, $this->umeta_account, true );
	}

	/**
	 * Disconnect user from this service
	 *
	 * @param int $user_id
	 *
	 * @return mixed
	 */
	public function disconnect( $user_id ) {
		delete_user_meta( $user_id, $this->umeta_account );
		delete_user_meta( $user_id, $this->umeta_plus );
	}

	/**
	 * Return api URL to authenticate
	 *
	 * If you need additional information (ex. token),
	 * use $this->session->write inside.
	 *
	 * <code>
	 * $this->session->write('token', $token);
	 * return $url;
	 * </code>
	 *
	 * @param string $action 'connect', 'login'
	 *
	 * @return string|false URL to redirect
	 * @throws \Exception
	 */
	protected function get_api_url( $action ) {
		switch ( $action ) {
			case 'login':
			case 'connect':
				return $this->api->createAuthUrl();
			default:
				return false;
		}
	}

	protected function login_label( $register = false, $context = '' ) {
		// translators: %s is service name.
		return sprintf( _x( 'Sign in with %s', 'login label', 'wp-gianism' ), $this->verbose_service_name );
	}

	protected function svg_path() {
		return 'google.png';
	}

	/**
	 * Getter
	 *
	 * @param string $name
	 *
	 * @return mixed
	 */
	public function __get( $name ) {
		switch ( $name ) {
			case 'api':
				if ( ! $this->_api ) {
					$this->_api = new \Google_Client();
					$this->_api->setApplicationName( get_bloginfo( 'name' ) );
					$this->_api->setClientId( $this->ggl_consumer_key );
					$this->_api->setClientSecret( $this->ggl_consumer_secret );
					$this->_api->setRedirectUri( $this->get_redirect_endpoint() );
					$this->_api->setApprovalPrompt( 'auto' );
					$this->_api->setAccessType( 'online' );
					$this->_api->setScopes(
						[
							'https://www.googleapis.com/auth/userinfo.profile',
							'https://www.googleapis.com/auth/userinfo.email',
						]
					);
				}
				return $this->_api;
			default:
				return parent::__get( $name );
		}
	}
}
