<?php
/**
 * ppok/blocks-registered — return the full WP_Block_Type_Registry.
 *
 * Read-only block-type discovery. Lets agents enumerate every block known
 * to this install — core blocks, theme/plugin custom blocks (e.g. acf/*),
 * dynamic vs. static — without scanning posts. Replaces the previous
 * "grep through every post hunting for block names" workaround.
 *
 * Cap: edit_posts.
 *
 * @package Ppok\AIToolkit
 * @license GPL-2.0-or-later
 */

namespace Ppok\AIToolkit\Abilities;

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

final class Blocks_Registered {

	public const SLUG = 'ppok/blocks-registered';

	public static function descriptor(): array {
		return array(
			'label'               => __( 'List registered block types', 'ppok-ai-toolkit' ),
			'description'         => __( 'Returns every block type in WP_Block_Type_Registry — core, theme, and plugin-contributed — with name, title, description, category, is_dynamic, and attributes schema. Use to discover custom blocks (acf/*, etc.) without grepping posts.', 'ppok-ai-toolkit' ),
			'category'            => 'ppok',
			'input_schema'        => self::input_schema(),
			'output_schema'       => self::output_schema(),
			'permission_callback' => array( __CLASS__, 'permission_callback' ),
			'execute_callback'    => array( __CLASS__, 'execute_callback' ),
			'meta'                => array(
				'show_in_rest' => true,
				'mcp'          => array( 'public' => true ),
				'ppok'         => array( 'write' => false ),
			),
		);
	}

	public static function input_schema(): array {
		return array(
			'type'                 => 'object',
			'additionalProperties' => false,
			'properties'           => array(
				'namespace' => array(
					'type'        => 'string',
					'maxLength'   => 191,
					'description' => __( 'Optional namespace filter (e.g. "core", "acf"). When set, only blocks whose name starts with "<namespace>/" are returned.', 'ppok-ai-toolkit' ),
				),
			),
		);
	}

	public static function output_schema(): array {
		return array(
			'type'       => 'object',
			'required'   => array( 'blocks', 'total' ),
			'properties' => array(
				'blocks' => array(
					'type'  => 'array',
					'items' => array(
						'type'       => 'object',
						'required'   => array( 'name', 'title', 'description', 'category', 'is_dynamic', 'attributes_schema' ),
						'properties' => array(
							'name'              => array( 'type' => 'string' ),
							'title'             => array( 'type' => 'string' ),
							'description'       => array( 'type' => 'string' ),
							'category'          => array( 'type' => array( 'string', 'null' ) ),
							'is_dynamic'        => array( 'type' => 'boolean' ),
							'attributes_schema' => array( 'type' => 'object' ),
						),
					),
				),
				'total'  => array( 'type' => 'integer' ),
			),
		);
	}

	public static function example(): array {
		return array(
			'namespace' => 'core',
		);
	}

	public static function permission_callback(): bool {
		return current_user_can( 'edit_posts' );
	}

	public static function execute_callback( array $args = array() ): array {
		$namespace = isset( $args['namespace'] ) ? trim( (string) $args['namespace'] ) : '';
		$prefix    = $namespace !== '' ? $namespace . '/' : '';

		$registry = \WP_Block_Type_Registry::get_instance();
		$items    = array();

		foreach ( $registry->get_all_registered() as $name => $block_type ) {
			$name = (string) $name;
			if ( $prefix !== '' && strpos( $name, $prefix ) !== 0 ) {
				continue;
			}
			$items[] = self::shape_block_type( $block_type );
		}

		usort(
			$items,
			static function ( array $a, array $b ): int {
				return strcmp( $a['name'], $b['name'] );
			}
		);

		return array(
			'blocks' => $items,
			'total'  => count( $items ),
		);
	}

	private static function shape_block_type( \WP_Block_Type $bt ): array {
		$attributes = is_array( $bt->attributes ) ? $bt->attributes : array();

		return array(
			'name'              => (string) $bt->name,
			'title'             => (string) ( $bt->title ?: $bt->name ),
			'description'       => (string) ( $bt->description ?? '' ),
			// WP_Block_Type::category is a string slug or null when uncategorized.
			'category'          => is_string( $bt->category ) && $bt->category !== '' ? $bt->category : null,
			// render_callback being callable is WP's own signal for dynamic blocks.
			'is_dynamic'        => is_callable( $bt->render_callback ),
			// Force object shape on the wire so an empty schema doesn't serialize as [].
			'attributes_schema' => empty( $attributes ) ? new \stdClass() : $attributes,
		);
	}
}
