<?php
/**
 * Inspired by the way elementor handles addons.
 *
 * @link https://github.com/elementor/elementor/
 * @package Activitypub
 */

namespace Activitypub\Transformer;

use WP_Post;
use WP_Comment;

use function Activitypub\camel_to_snake_case;
use function Activitypub\snake_to_camel_case;

if ( ! defined( 'ABSPATH' ) ) {
	exit; // Exit if accessed directly.
}

/**
 * ActivityPub transformers manager.
 *
 * ActivityPub transformers manager handler class is responsible for registering and
 * initializing all the supported WP-Pobject to ActivityPub transformers.
 *
 * @since version_number_transformer_management_placeholder
 */
class Transformer_Factory {
	const DEFAULT_TRANSFORMER_MAPPING = array(
		'post' => ACTIVITYPUB_DEFAULT_TRANSFORMER,
		'page' => ACTIVITYPUB_DEFAULT_TRANSFORMER,
	);

	/**
	 * Transformers.
	 *
	 * Holds the list of all the ActivityPub transformers. Default is `null`.
	 *
	 * @since version_number_transformer_management_placeholder
	 * @access private
	 *
	 * @var \ActivityPub\Transformer\Base[]
	 */
	private $transformers = null;

	/**
	 * Transformer_Manager instance.
	 *
	 * Holds the transformer instance.
	 *
	 * @since version_number_transformer_management_placeholder
	 * @access protected
	 *
	 * @var Transformer_Manager
	 */
	protected static $_instances = [];


	/**
	 * Instance.
	 *
	 * Ensures only one instance of the transformer manager class is loaded or can be loaded.
	 *
	 * @since version_number_transformer_management_placeholder
	 * @access public
	 * @static
	 *
	 * @return Transformer_Manager An instance of the class.
	 */
	public static function instance() {
		$class_name = static::class_name();

		if ( empty( static::$_instances[ $class_name ] ) ) {
			static::$_instances[ $class_name ] = new static();
		}

		return static::$_instances[ $class_name ];
	}

	/**
	 * Class name.
	 *
	 * Retrieve the name of the class.
	 *
	 * @since version_number_transformer_management_placeholder
	 * @access public
	 * @static
	 */
	public static function class_name() {
		return get_called_class();
	}

	/**
	 * Transformers manager constructor.
	 *
	 * Initializing ActivityPub transformers manager.
	 *
	 * @since version_number_transformer_management_placeholder
	 * @access public
	 */
	public function __construct() {
		$this->require_files();
	}

	/**
	 * Require files.
	 *
	 * Require ActivityPub transformer base class.
	 *
	 * @since version_number_transformer_management_placeholder
	 * @access private
	 */
	private function require_files() {
		require ACTIVITYPUB_PLUGIN_DIR . 'includes/transformer/class-base.php';
	}

	/**
	 * Checks if a transformer is registered.
	 *
	 * @since version_number_transformer_management_placeholder
	 *
	 * @param string $name Transformer name including namespace.
	 * @return bool True if the block type is registered, false otherwise.
	 */
	public function is_registered( $name ) {
		return isset( $this->transformers[ $name ] );
	}

	/**
	 * Register a transformer.
	 *
	 * @since version_number_transformer_management_placeholder
	 * @access public
	 *
	 * @param \ActivityPub\Transformer\Base $transformer_instance ActivityPub Transformer.
	 *
	 * @return bool True if the ActivityPub transformer was registered.
	 */
	public function register( \ActivityPub\Transformer\Base $transformer_instance ) {

		if ( ! $transformer_instance instanceof \ActivityPub\Transformer\Base ) {
			_doing_it_wrong(
				__METHOD__,
				\esc_html__( 'ActivityPub transformer instance must be a of \ActivityPub\Transformer_Base class.' ),
				'version_number_transformer_management_placeholder'
			);
			return false;
		}

		$transformer_name = $transformer_instance->get_name();
		if ( preg_match( '/[A-Z]+/', $transformer_name ) ) {
			_doing_it_wrong(
				__METHOD__,
				\esc_html__( 'ActivityPub transformer names must not contain uppercase characters.' ),
				'version_number_transformer_management_placeholder'
			);
			return false;
		}

		$name_matcher = '/^[a-z0-9-]+\/[a-z0-9-]+$/';
		if ( ! preg_match( $name_matcher, $transformer_name ) ) {
			_doing_it_wrong(
				__METHOD__,
				\esc_html__( 'ActivityPub transformer names must contain a namespace prefix. Example: my-plugin/my-custom-transformer' ),
				'version_number_transformer_management_placeholder'
			);
			return false;
		}

		if ( $this->is_registered( $transformer_name ) ) {
			_doing_it_wrong(
				__METHOD__,
				/* translators: %s: Block name. */
				sprintf( 'ActivityPub transformer with name "%s" is already registered.', esc_html( $transformer_name ) ),
				'version_number_transformer_management_placeholder'
			);
			return false;
		}

		/**
		 * Should the ActivityPub transformer be registered.
		 *
		 * @since version_number_transformer_management_placeholder
		 *
		 * @param bool $should_register Should the ActivityPub transformer be registered. Default is `true`.
		 * @param \ActivityPub\Transformer\Base $transformer_instance Widget instance.
		 */
		// TODO: does not implementing this slow down the website? -> compare with gutenberg block registration.
		// $should_register = apply_filters( 'activitypub/transformers/is_transformer_enabled', true, $transformer_instance );

		// if ( ! $should_register ) {
		//  return false;
		// }

		$this->transformers[ $transformer_name ] = $transformer_instance;

		return true;
	}

	/**
	 * Init transformers.
	 *
	 * Initialize ActivityPub transformer manager.
	 * Include the builtin transformers by default and add third party ones.
	 *
	 * @since version_number_transformer_management_placeholder
	 * @access private
	 */
	private function init_transformers() {
		$builtin_transformers = [
			'post',
		];

		$this->transformers = [];

		foreach ( $builtin_transformers as $transformer_name ) {
			$class_name = ucfirst( $transformer_name );

			$class_name = '\Activitypub\Transformer\\' . $class_name;

			$this->register( new $class_name() );
		}

		/**
		 * Let other transformers register.
		 *
		 * Fires after the built-in Activitypub transformers are registered.
		 *
		 * @since version_number_transformer_management_placeholder
		 *
		 * @param Transformer_Factory $this The widgets manager.
		 */
		do_action( 'activitypub_transformers_register', $this );
	}

	/**
	 * Get available ActivityPub transformers.
	 *
	 * Retrieve the registered transformers list. If given a transformer name
	 * it returns the given transformer if it is registered.
	 *
	 * @since version_number_transformer_management_placeholder
	 * @access public
	 *
	 * @param string $transformer_name Optional. Transformer name. Default is null.
	 *
	 * @return Base|Base[]|null Registered transformers.
	 */
	public function get_transformers( $transformer_name = null ) {
		if ( is_null( $this->transformers ) ) {
			$this->init_transformers();
		}

		if ( null !== $transformer_name ) {
			return isset( $this->transformers[ $transformer_name ] ) ? $this->transformers[ $transformer_name ] : null;
		}

		return $this->transformers;
	}

	/**
	 * Get the mapped ActivityPub transformer.
	 *
	 * Returns a new instance of the needed WordPress to ActivityPub transformer.
	 *
	 * @since version_number_transformer_management_placeholder
	 * @access public
	 *
	 * @param WP_Post|WP_Comment $wp_object The WordPress Post/Comment.
	 *
	 * @return \ActivityPub\Transformer\Base|null Registered transformers.
	 */
	public function transform( $wp_object ) {
		switch ( get_class( $wp_object ) ) {
			case 'WP_Post':
				$post_type = get_post_type( $wp_object );
				$transformer_mapping = \get_option( 'activitypub_transformer_mapping', self::DEFAULT_TRANSFORMER_MAPPING );
				$transformer_name = $transformer_mapping[ $post_type ];
				$transformer_class = $this->get_transformers( $transformer_name );
				$transformer_instance = new $transformer_class();
				$transformer_instance->set_wp_post( $wp_object );
				return $transformer_instance->transform();
			case 'WP_Comment':
				return new Comment( $wp_object );
			default:
				return apply_filters( 'activitypub_transformer', null, $wp_object, get_class( $wp_object ) );
		}
	}
}