<?php
/**
 * Tests for ppok/blocks-find-usage.
 *
 * @package Ppok\AIToolkit\Tests
 * @license GPL-2.0-or-later
 */

namespace Ppok\AIToolkit\Tests;

use Ppok\AIToolkit\Abilities\Blocks_Find_Usage;

class Test_Blocks_Find_Usage extends \WP_UnitTestCase {

	public function test_slug_constant(): void {
		$this->assertSame( 'ppok/blocks-find-usage', Blocks_Find_Usage::SLUG );
	}

	public function test_descriptor_is_read_only_and_mcp_public(): void {
		$d = Blocks_Find_Usage::descriptor();
		$this->assertFalse( $d['meta']['ppok']['write'] );
		$this->assertTrue( $d['meta']['mcp']['public'] );
	}

	public function test_input_schema_requires_block_name_with_pattern(): void {
		$schema = Blocks_Find_Usage::input_schema();
		$this->assertSame( array( 'block_name' ), $schema['required'] );
		$this->assertSame( '^[a-z0-9][a-z0-9-]*\\/[a-z0-9][a-z0-9-]*$', $schema['properties']['block_name']['pattern'] );
	}

	public function test_permission_requires_edit_posts(): void {
		wp_set_current_user( 0 );
		$this->assertFalse( Blocks_Find_Usage::permission_callback() );

		$editor = self::factory()->user->create( array( 'role' => 'editor' ) );
		wp_set_current_user( $editor );
		$this->assertTrue( Blocks_Find_Usage::permission_callback() );
	}

	public function test_execute_returns_error_on_missing_block_name(): void {
		$out = Blocks_Find_Usage::execute_callback( array() );
		$this->assertInstanceOf( \WP_Error::class, $out );
		$this->assertSame( 'rest_invalid_param', $out->get_error_code() );
	}

	public function test_finds_post_containing_canonical_marker(): void {
		$id = self::factory()->post->create(
			array(
				'post_status'  => 'publish',
				'post_content' => "<!-- wp:paragraph -->\n<p>Hi</p>\n<!-- /wp:paragraph -->\n\n<!-- wp:core/separator -->\n<hr class=\"wp-block-separator\"/>\n<!-- /wp:core/separator -->",
			)
		);

		$out = Blocks_Find_Usage::execute_callback( array( 'block_name' => 'core/separator' ) );
		$this->assertSame( 'core/separator', $out['block_name'] );

		$ids = array_column( $out['posts'], 'post_id' );
		$this->assertContains( $id, $ids );
	}

	public function test_counts_multiple_occurrences_in_same_post(): void {
		$content = '';
		for ( $i = 0; $i < 3; $i++ ) {
			$content .= "<!-- wp:core/separator -->\n<hr/>\n<!-- /wp:core/separator -->\n\n";
		}
		$id = self::factory()->post->create(
			array(
				'post_status'  => 'publish',
				'post_content' => $content,
			)
		);

		$out = Blocks_Find_Usage::execute_callback( array( 'block_name' => 'core/separator' ) );
		$row = null;
		foreach ( $out['posts'] as $r ) {
			if ( $r['post_id'] === $id ) {
				$row = $r;
				break;
			}
		}

		$this->assertNotNull( $row );
		$this->assertSame( 3, $row['count'] );
	}

	public function test_recurses_into_innerblocks(): void {
		$nested = "<!-- wp:group -->\n<div class=\"wp-block-group\">\n<!-- wp:core/separator -->\n<hr/>\n<!-- /wp:core/separator -->\n</div>\n<!-- /wp:group -->";
		$id     = self::factory()->post->create(
			array(
				'post_status'  => 'publish',
				'post_content' => $nested,
			)
		);

		$out = Blocks_Find_Usage::execute_callback( array( 'block_name' => 'core/separator' ) );
		$ids = array_column( $out['posts'], 'post_id' );
		$this->assertContains( $id, $ids );
	}

	public function test_handles_core_shortform_marker(): void {
		// Older content omits the "core/" prefix in serialized markup.
		// parse_blocks() normalizes it to canonical core/separator, but
		// the LIKE prefilter would miss it without the alias term.
		$id = self::factory()->post->create(
			array(
				'post_status'  => 'publish',
				'post_content' => "<!-- wp:separator -->\n<hr/>\n<!-- /wp:separator -->",
			)
		);

		$out = Blocks_Find_Usage::execute_callback( array( 'block_name' => 'core/separator' ) );
		$ids = array_column( $out['posts'], 'post_id' );
		$this->assertContains( $id, $ids );
	}

	public function test_filters_by_post_status(): void {
		$draft_id = self::factory()->post->create(
			array(
				'post_status'  => 'draft',
				'post_content' => "<!-- wp:core/separator -->\n<hr/>\n<!-- /wp:core/separator -->",
			)
		);
		$pub_id   = self::factory()->post->create(
			array(
				'post_status'  => 'publish',
				'post_content' => "<!-- wp:core/separator -->\n<hr/>\n<!-- /wp:core/separator -->",
			)
		);

		// Default: publish + pending — draft excluded.
		$out = Blocks_Find_Usage::execute_callback( array( 'block_name' => 'core/separator' ) );
		$ids = array_column( $out['posts'], 'post_id' );
		$this->assertContains( $pub_id, $ids );
		$this->assertNotContains( $draft_id, $ids );

		// Explicit draft inclusion picks it up.
		$out = Blocks_Find_Usage::execute_callback(
			array(
				'block_name'  => 'core/separator',
				'post_status' => array( 'draft' ),
			)
		);
		$ids = array_column( $out['posts'], 'post_id' );
		$this->assertContains( $draft_id, $ids );
		$this->assertNotContains( $pub_id, $ids );
	}

	public function test_pagination_slices_results(): void {
		for ( $i = 0; $i < 5; $i++ ) {
			self::factory()->post->create(
				array(
					'post_status'  => 'publish',
					'post_content' => "<!-- wp:core/separator -->\n<hr/>\n<!-- /wp:core/separator -->",
				)
			);
		}

		$page1 = Blocks_Find_Usage::execute_callback(
			array(
				'block_name' => 'core/separator',
				'per_page'   => 2,
				'page'       => 1,
			)
		);
		$page2 = Blocks_Find_Usage::execute_callback(
			array(
				'block_name' => 'core/separator',
				'per_page'   => 2,
				'page'       => 2,
			)
		);

		$this->assertCount( 2, $page1['posts'] );
		$this->assertCount( 2, $page2['posts'] );
		$this->assertGreaterThanOrEqual( 5, $page1['total'] );
		$this->assertGreaterThanOrEqual( 3, $page1['pages'] );

		$ids1 = array_column( $page1['posts'], 'post_id' );
		$ids2 = array_column( $page2['posts'], 'post_id' );
		$this->assertEmpty( array_intersect( $ids1, $ids2 ), 'Pages must not overlap.' );
	}

	public function test_excludes_posts_where_marker_string_appears_only_in_innerhtml(): void {
		// Literal marker string inside a code block's innerHTML must NOT
		// register as a real separator occurrence after parse_blocks.
		$id = self::factory()->post->create(
			array(
				'post_status'  => 'publish',
				'post_content' => "<!-- wp:code -->\n<pre class=\"wp-block-code\"><code>The marker &lt;!-- wp:core/separator --&gt; lives here in text.</code></pre>\n<!-- /wp:code -->",
			)
		);

		$out = Blocks_Find_Usage::execute_callback( array( 'block_name' => 'core/separator' ) );
		$ids = array_column( $out['posts'], 'post_id' );
		$this->assertNotContains( $id, $ids );
	}

	public function test_like_terms_for_core_includes_shortform(): void {
		$terms = Blocks_Find_Usage::like_terms_for( 'core/separator' );
		$this->assertContains( '<!-- wp:core/separator ', $terms );
		$this->assertContains( '<!-- wp:separator ', $terms );
	}

	public function test_like_terms_for_non_core_is_single_term(): void {
		$terms = Blocks_Find_Usage::like_terms_for( 'acf/feature-card' );
		$this->assertSame( array( '<!-- wp:acf/feature-card ' ), $terms );
	}

	public function test_count_blocks_in_content_zero_when_absent(): void {
		$this->assertSame(
			0,
			Blocks_Find_Usage::count_blocks_in_content(
				'core/separator',
				"<!-- wp:paragraph -->\n<p>just text</p>\n<!-- /wp:paragraph -->"
			)
		);
	}

	public function test_example_returns_array_with_block_name(): void {
		$ex = Blocks_Find_Usage::example();
		$this->assertIsArray( $ex );
		$this->assertArrayHasKey( 'block_name', $ex );
	}
}
