<?php
/**
 * Ability: wp-agent/get-post
 *
 * Read-only. Liest die Kern-Metadaten eines Beitrags oder einer Seite (jeder
 * registrierte, öffentliche Post-Type) plus einen Block-Report, damit der
 * Aufrufer ohne einen zweiten render-check-Aufruf weiß, ob der Inhalt bereits
 * aus Blocks besteht.
 *
 * @package WPAgentCompanion
 */

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

function wpagent_ability_get_post(): array {
	return array(
		'label'               => __( 'Beitrag lesen', 'wp-agent-companion' ),
		'description'         => __( 'Liest Kern-Metadaten eines Beitrags oder einer Seite inklusive Block-Report. state_hash schützt die bestehenden Kern-Post-Writer; content_state_hash bindet zusätzlich sämtliche Meta- und Term-Zuordnungen für Read- und Preflight-Prüfungen der verbleibenden zustandsgebundenen Writer. Mit term_taxonomy liefert es zusätzlich einen Hash der genauen Term-Zuordnung dieser Taxonomie.', 'wp-agent-companion' ),
		'category'            => 'wp-agent',
		'input_schema'        => array(
			'type'       => 'object',
			'additionalProperties' => false,
			'properties' => array(
				'post_id'        => array( 'type' => 'integer', 'minimum' => 1 ),
				'term_taxonomy' => array( 'type' => 'string', 'maxLength' => 32 ),
			),
			'required'   => array( 'post_id' ),
		),
		'output_schema'       => array(
			'type'       => 'object',
			'additionalProperties' => false,
			'properties' => array(
				'id'             => array( 'type' => 'integer' ),
				'post_type'      => array( 'type' => 'string' ),
				'status'         => array( 'type' => 'string' ),
				'title'          => array( 'type' => 'string' ),
				'slug'           => array( 'type' => 'string' ),
				'excerpt'        => array( 'type' => 'string' ),
				'date'           => array( 'type' => 'string' ),
				'modified'       => array( 'type' => 'string' ),
				'parent'         => array( 'type' => 'integer' ),
				'menu_order'     => array( 'type' => 'integer' ),
				'featured_media_id' => array( 'type' => 'integer', 'minimum' => 0 ),
				'block_count'    => array( 'type' => 'integer' ),
				'freeform_count' => array( 'type' => 'integer' ),
				'state_hash'     => array( 'type' => 'string', 'pattern' => '^[a-f0-9]{64}$' ),
				'content_state_hash' => array( 'type' => 'string', 'pattern' => '^[a-f0-9]{64}$' ),
				'term_assignment' => array(
					'type'       => 'object',
					'additionalProperties' => false,
					'properties' => array(
						'taxonomy'   => array( 'type' => 'string' ),
						'count'      => array( 'type' => 'integer', 'minimum' => 0 ),
						'state_hash' => array( 'type' => 'string', 'pattern' => '^[a-f0-9]{64}$' ),
					),
					'required' => array( 'taxonomy', 'count', 'state_hash' ),
				),
			),
			'required' => array( 'id', 'post_type', 'status', 'title', 'slug', 'excerpt', 'date', 'modified', 'parent', 'menu_order', 'featured_media_id', 'block_count', 'freeform_count', 'state_hash', 'content_state_hash' ),
		),
		'execute_callback'    => 'wpagent_exec_get_post',
		'permission_callback' => 'wpagent_perm_edit_posts',
		'meta'                => array(
			'show_in_rest' => true,
			'annotations'  => array(
				'readonly'    => true,
				'destructive' => false,
				'idempotent'  => true,
			),
		),
	);
}

/**
 * Hashes the exact core-post state guarded by wp-agent/update-post.
 *
 * Modified timestamps are deliberately excluded. WordPress stores them with
 * second precision, while the actual values below detect same-second writes.
 */
function wpagent_post_state_values( WP_Post $post, ?int $featured_media_id = null ): array {
	$result = array(
		'ID'             => (int) $post->ID,
		'post_type'      => (string) $post->post_type,
		'post_author'    => (int) $post->post_author,
		'post_title'     => (string) $post->post_title,
		'post_content'   => (string) $post->post_content,
		'post_excerpt'   => (string) $post->post_excerpt,
		'post_name'      => (string) $post->post_name,
		'post_status'    => (string) $post->post_status,
		'post_date'      => (string) $post->post_date,
		'post_date_gmt'  => (string) $post->post_date_gmt,
		'post_parent'    => (int) $post->post_parent,
		'menu_order'     => (int) $post->menu_order,
		'post_password'  => (string) $post->post_password,
		'comment_status' => (string) $post->comment_status,
		'ping_status'    => (string) $post->ping_status,
		// Featured images are Core post state too. Including the effective ID
		// prevents a status/content write from racing a thumbnail assignment.
		'featured_media_id' => null === $featured_media_id ? (int) get_post_thumbnail_id( $post->ID ) : $featured_media_id,
	);
	return $result;
}

/** @param array<string,mixed> $state */
function wpagent_post_state_hash_from_values( array $state ): string {
	return hash( 'sha256', "wpagently-post-state-v1\0" . serialize( $state ) );
}

function wpagent_post_state_hash( WP_Post $post ): string {
	return wpagent_post_state_hash_from_values( wpagent_post_state_values( $post ) );
}

/** Hashes a post as if exactly this featured-media ID were persisted. */
function wpagent_post_featured_state_hash( WP_Post $post, int $featured_media_id ): string {
	return wpagent_post_state_hash_from_values( wpagent_post_state_values( $post, $featured_media_id ) );
}

/**
 * @param mixed $input
 * @return array|WP_Error
 */
function wpagent_exec_get_post( $input ) {
	$input   = is_array( $input ) ? $input : array();
	$post_id = (int) ( $input['post_id'] ?? 0 );
	// current_user_can( 'edit_post', ... ) löst über WordPress' eigene
	// Meta-Capability-Zuordnung automatisch die richtige Capability für den
	// jeweiligen Post-Type auf (z. B. edit_pages für eine Seite), unabhängig
	// von der groben permission_callback-Schranke oben. Die Prüfung muss vor
	// dem Laden erfolgen, damit fremde und fehlende IDs gleich fail-closed
	// bleiben und weder Inhalt noch Zustands-Hashes preisgeben.
	if ( ! current_user_can( 'edit_post', $post_id ) ) {
		return new WP_Error( 'wpagent_forbidden', __( 'Keine Berechtigung.', 'wp-agent-companion' ), array( 'status' => 403 ) );
	}
	$post = get_post( $post_id );
	if ( ! $post ) {
		return new WP_Error( 'wpagent_forbidden', __( 'Keine Berechtigung.', 'wp-agent-companion' ), array( 'status' => 403 ) );
	}

	$report = wpagent_block_report( $post->post_content );
	$state  = wpagent_post_state_values( $post );
	$content_state = wpagent_markdown_post_full_state( $post_id );
	if ( is_wp_error( $content_state ) ) {
		return $content_state;
	}

	$result = array(
		'id'             => $post_id,
		'post_type'      => $post->post_type,
		'status'         => $post->post_status,
		'title'          => $post->post_title,
		'slug'           => $post->post_name,
		'excerpt'        => $post->post_excerpt,
		'date'           => $post->post_date,
		'modified'       => $post->post_modified,
		'parent'         => (int) $post->post_parent,
		'menu_order'     => (int) $post->menu_order,
		'featured_media_id' => $state['featured_media_id'],
		'block_count'    => $report['block_count'],
		'freeform_count' => $report['freeform_count'],
		'state_hash'     => wpagent_post_state_hash_from_values( $state ),
		'content_state_hash' => wpagent_markdown_post_state_hash( $content_state ),
	);
	if ( array_key_exists( 'term_taxonomy', $input ) ) {
		$taxonomy = (string) $input['term_taxonomy'];
		if ( '' === $taxonomy || ! taxonomy_exists( $taxonomy ) || ! is_object_in_taxonomy( $post->post_type, $taxonomy ) ) {
			return new WP_Error( 'wpagent_bad_term_taxonomy', __( 'Die angeforderte Taxonomie ist für diesen Beitrag nicht verfügbar.', 'wp-agent-companion' ), array( 'status' => 400 ) );
		}
		$tax_object = get_taxonomy( $taxonomy );
		$read_cap   = ( $tax_object && isset( $tax_object->cap->assign_terms ) && '' !== (string) $tax_object->cap->assign_terms )
			? (string) $tax_object->cap->assign_terms
			: 'do_not_allow';
		if ( ! current_user_can( $read_cap ) ) {
			return new WP_Error( 'wpagent_forbidden', __( 'Keine Berechtigung für diese Taxonomie.', 'wp-agent-companion' ), array( 'status' => 403 ) );
		}
		$assignment = wpagent_post_term_assignment_snapshot( $post_id, $taxonomy );
		if ( is_wp_error( $assignment ) ) {
			return $assignment;
		}
		$result['term_assignment'] = array(
			'taxonomy'   => $taxonomy,
			'count'      => count( $assignment['term_ids'] ),
			'state_hash' => wpagent_post_terms_state_hash_from_snapshot( $assignment ),
		);
	}
	return $result;
}
