<?php
/**
 * Defines Rest Settings API
 *
 * @link  https://personalizewp.com
 * @since 2.6.0
 *
 * @package PersonalizeWP/
 * @subpackage PersonalizeWP/Admin/REST
 */

namespace PersonalizeWP\Admin\REST;

use PersonalizeWP\Admin\PageRules;
use PersonalizeWP\Model\Rule;
use PersonalizeWP\Settings;

defined( 'ABSPATH' ) || exit; // Exit if accessed directly.

/**
 * Settings Controller class
 *
 * @extends  WP_REST_Controller
 */
class Settings_Controller extends \WP_REST_Controller {

	/**
	 * Endpoint namespace.
	 *
	 * @var string
	 */
	protected $namespace;

	/**
	 * Route base.
	 *
	 * @var string
	 */
	protected $rest_base = 'settings';

	/**
	 * Initialize the class and set its properties.
	 *
	 * @param string $ns Namespace
	 */
	public function __construct( $ns ) {
		$this->namespace = $ns;
	}

	/**
	 * Registers the routes for the search controller.
	 *
	 * @see register_rest_route()
	 */
	public function register_routes() {
		register_rest_route(
			"{$this->namespace}v1",
			'/' . $this->rest_base,
			array(
				array(
					'methods'             => \WP_REST_Server::READABLE,
					'callback'            => [ $this, 'get_settings' ],
					'permission_callback' => [ $this, 'get_settings_permission_check' ],
				),
				'schema' => [ $this, 'get_public_item_schema' ],
			)
		);
	}

	/**
	 * Checks if the request has access.
	 *
	 * @param \WP_REST_Request $request Full details about the request.
	 * @return true|\WP_Error True if the request has access, WP_Error object otherwise.
	 */
	public function get_settings_permission_check( $request ) {

		// Restrict endpoint to only users who have the edit_posts capability.
		if ( current_user_can( 'edit_posts' ) ) {
			return true;
		}

		// This is an allow-list approach.
		return new \WP_Error( 'rest_forbidden', esc_html__( 'You can not view private data.', 'personalizewp' ), array( 'status' => 401 ) );
	}

	/**
	 * Gets a collection of settings and variables.
	 *
	 * @param \WP_REST_Request $request Full details about the request.
	 * @return \WP_REST_Response|\WP_Error Response object on success, or WP_Error object on failure.
	 */
	public function get_settings( $request ) {

		$settings = array(
			'plugin_settings' => array(
				'version' => PERSONALIZEWP_VERSION,
			),
			'is_pro'          => false,
			'rules'           => $this->get_all_rules(),
			'segments'        => $this->get_segments(),
			'user_roles'      => $this->get_user_roles(),
			'profile_fields'  => $this->get_profile_custom_fields(),
			'add_rule_link'   => admin_url( PageRules::$create_url ),
			'integrations'    => [
				'plugins' => [],
			],
		);

		/**
		 * Filter PersonalizeWP REST settings for block editor JS.
		 *
		 * @since 2.6.0
		 *
		 * @param array            $settings  Base settings for PersonalizeWP
		 * @param \WP_REST_Request $request   REST Request
		 */
		$settings = apply_filters(
			'personalizewp_rest_settings',
			$settings,
			$request
		);

		return rest_ensure_response( $settings );
	}

	/**
	 * Retrieves all PersonalizeWP rules on the website.
	 *
	 * @return array
	 */
	public function get_all_rules() {

		$all_rules = Rule::all();

		// Convert rules objects into simplier array
		$rules = [];
		foreach ( $all_rules as $rule ) {
			$rules[] = [
				'id'        => $rule->id,
				'name'      => $rule->name,
				'is_usable' => $rule->is_usable,
			];
		}

		return $rules;
	}

	/**
	 * Retrieves all segments on the website.
	 *
	 * @uses personalizewp_rest_settings
	 *
	 * @since 3.2.0
	 *
	 * @return array
	 */
	public function get_segments() {
		global $wpdb;

		$segments = wp_cache_get( 'pwp_pro_segments', 'personalizewp' );
		if ( false === $segments ) {

			// Always store as array.
			$segments = array();
			$rows     = $wpdb->get_results( "SELECT * FROM {$wpdb->prefix}pwp_segments ORDER BY name ASC;", ARRAY_A ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Reason: custom db table
			foreach ( $rows as $row ) {
				$segments[] = [
					'id'        => $row['ID'],
					'name'      => $row['name'],
					'is_usable' => (bool) $row['is_active'],
				];
			}
			wp_cache_set( 'pwp_pro_segments', $segments, 'personalizewp', HOUR_IN_SECONDS );
		}

		return $segments;
	}

	/**
	 * Retrieves all editable user roles on the website.
	 *
	 * @uses personalizewp_rest_settings
	 *
	 * @since 3.2.0
	 *
	 * @return array
	 */
	public function get_user_roles() {

		$roles      = array();
		$all_roles  = wp_roles()->roles;
		$core_roles = array(
			'administrator' => 'core',
			'editor'        => 'core',
			'author'        => 'core',
			'contributor'   => 'core',
			'subscriber'    => 'core',
		);

		foreach ( $all_roles as $role => $details ) {
			$atts = array(
				'value' => $role,
				'label' => $details['name'],
			);

			if ( isset( $core_roles[ $role ] ) ) {
				$atts['type'] = $core_roles[ $role ];
			} else {
				$atts['type'] = 'custom';
			}

			$roles[] = $atts;
		}

		/**
		 * Filters the list of roles.
		 *
		 * @since 2.6.0
		 *
		 * @param array $roles User roles
		 */
		$roles = apply_filters( 'personalizewp_user_roles', $roles );

		return $roles;
	}

	/**
	 * Retrieves all custom fields available for Visitor Profiles on the website, formatting for JS.
	 *
	 * @uses personalizewp_rest_settings
	 *
	 * @since 3.2.0
	 *
	 * @return array
	 */
	public function get_profile_custom_fields() {

		$custom_fields = [];
		$_fields       = Settings::instance()->get_profile_custom_fields();
		if ( ! empty( $_fields ) ) {
			foreach ( $_fields as $key => $label ) {
				// Formatted for JS.
				$custom_fields[] = array(
					'value' => $key,
					'label' => $label,
				);
			}
		}

		return $custom_fields;
	}

	/**
	 * Get the Settings schema, conforming to JSON Schema.
	 *
	 * @return array
	 */
	public function get_item_schema() {
		if ( $this->schema ) {
			// Since WordPress 5.3, the schema can be cached in the $schema property.
			return $this->schema;
		}

		$schema = array(
			'$schema'    => 'http://json-schema.org/draft-04/schema#',
			'title'      => 'settings',
			'type'       => 'object',
			'properties' => array(
				'plugin_variables' => array(
					'description' => __( 'An array of plugin variables, such as version number.', 'personalizewp' ),
					'type'        => 'array',
					'readonly'    => true,
				),
				'is_pro'           => array(
					'description' => __( 'An indicator to determine if PersonalizeWP Pro is enabled.', 'personalizewp' ),
					'type'        => 'boolean',
					'readonly'    => true,
				),
				'rules'            => array(
					'description' => __( 'The available rules on the site.', 'personalizewp' ),
					'type'        => 'array',
					'readonly'    => true,
				),
				'segments'         => array(
					'description' => __( 'The available segments on the site.', 'personalizewp' ),
					'type'        => 'array',
					'readonly'    => true,
				),
				'user_roles'       => array(
					'description' => __( 'The available user roles on the site.', 'personalizewp' ),
					'type'        => 'array',
					'readonly'    => true,
				),
				'profile_fields'   => array(
					'description' => __( 'The available custom Profile fields on the site.', 'personalizewp' ),
					'type'        => 'array',
					'readonly'    => true,
				),
				'integrations'     => array(
					'title'      => __( 'The available 3rd party integrations.', 'personalizewp' ),
					'type'       => 'object',
					'readonly'   => true,
					'properties' => array(
						'plugins' => array(
							'description' => __( 'An array of additional editor plugins, as plugin slug and plugin display name pairs.', 'personalizewp' ),
							'type'        => 'array',
							'readonly'    => true,
						),
					),
				),
			),
		);

		/**
		 * Filter the schema for the PersonalizeWP REST settings.
		 *
		 * @since 2.6.0
		 *
		 * @param array $schema Current json schema
		 */
		$this->schema = apply_filters(
			'personalizewp_rest_settings_schema',
			$schema
		);

		return $this->schema;
	}
}
