<?php
/**
 * Ability: wp-agent/list-comments
 *
 * Read-only. Listet Kommentare mit optionalem Beitrags-, Status- und
 * Paginierungs-Filter als schlanke Liste (id, post_id, parent, author,
 * author_email, content, status, date) plus Gesamtzahl. Nutzt get_comments()
 * zweimal: einmal für die paginierten Treffer, einmal mit dem eingebauten
 * 'count' => true Query-Var für die Gesamtzahl (siehe
 * WP_Comment_Query::get_comments()), analog zu list-posts.php, das denselben
 * Zweck über WP_Query::$found_posts löst.
 *
 * Die 'status'-Angabe wird 1:1 an get_comments() durchgereicht (Default
 * 'all' = genehmigt + wartend, ohne Spam/Papierkorb). Akzeptiert u. a.
 * 'hold', 'approve', 'spam', 'trash', 'all' oder 'any' (WordPress' eigenes
 * Vokabular für WP_Comment_Query, siehe deren Doku-Kommentar).
 *
 * @package WPAgentCompanion
 */

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

if ( ! function_exists( 'wpagent_perm_moderate_comments' ) ) {
	/**
	 * Permission callback: der Aufrufer muss Kommentare moderieren dürfen.
	 *
	 * Grobe Schranke für die gesamte Kommentare-Ability-Gruppe (list-comments,
	 * moderate-comment, reply-to-comment, delete-comment). Für Abilities mit
	 * einem konkreten Kommentar-Ziel kommt zusätzlich im execute_callback ein
	 * feingranularer current_user_can( 'edit_comment', $comment_id )-Check dazu,
	 * analog zum edit_post-Feincheck der Content-Lifecycle-Abilities in
	 * update-post.php/get-post.php.
	 *
	 * Absichtlich als eigene, mit function_exists() abgesicherte Definition in
	 * dieser Datei statt in includes/helpers.php: Diese Phase legt ausschließlich
	 * neue Dateien an, geteilte Dateien bleiben der Integrationsphase
	 * vorbehalten. Dieselbe Definition steht identisch (function_exists()-
	 * geschützt) auch in den drei anderen neuen Kommentare-Ability-Dateien,
	 * damit keine Ladereihenfolge zwischen ihnen vorausgesetzt werden muss.
	 *
	 * @param mixed $input Ungenutzter Ability-Input.
	 * @return bool
	 */
	function wpagent_perm_moderate_comments( $input = null ): bool {
		return current_user_can( 'moderate_comments' );
	}
}

function wpagent_ability_list_comments(): array {
	return array(
		'label'               => __( 'Kommentare auflisten', 'wp-agent-companion' ),
		'description'         => __( 'Listet Kommentare mit optionalem Beitrags-, Status- und Paginierungs-Filter als schlanke Liste (id, post_id, parent, author, author_email, content, status, date) plus Gesamtzahl.', 'wp-agent-companion' ),
		'category'            => 'wp-agent',
		'input_schema'        => array(
			'type'                 => 'object',
			'properties'           => array(
				'post_id'  => array(
					'type'        => 'integer', 'minimum' => 1, 'maximum' => 2147483647,
					'description' => __( 'Optional: nur Kommentare zu diesem Beitrag.', 'wp-agent-companion' ),
				),
				'status'   => array(
					'type'        => 'string', 'maxLength' => 20, 'pattern' => '^[a-z0-9_-]{1,20}$',
					'default'     => 'all',
					'description' => __( 'Wird an get_comments() durchgereicht. Akzeptiert u. a. hold, approve, spam, trash, all (Default, ohne Spam/Papierkorb) oder any (wirklich alle Stati).', 'wp-agent-companion' ),
				),
				'per_page' => array(
					'type'    => 'integer', 'minimum' => 1, 'maximum' => 100,
					'default' => 20,
				),
				'page'     => array(
					'type'    => 'integer', 'minimum' => 1, 'maximum' => 10000,
					'default' => 1,
				),
			),
			'additionalProperties' => false,
		),
		'output_schema'       => array(
			'type'                 => 'object',
			'properties'           => array(
				'items' => array(
					'type'  => 'array', 'maxItems' => 100,
					'items' => array(
						'type'                 => 'object',
						'properties'           => array(
							'id'           => array( 'type' => 'integer', 'minimum' => 1, 'maximum' => 2147483647 ),
							'post_id'      => array( 'type' => 'integer', 'minimum' => 0, 'maximum' => 2147483647 ),
							'parent'       => array( 'type' => 'integer', 'minimum' => 0, 'maximum' => 2147483647 ),
							'author'       => array( 'type' => 'string', 'maxLength' => 255 ),
							'author_email' => array( 'type' => 'string', 'maxLength' => 320 ),
							'content'      => array( 'type' => 'string', 'maxLength' => 10000 ),
							'status'       => array( 'type' => 'string', 'maxLength' => 20 ),
							'date'         => array( 'type' => 'string', 'maxLength' => 32 ),
							'state_hash'   => array( 'type' => 'string', 'pattern' => '^[a-f0-9]{64}$' ),
						),
						'required'             => array( 'id', 'post_id', 'parent', 'author', 'author_email', 'content', 'status', 'date', 'state_hash' ),
						'additionalProperties' => false,
					),
				),
				'total' => array( 'type' => 'integer', 'minimum' => 0, 'maximum' => 2147483647 ),
			),
			'required'             => array( 'items', 'total' ),
			'additionalProperties' => false,
		),
		'execute_callback'    => 'wpagent_exec_list_comments',
		'permission_callback' => 'wpagent_perm_moderate_comments',
		'meta'                => array(
			'show_in_rest' => true,
			'annotations'  => array(
				'readonly'    => true,
				'destructive' => false,
				'idempotent'  => true,
			),
		),
	);
}

/**
 * @param mixed $input
 * @return array|WP_Error
 */
function wpagent_exec_list_comments( $input ) {
	$input = is_array( $input ) ? $input : array();

	$post_id = 0;
	if ( array_key_exists( 'post_id', $input ) ) {
		$post_id = wpagent_core_schema_input_integer( $input['post_id'], 1, 2147483647, 'post_id' );
		if ( is_wp_error( $post_id ) ) {
			return $post_id;
		}
	}
	if ( $post_id > 0 && ! current_user_can( 'edit_post', $post_id ) ) {
		return new WP_Error( 'wpagent_forbidden', __( 'Keine Berechtigung für diesen Beitrag.', 'wp-agent-companion' ), array( 'status' => 403 ) );
	}
	if ( $post_id > 0 && ! get_post( $post_id ) ) {
		return new WP_Error( 'wpagent_forbidden', __( 'Keine Berechtigung für diesen Beitrag.', 'wp-agent-companion' ), array( 'status' => 403 ) );
	}

	$status = 'all';
	if ( array_key_exists( 'status', $input ) ) {
		$status = wpagent_core_mutator_bounded_text( $input['status'], 20 );
		if ( is_wp_error( $status ) || 1 !== preg_match( '/^[a-z0-9_-]{1,20}$/D', $status ) ) {
			return new WP_Error( 'wpagent_bad_comment_status', __( 'status ist ungültig.', 'wp-agent-companion' ), array( 'status' => 400 ) );
		}
	}

	$per_page = 20;
	if ( array_key_exists( 'per_page', $input ) ) {
		$per_page = wpagent_core_schema_input_integer( $input['per_page'], 1, 100, 'per_page' );
		if ( is_wp_error( $per_page ) ) {
			return $per_page;
		}
	}

	$page = 1;
	if ( array_key_exists( 'page', $input ) ) {
		$page = wpagent_core_schema_input_integer( $input['page'], 1, 10000, 'page' );
		if ( is_wp_error( $page ) ) {
			return $page;
		}
	}

	$query_args = array(
		'status' => $status,
		'number' => $per_page,
		'paged'  => $page,
		'order'  => 'DESC',
	);
	if ( $post_id > 0 ) {
		$query_args['post_id'] = $post_id;
	}

	$comments = get_comments( $query_args );

	// get_comments() liefert bei 'count' => true direkt einen int zurück (siehe
	// WP_Comment_Query::get_comments()); dieselben Filter OHNE Paginierung
	// liefern so die Gesamtzahl, ohne den WP_Comment_Query-Objektzustand separat
	// auslesen zu müssen (kein Zugriff auf ->found_comments nötig).
	$count_args          = $query_args;
	$count_args['count'] = true;
	unset( $count_args['number'], $count_args['paged'], $count_args['order'] );
	$total = (int) get_comments( $count_args );

	$items = array();
	foreach ( $comments as $comment ) {
		$item = array(
			'id'           => (int) $comment->comment_ID,
			'post_id'      => (int) $comment->comment_post_ID,
			'parent'       => (int) $comment->comment_parent,
			'author'       => wpagent_core_schema_output_text( $comment->comment_author, 255, 'items.author' ),
			'author_email' => wpagent_core_schema_output_text( $comment->comment_author_email, 320, 'items.author_email' ),
			'content'      => wpagent_core_schema_output_text( $comment->comment_content, 10000, 'items.content' ),
			// wp_get_comment_status() liest hier zwar den bereits über
			// get_comments() gecachten Kommentar erneut aus (WP-Objektcache,
			// keine erneute DB-Anfrage), gibt dafür aber garantiert exakt das
			// vom WordPress-Kern selbst definierte Status-Vokabular zurück
			// (approved/unapproved/spam/trash) statt einer eigenen, doppelt
			// gepflegten Neuinterpretation von comment_approved.
			'status'       => wpagent_core_schema_output_text( (string) wp_get_comment_status( $comment->comment_ID ), 20, 'items.status' ),
			'date'         => wpagent_core_schema_output_text( $comment->comment_date, 32, 'items.date' ),
			'state_hash'   => wpagent_comment_state_hash( $comment ),
		);
		foreach ( $item as $value ) {
			if ( is_wp_error( $value ) ) {
				return $value;
			}
		}
		$items[] = $item;
	}
	$items = wpagent_core_schema_output_list( $items, 100, 'items' );
	if ( is_wp_error( $items ) ) {
		return $items;
	}
	if ( $total < 0 || $total > 2147483647 ) {
		return new WP_Error( 'wpagent_core_schema_output_overflow', __( 'Die Gesamtzahl der Kommentare überschreitet die veröffentlichte Sicherheitsgrenze.', 'wp-agent-companion' ), array( 'status' => 500 ) );
	}

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