<?php
/**
 * Ability: wp-agent/moderate-comment
 *
 * Setzt den Status eines bestehenden Kommentars direkt (approve, hold, spam,
 * trash) über wp_set_comment_status() und verifiziert per Re-Read über
 * wp_get_comment_status().
 *
 * Fallstrick, den diese Ability explizit ausgleicht: wp_set_comment_status()
 * nimmt als Eingabe-Vokabular 'approve'/'hold'/'spam'/'trash' entgegen,
 * wp_get_comment_status() liefert beim Zurücklesen aber ein ANDERES
 * Vokabular ('approved'/'unapproved'/'spam'/'trash'; 'hold' wird zu
 * 'unapproved'). Ein naiver String-Vergleich der beiden Werte würde also
 * ausgerechnet für approve/hold IMMER fehlschlagen, obwohl der Status
 * korrekt gesetzt wurde. Die Verifikation bildet deshalb explizit auf das
 * Rückgabe-Vokabular ab, bevor sie vergleicht.
 *
 * @package WPAgentCompanion
 */

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

if ( ! function_exists( 'wpagent_perm_moderate_comments' ) ) {
	/**
	 * Permission callback: der Aufrufer muss Kommentare moderieren dürfen.
	 *
	 * Siehe die ausführliche Begründung in list-comments.php (identische,
	 * function_exists()-geschützte Definition in allen vier 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_moderate_comment(): array {
	return array(
		'label'               => __( 'Kommentar moderieren', 'wp-agent-companion' ),
		'description'         => __( 'Setzt den Status eines Kommentars konfliktgeschützt (approve, hold, spam, trash). expected_state_hash muss aus list-comments stammen; die vollständige Kommentarzeile wird vor dem Statuswechsel per Datenbank-CAS gebunden und per Re-Read verifiziert.', 'wp-agent-companion' ),
		'category'            => 'wp-agent',
		'input_schema'        => array(
			'type'                 => 'object',
			'properties'           => array(
				'comment_id'          => array( 'type' => 'integer', 'minimum' => 1, 'maximum' => 2147483647 ),
				'status'     => array(
					'type' => 'string',
					'enum' => array( 'approve', 'hold', 'spam', 'trash' ),
				),
				'expected_state_hash' => array( 'type' => 'string', 'pattern' => '^[a-f0-9]{64}$' ),
			),
			'required'             => array( 'comment_id', 'status', 'expected_state_hash' ),
			'additionalProperties' => false,
		),
		'output_schema'       => array(
			'type'                 => 'object',
			'properties'           => array(
				'comment_id'          => array( 'type' => 'integer', 'minimum' => 1, 'maximum' => 2147483647 ),
				'status'              => array( 'type' => 'string', 'enum' => array( 'approved', 'unapproved', 'spam', 'trash' ) ),
				'verified'   => array( 'type' => 'boolean' ),
				'previous_state_hash' => array( 'type' => 'string', 'pattern' => '^[a-f0-9]{64}$' ),
				'state_hash'          => array( 'type' => 'string', 'pattern' => '^[a-f0-9]{64}$' ),
			),
			'required'             => array( 'comment_id', 'status', 'verified', 'previous_state_hash', 'state_hash' ),
			'additionalProperties' => false,
		),
		'execute_callback'    => 'wpagent_exec_moderate_comment',
		'permission_callback' => 'wpagent_perm_moderate_comments',
		'meta'                => array(
			'show_in_rest' => true,
			'annotations'  => array(
				'readonly'    => false,
				'destructive' => false,
				'idempotent'  => false,
			),
		),
	);
}

/**
 * @param mixed $input
 * @return array|WP_Error
 */
function wpagent_exec_moderate_comment( $input ) {
	$input = is_array( $input ) ? $input : array();
	if ( ! wpagent_core_mutator_is_strict_integer( $input['comment_id'] ?? null ) || (int) $input['comment_id'] < 1 || (int) $input['comment_id'] > 2147483647 ) {
		return new WP_Error( 'wpagent_bad_comment_id', __( 'comment_id muss eine positive Ganzzahl sein.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	$comment_id = (int) $input['comment_id'];
	// edit_comment is a target-aware WordPress meta capability. It has to run
	// before the snapshot so inaccessible and missing IDs do not reveal a
	// comment state hash or a different existence response.
	if ( ! current_user_can( 'edit_comment', $comment_id ) ) {
		return new WP_Error( 'wpagent_forbidden', __( 'Keine Berechtigung.', 'wp-agent-companion' ), array( 'status' => 403 ) );
	}
	$expected_state_hash = wpagent_comment_expected_hash( $input );
	if ( is_wp_error( $expected_state_hash ) ) {
		return $expected_state_hash;
	}
	$comment = wpagent_comment_snapshot( $comment_id );
	if ( is_wp_error( $comment ) ) {
		return new WP_Error( 'wpagent_forbidden', __( 'Keine Berechtigung.', 'wp-agent-companion' ), array( 'status' => 403 ) );
	}
	$status  = is_string( $input['status'] ?? null ) ? $input['status'] : '';
	$allowed = array( 'approve', 'hold', 'spam', 'trash' );
	if ( ! in_array( $status, $allowed, true ) ) {
		/* translators: %s: submitted comment status. */
		return new WP_Error( 'wpagent_bad_status', sprintf( __( 'Ungültiger Status "%s".', 'wp-agent-companion' ), $status ), array( 'status' => 400 ) );
	}

	// Eingabe-Vokabular (wp_set_comment_status) -> Rückgabe-Vokabular
	// (wp_get_comment_status), siehe Datei-Kommentar oben.
	$expected_label = array(
		'approve' => 'approved',
		'hold'    => 'unapproved',
		'spam'    => 'spam',
		'trash'   => 'trash',
	);

	$blog_id = (int) get_current_blog_id();
	$lock = wpagent_comment_acquire_lock( $comment_id );
	if ( is_wp_error( $lock ) ) {
		return $lock;
	}
	try {
		if ( $blog_id !== (int) get_current_blog_id() || ! wpagent_comment_refresh_lock( $comment_id, $lock ) || ! wpagent_comment_lock_is_owned( $comment_id, $lock ) ) {
			return new WP_Error( 'wpagent_comment_locked', __( 'Die Sperre ging vor der Moderation verloren. Es wurde nichts geändert.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		}
		$current = wpagent_comment_snapshot( $comment_id );
		if ( is_wp_error( $current ) ) {
			return new WP_Error( 'wpagent_forbidden', __( 'Keine Berechtigung.', 'wp-agent-companion' ), array( 'status' => 403 ) );
		}
		if ( ! current_user_can( 'edit_comment', $comment_id ) ) {
			return new WP_Error( 'wpagent_forbidden', __( 'Keine Berechtigung.', 'wp-agent-companion' ), array( 'status' => 403 ) );
		}
		$previous_state_hash = wpagent_comment_state_hash( $current );
		if ( ! hash_equals( $expected_state_hash, $previous_state_hash ) ) {
			return wpagent_comment_conflict( $previous_state_hash );
		}

		$current_status = wp_get_comment_status( $current );
		if ( $expected_label[ $status ] === $current_status ) {
			if ( $blog_id !== (int) get_current_blog_id() || ! wpagent_comment_refresh_lock( $comment_id, $lock ) || ! wpagent_comment_lock_is_owned( $comment_id, $lock ) || ! current_user_can( 'edit_comment', $comment_id ) ) {
				return new WP_Error( 'wpagent_comment_conflict', __( 'Der Kommentar änderte sich während der No-op-Prüfung. Lies ihn neu.', 'wp-agent-companion' ), array( 'status' => 409 ) );
			}
			$confirmed = wpagent_comment_snapshot( $comment_id );
			if ( is_wp_error( $confirmed ) || ! hash_equals( $previous_state_hash, is_wp_error( $confirmed ) ? '' : wpagent_comment_state_hash( $confirmed ) ) ) {
				return wpagent_comment_conflict( is_wp_error( $confirmed ) ? '' : wpagent_comment_state_hash( $confirmed ) );
			}
			return array(
				'comment_id'          => $comment_id,
				'status'              => $current_status,
				'verified'            => true,
				'previous_state_hash' => $previous_state_hash,
				'state_hash'          => $previous_state_hash,
			);
		}

		if ( $blog_id !== (int) get_current_blog_id() || ! current_user_can( 'edit_comment', $comment_id ) || ! wpagent_comment_refresh_lock( $comment_id, $lock ) || ! wpagent_comment_lock_is_owned( $comment_id, $lock ) ) {
			return new WP_Error( 'wpagent_comment_locked', __( 'Berechtigung oder Sperre ging vor der Moderation verloren. Es wurde nichts geändert.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		}
		$prewrite = wpagent_comment_snapshot( $comment_id );
		if ( is_wp_error( $prewrite ) ) {
			return new WP_Error( 'wpagent_forbidden', __( 'Keine Berechtigung.', 'wp-agent-companion' ), array( 'status' => 403 ) );
		}
		if ( ! hash_equals( $previous_state_hash, wpagent_comment_state_hash( $prewrite ) ) ) {
			return wpagent_comment_conflict( wpagent_comment_state_hash( $prewrite ) );
		}
		do_action( 'wpagent_comment_before_status_write', $comment_id, $status, $prewrite );
		$written = wpagent_comment_update_status_if_current( $prewrite, $status );
		if ( false === $written ) {
			$fresh_conflict = wpagent_comment_snapshot( $comment_id );
			return wpagent_comment_conflict( is_wp_error( $fresh_conflict ) ? '' : wpagent_comment_state_hash( $fresh_conflict ) );
		}
		if ( is_wp_error( $written ) ) {
			return wpagent_comment_recovery_required( $comment_id, __( 'Der Kommentarstatus wurde geschrieben, kann aber nicht sicher zurückgelesen werden. Prüfe ihn manuell.', 'wp-agent-companion' ) );
		}
		$fresh = wpagent_comment_snapshot( $comment_id );
		$fresh_hash = is_wp_error( $fresh ) ? '' : wpagent_comment_state_hash( $fresh );
		if ( is_wp_error( $fresh ) || $blog_id !== (int) get_current_blog_id() || ! current_user_can( 'edit_comment', $comment_id ) || ! wpagent_comment_lock_is_owned( $comment_id, $lock ) || $expected_label[ $status ] !== wp_get_comment_status( $fresh->comment_ID ) ) {
			return wpagent_comment_recovery_required( $comment_id, __( 'Der Kommentarstatus konnte nach dem Schreiben nicht vollständig verifiziert werden. Prüfe ihn manuell.', 'wp-agent-companion' ), $fresh_hash );
		}
		return array(
			'comment_id'          => $comment_id,
			'status'              => $expected_label[ $status ],
			'verified'            => true,
			'previous_state_hash' => $previous_state_hash,
			'state_hash'          => $fresh_hash,
		);
	} finally {
		wpagent_comment_release_lock_for_blog( $comment_id, $blog_id, $lock );
	}
}
