<?php
/**
 * NodeInfo controller file.
 *
 * @package Activitypub
 */

namespace Activitypub\Rest;

use function Activitypub\get_masked_wp_version;
use function Activitypub\get_rest_url_by_path;

/**
 * ActivityPub NodeInfo Controller.
 *
 * @author Matthias Pfefferle
 *
 * @see https://nodeinfo.diaspora.software/
 */
class Nodeinfo_Controller extends \WP_REST_Controller {
	/**
	 * The namespace of this controller's route.
	 *
	 * @var string
	 */
	protected $namespace = ACTIVITYPUB_REST_NAMESPACE;

	/**
	 * The REST base for this controller's route.
	 *
	 * @var string
	 */
	protected $rest_base = 'nodeinfo';

	/**
	 * Register routes.
	 */
	public function register_routes() {
		\register_rest_route(
			$this->namespace,
			'/' . $this->rest_base,
			array(
				array(
					'methods'             => \WP_REST_Server::READABLE,
					'callback'            => array( $this, 'get_items' ),
					'permission_callback' => '__return_true',
				),
			)
		);

		\register_rest_route(
			$this->namespace,
			'/' . $this->rest_base . '/(?P<version>\d\.\d)',
			array(
				'args'   => array(
					'version' => array(
						'description' => 'The version of the NodeInfo schema.',
						'type'        => 'string',
						'required'    => true,
					),
				),
				array(
					'methods'             => \WP_REST_Server::READABLE,
					'callback'            => array( $this, 'get_item' ),
					'permission_callback' => '__return_true',
				),
				'schema' => array( $this, 'get_item_schema' ),
			)
		);
	}

	/**
	 * Retrieves the NodeInfo discovery profile.
	 *
	 * @param \WP_REST_Request $request The request object.
	 *
	 * @return \WP_REST_Response Response object.
	 */
	public function get_items( $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
		$response = array(
			'links' => array(

				/*
				 * Needs http protocol for spec compliance.
				 * @ticket https://github.com/Automattic/wordpress-activitypub/pull/1275
				 */
				array(
					'rel'  => 'http://nodeinfo.diaspora.software/ns/schema/2.0',
					'href' => get_rest_url_by_path( '/nodeinfo/2.0' ),
				),
				array(
					'rel'  => 'https://nodeinfo.diaspora.software/ns/schema/2.0',
					'href' => get_rest_url_by_path( '/nodeinfo/2.0' ),
				),
				array(
					'rel'  => 'http://nodeinfo.diaspora.software/ns/schema/2.1',
					'href' => get_rest_url_by_path( '/nodeinfo/2.1' ),
				),
				array(
					'rel'  => 'https://nodeinfo.diaspora.software/ns/schema/2.1',
					'href' => get_rest_url_by_path( '/nodeinfo/2.1' ),
				),
				array(
					'rel'  => 'https://www.w3.org/ns/activitystreams#Application',
					'href' => get_rest_url_by_path( 'application' ),
				),
			),
		);

		return \rest_ensure_response( $response );
	}

	/**
	 * Retrieves the NodeInfo profile.
	 *
	 * @param \WP_REST_Request $request The request object.
	 * @return \WP_REST_Response Response object.
	 */
	public function get_item( $request ) {
		$version = $request->get_param( 'version' );

		/**
		 * Fires before the NodeInfo data is created and sent to the client.
		 *
		 * @param string $version The NodeInfo version.
		 */
		\do_action( 'activitypub_rest_nodeinfo_pre', $version );

		switch ( $version ) {
			case '2.0':
			case '2.1':
				$response = $this->get_version_2_X( $version );
				break;

			default:
				$response = new \WP_Error( 'activitypub_rest_nodeinfo_invalid_version', 'Unsupported NodeInfo version.', array( 'status' => 405 ) );
				break;
		}

		return \rest_ensure_response( $response );
	}

	/**
	 * Get the NodeInfo 2.0 data.
	 *
	 * @param string $version The NodeInfo version.
	 *
	 * @return array The NodeInfo data.
	 */
	public function get_version_2_X( $version ) {
		$posts    = \wp_count_posts();
		$comments = \wp_count_comments();

		$nodeinfo = array(
			'version'           => $version,
			'software'          => array(
				'name'    => 'wordpress',
				'version' => get_masked_wp_version(),
			),
			'openRegistrations' => (bool) get_option( 'users_can_register' ),
			'usage'             => array(
				'localPosts'    => (int) $posts->publish,
				'localComments' => $comments->approved,
			),
			'metadata'          => array(
				'nodeName'        => \get_bloginfo( 'name' ),
				'nodeDescription' => \get_bloginfo( 'description' ),
				'nodeIcon'        => \get_site_icon_url(),
			),
		);

		/**
		 * Filter the NodeInfo data.
		 *
		 * @param array  $nodeinfo The NodeInfo data.
		 * @param string $version  The NodeInfo version.
		 */
		return \apply_filters( 'nodeinfo_data', $nodeinfo, $version );
	}

	/**
	 * Retrieves the NodeInfo schema, conforming to JSON Schema.
	 *
	 * @return array NodeInfo schema data.
	 */
	public function get_item_schema() {
		return array(
			'$schema'    => 'http://json-schema.org/draft-04/schema#',
			'title'      => 'nodeinfo',
			'type'       => 'object',
			'properties' => array(
				'version'           => array(
					'description' => 'The version of the NodeInfo schema.',
					'type'        => 'string',
					'enum'        => array( '2.0', '2.1' ),
				),
				'software'          => array(
					'description' => 'Information about the software.',
					'type'        => 'object',
					'properties'  => array(
						'name'       => array(
							'description' => 'The canonical name of this server software.',
							'type'        => 'string',
						),
						'version'    => array(
							'description' => 'The version of this server software.',
							'type'        => 'string',
						),
						'homepage'   => array(
							'description' => 'The url of the homepage of this server software.',
							'type'        => 'string',
							'format'      => 'uri',
						),
						'repository' => array(
							'description' => 'The url of the source code repository of this server software.',
							'type'        => 'string',
							'format'      => 'uri',
						),
					),
				),
				'protocols'         => array(
					'description' => 'The protocols supported on this server.',
					'type'        => 'array',
					'items'       => array(
						'type' => 'string',
						'enum' => array(
							'activitypub',
							'buddycloud',
							'dfrn',
							'diaspora',
							'libertree',
							'ostatus',
							'pumpio',
							'tent',
							'xmpp',
							'zot',
						),
					),
				),
				'services'          => array(
					'description' => 'The third party sites this server can connect to via their application API.',
					'type'        => 'object',
					'properties'  => array(
						'inbound'  => array(
							'description' => 'The third party sites this server can retrieve messages from for combined display with regular traffic.',
							'type'        => 'array',
							'items'       => array(
								'type' => 'string',
								'enum' => array(
									'atom1.0',
									'gnusocial',
									'imap',
									'pnut',
									'pop3',
									'pumpio',
									'rss2.0',
									'twitter',
								),
							),
						),
						'outbound' => array(
							'description' => 'The third party sites this server can publish messages to on the behalf of a user.',
							'type'        => 'array',
							'items'       => array(
								'type' => 'string',
								'enum' => array(
									'atom1.0',
									'blogger',
									'buddycloud',
									'diaspora',
									'dreamwidth',
									'drupal',
									'facebook',
									'friendica',
									'gnusocial',
									'google',
									'insanejournal',
									'libertree',
									'linkedin',
									'livejournal',
									'mediagoblin',
									'myspace',
									'pinterest',
									'pnut',
									'posterous',
									'pumpio',
									'redmatrix',
									'rss2.0',
									'smtp',
									'tent',
									'tumblr',
									'twitter',
									'wordpress',
									'xmpp',
								),
							),
						),
					),
				),
				'openRegistrations' => array(
					'description' => 'Whether this server allows open self-registration.',
					'type'        => 'boolean',
				),
				'usage'             => array(
					'description' => 'Usage statistics for this server.',
					'type'        => 'object',
					'properties'  => array(
						'users'         => array(
							'description' => 'Statistics about the users of this server.',
							'type'        => 'object',
							'properties'  => array(
								'total'          => array(
									'description' => 'The total amount of on this server registered users.',
									'type'        => 'integer',
									'minimum'     => 0,
								),
								'activeMonth'    => array(
									'description' => 'The amount of users that signed in at least once in the last 30 days.',
									'type'        => 'integer',
									'minimum'     => 0,
								),
								'activeHalfyear' => array(
									'description' => 'The amount of users that signed in at least once in the last 180 days.',
									'type'        => 'integer',
									'minimum'     => 0,
								),
							),
						),
						'localPosts'    => array(
							'description' => 'The amount of posts that were made by users that are registered on this server.',
							'type'        => 'integer',
							'minimum'     => 0,
						),
						'localComments' => array(
							'description' => 'The amount of comments that were made by users that are registered on this server.',
							'type'        => 'integer',
							'minimum'     => 0,
						),
					),
				),
				'metadata'          => array(
					'description' => 'Free form key value pairs for software specific values.',
					'type'        => 'object',
				),
			),
		);
	}
}
