<?php
/**
 * ppok/patterns-registered — return every block pattern WP knows about.
 *
 * Read-only pattern discovery. Surfaces every pattern in
 * WP_Block_Patterns_Registry — core defaults, theme-registered patterns
 * (the block theme on this install ships many), and any plugin that
 * calls register_block_pattern(). Returns the full pattern content (the
 * raw block markup) so an agent can read or insert it directly.
 *
 * Why not the wp_block CPT (Synced Patterns / Reusable Blocks)?
 *   That's already free via ppok/rest-call against /wp/v2/blocks. The
 *   gap is the *registered* pattern surface — agents cannot reach it
 *   through the REST API today.
 *
 * Cap: edit_posts.
 *
 * @package Ppok\AIToolkit
 * @license GPL-2.0-or-later
 */

namespace Ppok\AIToolkit\Abilities;

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

final class Patterns_Registered {

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

	public static function descriptor(): array {
		return array(
			'label'               => __( 'List registered block patterns', 'ppok-ai-toolkit' ),
			'description'         => __( 'Returns every block pattern in WP_Block_Patterns_Registry — core, theme, and plugin-registered — with name, title, description, categories, source, and the full block-markup content. Read-only. For admin-managed synced patterns (wp_block CPT), use ppok/rest-call against /wp/v2/blocks instead.', '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(
				'category'        => array(
					'type'        => 'string',
					'maxLength'   => 191,
					'description' => __( 'Optional pattern-category slug filter (e.g. "featured", "header", "footer"). Pattern is included if any of its categories match.', 'ppok-ai-toolkit' ),
				),
				'include_content' => array(
					'type'        => 'boolean',
					'default'     => true,
					'description' => __( 'When false, omit the block-markup content from each pattern. Use to enumerate the catalog cheaply before pulling a specific pattern.', 'ppok-ai-toolkit' ),
				),
			),
		);
	}

	public static function output_schema(): array {
		return array(
			'type'       => 'object',
			'required'   => array( 'patterns', 'total' ),
			'properties' => array(
				'patterns' => array(
					'type'  => 'array',
					'items' => array(
						'type'       => 'object',
						'required'   => array( 'name', 'title', 'description', 'categories', 'keywords', 'source' ),
						'properties' => array(
							'name'        => array( 'type' => 'string' ),
							'title'       => array( 'type' => 'string' ),
							'description' => array( 'type' => 'string' ),
							'categories'  => array( 'type' => 'array', 'items' => array( 'type' => 'string' ) ),
							'keywords'    => array( 'type' => 'array', 'items' => array( 'type' => 'string' ) ),
							// 'core' | 'theme' | 'plugin' | 'pattern-directory/core' | 'block' | etc.
							'source'      => array( 'type' => array( 'string', 'null' ) ),
							'viewport_width' => array( 'type' => array( 'integer', 'null' ) ),
							'block_types' => array( 'type' => 'array', 'items' => array( 'type' => 'string' ) ),
							'post_types'  => array( 'type' => 'array', 'items' => array( 'type' => 'string' ) ),
							'inserter'    => array( 'type' => 'boolean' ),
							'content'     => array( 'type' => 'string' ),
						),
					),
				),
				'total'    => array( 'type' => 'integer' ),
			),
		);
	}

	public static function example(): array {
		return array(
			'category'        => 'featured',
			'include_content' => false,
		);
	}

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

	public static function execute_callback( array $args = array() ): array {
		$category        = isset( $args['category'] ) ? trim( (string) $args['category'] ) : '';
		$include_content = array_key_exists( 'include_content', $args ) ? (bool) $args['include_content'] : true;

		if ( ! class_exists( '\WP_Block_Patterns_Registry' ) ) {
			return array( 'patterns' => array(), 'total' => 0 );
		}

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

		foreach ( $registry->get_all_registered() as $pattern ) {
			if ( ! is_array( $pattern ) ) {
				continue;
			}
			$shape = self::shape_pattern( $pattern, $include_content );

			if ( $category !== '' && ! in_array( $category, $shape['categories'], true ) ) {
				continue;
			}
			$items[] = $shape;
		}

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

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

	/**
	 * @param array<string,mixed> $pattern Raw pattern row from the registry.
	 */
	private static function shape_pattern( array $pattern, bool $include_content ): array {
		$out = array(
			'name'           => (string) ( $pattern['name'] ?? '' ),
			'title'          => (string) ( $pattern['title'] ?? '' ),
			'description'    => (string) ( $pattern['description'] ?? '' ),
			'categories'     => self::normalize_string_list( $pattern['categories'] ?? array() ),
			'keywords'       => self::normalize_string_list( $pattern['keywords'] ?? array() ),
			'source'         => isset( $pattern['source'] ) && is_string( $pattern['source'] ) && $pattern['source'] !== ''
				? $pattern['source']
				: null,
			'viewport_width' => isset( $pattern['viewportWidth'] ) ? (int) $pattern['viewportWidth'] : null,
			'block_types'    => self::normalize_string_list( $pattern['blockTypes'] ?? array() ),
			'post_types'     => self::normalize_string_list( $pattern['postTypes'] ?? array() ),
			'inserter'       => array_key_exists( 'inserter', $pattern ) ? (bool) $pattern['inserter'] : true,
		);

		if ( $include_content ) {
			$out['content'] = (string) ( $pattern['content'] ?? '' );
		}

		return $out;
	}

	/**
	 * @param mixed $value
	 * @return array<int,string>
	 */
	private static function normalize_string_list( $value ): array {
		if ( is_string( $value ) ) {
			$value = array( $value );
		}
		if ( ! is_array( $value ) ) {
			return array();
		}
		return array_values( array_map( 'strval', array_filter( $value, 'is_scalar' ) ) );
	}
}
