<?php
/**
 * Ability: wp-agent/refresh-hooks
 *
 * Feuert die Admin-/Save-only-Nacharbeiten für AT-4 (Hook-Gap): Wenn Inhalte
 * über die REST-/Abilities-Schicht statt über den Editor geschrieben werden,
 * bleiben einige der Nacharbeiten aus, die WordPress sonst beim Speichern im
 * Admin auslöst. Diese Ability holt sie explizit nach:
 *
 * 1. Optional flush_rewrite_rules(false), nur bei ausdrücklichem
 *    rebuild_rewrite_rules=true
 * 2. clean_post_cache für die zuletzt geänderten Posts
 * 3. Falls Rank Math aktiv ist: Sitemap-Cache invalidieren
 *
 * @package WPAgentCompanion
 */

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

function wpagent_ability_refresh_hooks(): array {
	return array(
		'label'               => __( 'Save-Nacharbeiten nachholen (Hook-Gap AT-4)', 'wp-agent-companion' ),
		'description'         => __( 'Holt Save-Nacharbeiten nach: Post-Cache leeren, Rank-Math-Sitemap-Cache invalidieren und optional Rewrite-Regeln ausdrücklich neu aufbauen.', 'wp-agent-companion' ),
		'category'            => 'wp-agent',
		'input_schema'        => array(
			'type'                 => 'object',
			'properties'           => array(
				'limit'    => array(
					'type'        => 'integer',
					'minimum'     => 1,
					'maximum'     => 100,
					'description' => __( 'Anzahl der zuletzt geänderten Posts, deren Cache geleert wird (1-100, Default 20).', 'wp-agent-companion' ),
					'default'     => 20,
				),
				'post_ids' => array(
					'type'        => 'array',
					'maxItems'    => 100,
					'description' => __( 'Optionale explizite Post-IDs; wenn gesetzt, wird statt der zuletzt geänderten Posts genau diese Liste geleert.', 'wp-agent-companion' ),
					'items'       => array( 'type' => 'integer', 'minimum' => 1 ),
				),
				'rebuild_rewrite_rules' => array(
					'type'        => 'boolean',
					'description' => __( 'Rewrite-Regeln ausdrücklich neu aufbauen. Für normale Beitrags-Saves nicht erforderlich und deshalb standardmäßig aus.', 'wp-agent-companion' ),
					'default'     => false,
				),
			),
			'additionalProperties' => false,
		),
		'output_schema'       => array(
			'type'                 => 'object',
			'maxProperties'        => 3,
			'properties' => array(
				'actions'          => array(
					'type'     => 'array',
					'maxItems' => 3,
					'items'    => array( 'type' => 'string', 'minLength' => 1, 'maxLength' => 200 ),
				),
				'cleaned_post_ids' => array(
					'type'     => 'array',
					'maxItems' => 100,
					'items'    => array( 'type' => 'integer', 'minimum' => 1 ),
				),
				'rankmath'         => array(
					'type'                 => 'object',
					'maxProperties'        => 3,
					'additionalProperties' => false,
					'properties'           => array(
						'active'       => array( 'type' => 'boolean' ),
						'method'       => array( 'type' => array( 'string', 'null' ), 'enum' => array( null, 'RankMath\\Sitemap\\Cache::invalidate_storage()', 'RankMath\\Sitemap\\Cache_Watcher::clear()' ) ),
						'cli_fallback' => array( 'type' => 'string', 'enum' => array( 'wp rankmath sitemap generate' ), 'maxLength' => 32 ),
					),
					'required'             => array( 'active', 'method', 'cli_fallback' ),
				),
			),
			'required'             => array( 'actions', 'cleaned_post_ids', 'rankmath' ),
			'additionalProperties' => false,
		),
		'execute_callback'    => 'wpagent_exec_refresh_hooks',
		'permission_callback' => 'wpagent_perm_edit_posts',
		'meta'                => array(
			'show_in_rest' => true,
			'annotations'  => array(
				'readonly'    => false,
				'destructive' => false,
				'idempotent'  => false,
			),
		),
	);
}

/**
 * @param mixed $input
 * @return array|WP_Error
 */
function wpagent_exec_refresh_hooks( $input ) {
	$input   = is_array( $input ) ? $input : array();
	$actions = array();
	$lock = wpagent_core_mutator_acquire_lock( 'refresh-hooks', 'global' );
	if ( is_wp_error( $lock ) ) {
		return $lock;
	}
	try {
		if ( ! current_user_can( 'edit_posts' ) ) {
			return new WP_Error( 'wpagent_forbidden', __( 'Die Berechtigung änderte sich während der Sperrübernahme.', 'wp-agent-companion' ), array( 'status' => 403 ) );
		}

	// Rewrite-Regeln sind global und ändern sich bei normalen Beitrags-Saves
	// nicht. Der teure Rebuild läuft deshalb nur bei ausdrücklicher Anforderung.
	if ( true === ( $input['rebuild_rewrite_rules'] ?? false ) ) {
		if ( ! wpagent_core_mutator_refresh_lock( 'refresh-hooks', 'global', $lock ) ) {
			return new WP_Error( 'wpagent_refresh_hooks_busy', __( 'Die Wartungs-Sperre ging vor dem Rewrite-Rebuild verloren.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		}
		try {
			flush_rewrite_rules( false );
		} catch ( \Throwable ) {
			return wpagent_core_mutator_recovery_required( 'wpagent_refresh_hooks_recovery_required', __( 'Der Rewrite-Rebuild endete nicht eindeutig. Prüfe die Site-Wartung manuell.', 'wp-agent-companion' ), '' );
		}
		$actions[] = 'flush_rewrite_rules(false)';
	}

	// 2. Post-Cache leeren: entweder die explizit übergebenen IDs oder die
	//    zuletzt geänderten Posts.
	$post_ids = array();
	if ( isset( $input['post_ids'] ) && is_array( $input['post_ids'] ) ) {
		foreach ( $input['post_ids'] as $pid ) {
			if ( ! is_int( $pid ) || $pid < 1 ) {
				return new WP_Error( 'wpagent_refresh_hooks_post_ids', __( 'post_ids darf nur positive Ganzzahlen enthalten.', 'wp-agent-companion' ), array( 'status' => 400 ) );
			}
			$post_ids[] = $pid;
		}
		$post_ids = array_values( array_unique( $post_ids ) );
	}

	if ( array() === $post_ids ) {
		$limit    = isset( $input['limit'] ) ? (int) $input['limit'] : 20;
		$limit    = max( 1, min( 100, $limit ) );
		$post_ids = get_posts(
			array(
				'post_type'        => 'any',
				'post_status'      => 'any',
				'orderby'          => 'modified',
				'order'            => 'DESC',
				'numberposts'      => $limit,
				'fields'           => 'ids',
				'suppress_filters' => true,
			)
		);
		$post_ids = array_map( 'intval', $post_ids );
	}

	if ( ! wpagent_core_mutator_refresh_lock( 'refresh-hooks', 'global', $lock ) ) {
		return new WP_Error( 'wpagent_refresh_hooks_busy', __( 'Die Wartungs-Sperre ging vor dem Cache-Leeren verloren.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	try {
		foreach ( $post_ids as $pid ) {
			clean_post_cache( $pid );
		}
	} catch ( \Throwable ) {
		return wpagent_core_mutator_recovery_required( 'wpagent_refresh_hooks_recovery_required', __( 'Das Cache-Leeren endete nicht eindeutig. Prüfe die Site-Wartung manuell.', 'wp-agent-companion' ), '' );
	}
	$actions[] = 'clean_post_cache x' . count( $post_ids );

	// 3. Rank-Math-Sitemap-Cache invalidieren, falls Rank Math aktiv ist.
	try {
		$rankmath = wpagent_refresh_rankmath_sitemap( $actions );
	} catch ( \Throwable ) {
		return wpagent_core_mutator_recovery_required( 'wpagent_refresh_hooks_recovery_required', __( 'Die Sitemap-Invalidierung endete nicht eindeutig. Prüfe die Site-Wartung manuell.', 'wp-agent-companion' ), '' );
	}
	if ( ! wpagent_core_mutator_lock_is_owned( 'refresh-hooks', 'global', $lock ) ) {
		return wpagent_core_mutator_recovery_required( 'wpagent_refresh_hooks_recovery_required', __( 'Die Wartungs-Sperre ging nach der Ausführung verloren. Prüfe die Site-Wartung manuell.', 'wp-agent-companion' ), '' );
	}

	return array(
		'actions'          => $actions,
		'cleaned_post_ids' => $post_ids,
		'rankmath'         => $rankmath,
	);
	} finally {
		wpagent_core_mutator_release_lock( 'refresh-hooks', 'global', $lock );
	}
}

/**
 * Invalidiert den Rank-Math-Sitemap-Cache, sofern Rank Math aktiv ist.
 *
 * Bevorzugt die statische Methode RankMath\Sitemap\Cache::invalidate_storage(),
 * fällt sonst auf RankMath\Sitemap\Cache_Watcher::clear() zurück. Wenn keine
 * der Methoden vorhanden ist, bleibt der dokumentierte WP-CLI-Fallback
 * `wp rankmath sitemap generate`.
 *
 * @param array $actions Referenz auf die Aktionsliste (wird ergänzt).
 * @return array{active:bool,method:?string,cli_fallback:string}
 */
function wpagent_refresh_rankmath_sitemap( array &$actions ): array {
	$active = ( defined( 'RANK_MATH_VERSION' ) || function_exists( 'rank_math' ) || class_exists( 'RankMath\\Helper' ) );
	$result = array(
		'active'       => $active,
		'method'       => null,
		'cli_fallback' => 'wp rankmath sitemap generate',
	);

	if ( ! $active ) {
		return $result;
	}

	if ( is_callable( array( 'RankMath\\Sitemap\\Cache', 'invalidate_storage' ) ) ) {
		call_user_func( array( 'RankMath\\Sitemap\\Cache', 'invalidate_storage' ) );
		$result['method'] = 'RankMath\\Sitemap\\Cache::invalidate_storage()';
	} elseif ( is_callable( array( 'RankMath\\Sitemap\\Cache_Watcher', 'clear' ) ) ) {
		call_user_func( array( 'RankMath\\Sitemap\\Cache_Watcher', 'clear' ) );
		$result['method'] = 'RankMath\\Sitemap\\Cache_Watcher::clear()';
	}

	$actions[] = null !== $result['method']
		? 'rankmath_sitemap: ' . $result['method']
		: 'rankmath_sitemap: keine Cache-Methode gefunden, WP-CLI-Fallback `wp rankmath sitemap generate`';

	return $result;
}
