<?php
/**
 * Ability: wp-agent/activate-plugin
 *
 * Aktiviert ein installiertes Plugin. activate_plugin() im WordPress-Kern
 * bindet die Plugin-Hauptdatei bei einer Aktivierung, die noch nicht aktiv
 * war, direkt per include_once ein und feuert die
 * activate_plugin/activate_{plugin}-Hooks; ein fehlerhaftes Plugin kann dabei
 * einen fatalen Fehler auslösen. Seit PHP 7 sind die meisten früheren
 * "Fatal error"-Fälle (Klasse nicht gefunden, Typfehler, Parse-Fehler bei
 * include) als Throwable katchbar, deshalb fängt diese Ability sie explizit
 * ab, statt den ganzen REST-Request mitzureißen. Echte, nicht abfangbare
 * PHP-Fatals (Speicherlimit, Timeout) bleiben ein WordPress-Kern-Grenzfall,
 * den keine Ability-Ebene lösen kann.
 *
 * Der von der wp-admin-UI genutzte Redirect-Trick (activate_plugin() setzt
 * bei einem nicht-leeren $redirect-Parameter VOR dem riskanten include per
 * wp_redirect() bereits einen Location-Header, damit der Browser bei einem
 * echten Fatal wenigstens auf eine Fehler-Anzeige-Seite umgeleitet wird) ist
 * für diesen JSON-REST-Kontext wirkungslos: es gibt hier keinen Browser, der
 * einem Location-Header folgt. Der Parameter bleibt deshalb bewusst leer;
 * der Throwable-Fang und die explizite WP_Error-Prüfung sind der eigentliche
 * Schutz.
 *
 * wp-agent-companion und wp-agent-power sind von dieser Ability
 * ausgeschlossen: das (De-)Aktivieren dieser beiden Plugins bleibt
 * ausschließlich manuellem Betreiber-Zugriff vorbehalten, damit sich der
 * Agent nicht selbst die eigene Grundlage entziehen oder umkonfigurieren
 * kann.
 *
 * @package WPAgentCompanion
 */

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

function wpagent_ability_activate_plugin(): array {
	return array(
		'label'               => __( 'Plugin aktivieren', 'wp-agent-companion' ),
		'description'         => __( 'Aktiviert ein installiertes Plugin und verifiziert per Re-Read (is_plugin_active), dass es aktiv ist. Fängt fatale Fehler aus einem fehlerhaften Plugin ab, statt den Request abstürzen zu lassen. wp-agent-companion und wp-agent-power sind ausgenommen.', 'wp-agent-companion' ),
		'category'            => 'wp-agent',
		'input_schema'        => array(
			'type'                 => 'object',
			'properties'           => array(
				'plugin_file' => array(
					'type'        => 'string',
					'maxLength'   => 255,
					'description' => __( 'Relativer Plugin-Pfad wie von get_plugins() geliefert, z. B. "akismet/akismet.php".', 'wp-agent-companion' ),
				),
				'expected_active'  => array( 'type' => 'boolean' ),
				'expected_version' => array( 'type' => 'string', 'maxLength' => 255 ),
			),
			'required'             => array( 'plugin_file', 'expected_active', 'expected_version' ),
			'additionalProperties' => false,
		),
		'output_schema'       => array(
			'type'                 => 'object',
			'properties'           => array(
				'plugin_file' => array( 'type' => 'string' ),
				'active'      => array( 'type' => 'boolean' ),
				'version'     => array( 'type' => 'string' ),
				'verified'    => array( 'type' => 'boolean' ),
			),
			'required'             => array( 'plugin_file', 'active', 'version', 'verified' ),
			'additionalProperties' => false,
		),
		'execute_callback'    => 'wpagent_exec_activate_plugin',
		'permission_callback' => 'wpagent_perm_activate_plugins',
		'meta'                => array(
			'show_in_rest' => true,
			'annotations'  => array(
				'readonly'    => false,
				'destructive' => false,
				'idempotent'  => false,
			),
		),
	);
}

/** @return array{active:bool,version:string}|null */
function wpagent_plugin_mutator_state( string $plugin_file ): ?array {
	if ( ! function_exists( 'get_plugins' ) ) {
		require_once ABSPATH . 'wp-admin/includes/plugin.php';
	}
	$installed = get_plugins();
	if ( ! isset( $installed[ $plugin_file ] ) || ! is_array( $installed[ $plugin_file ] ) ) {
		return null;
	}
	return array(
		'active'  => is_plugin_active( $plugin_file ),
		'version' => (string) ( $installed[ $plugin_file ]['Version'] ?? '' ),
	);
}

/** @param array{active:bool,version:string} $state */
function wpagent_plugin_mutator_matches_expected( array $input, array $state ): bool {
	return is_bool( $input['expected_active'] ?? null )
		&& is_string( $input['expected_version'] ?? null )
		&& (bool) $input['expected_active'] === $state['active']
		&& hash_equals( (string) $input['expected_version'], $state['version'] );
}

/** @return WP_Error */
function wpagent_plugin_mutator_recovery_required( string $action, string $plugin_file, ?array $current = null ): WP_Error {
	return wpagent_core_mutator_recovery_required(
		'wpagent_plugin_' . $action . '_recovery_required',
		__( 'Der Plugin-Vorgang führte fremden Plugin-Code aus und endete nicht eindeutig. Prüfe den aktuellen Plugin-Zustand manuell und wiederhole ihn nicht automatisch.', 'wp-agent-companion' ),
		is_array( $current ) ? wpagent_core_mutator_hash( array( 'plugin_file' => $plugin_file, 'state' => $current ) ) : ''
	);
}

/**
 * @param mixed $input
 * @return array|WP_Error
 */
function wpagent_exec_activate_plugin( $input ) {
	$input       = is_array( $input ) ? $input : array();
	$plugin_file = (string) ( $input['plugin_file'] ?? '' );

	if ( '' === trim( $plugin_file ) ) {
		return new WP_Error( 'wpagent_missing_plugin', __( 'plugin_file fehlt.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}

	if ( ! is_array( wpagent_plugin_mutator_state( $plugin_file ) ) ) {
		return new WP_Error( 'wpagent_no_plugin', __( 'Plugin nicht gefunden.', 'wp-agent-companion' ), array( 'status' => 404 ) );
	}

	// Siehe Datei-Kommentar: wp-agent-companion und wp-agent-power verwalten
	// sich nicht über diese Ability selbst, in keine der beiden Richtungen.
	$protected = array( 'wp-agent-companion/wp-agent-companion.php', 'wp-agent-power/wp-agent-power.php' );
	if ( in_array( $plugin_file, $protected, true ) ) {
		return new WP_Error(
			'wpagent_protected_plugin',
			__( 'wp-agent-companion und wp-agent-power können über diese Ability nicht (de-)aktiviert werden.', 'wp-agent-companion' ),
			array( 'status' => 400 )
		);
	}

	$lock = wpagent_core_mutator_acquire_lock( 'plugin', $plugin_file );
	if ( is_wp_error( $lock ) ) {
		return $lock;
	}
	try {
		$current = wpagent_plugin_mutator_state( $plugin_file );
		if ( ! is_array( $current ) ) {
			return new WP_Error( 'wpagent_no_plugin', __( 'Plugin wurde während der Sperrübernahme entfernt.', 'wp-agent-companion' ), array( 'status' => 404 ) );
		}
		if ( ! current_user_can( 'activate_plugins' ) ) {
			return new WP_Error( 'wpagent_forbidden', __( 'Die Berechtigung änderte sich während der Sperrübernahme.', 'wp-agent-companion' ), array( 'status' => 403 ) );
		}
		if ( ! wpagent_plugin_mutator_matches_expected( $input, $current ) ) {
			return new WP_Error( 'wpagent_plugin_conflict', __( 'Das Plugin wurde seit dem Lesen verändert. Lies die Plugin-Liste neu.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		}
		if ( $current['active'] ) {
			if ( ! wpagent_core_mutator_lock_is_owned( 'plugin', $plugin_file, $lock ) ) {
				return wpagent_plugin_mutator_recovery_required( 'activation', $plugin_file, $current );
			}
			return array( 'plugin_file' => $plugin_file, 'active' => true, 'version' => $current['version'], 'verified' => true );
		}
		if ( ! wpagent_core_mutator_refresh_lock( 'plugin', $plugin_file, $lock ) ) {
			return new WP_Error( 'wpagent_plugin_busy', __( 'Die Plugin-Sperre ging vor der Aktivierung verloren.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		}
		if ( ! current_user_can( 'activate_plugins' ) ) {
			return new WP_Error( 'wpagent_forbidden', __( 'Die Berechtigung ging unmittelbar vor der Aktivierung verloren.', 'wp-agent-companion' ), array( 'status' => 403 ) );
		}
		try {
			$result = activate_plugin( $plugin_file );
		} catch ( \Throwable ) {
			return wpagent_plugin_mutator_recovery_required( 'activation', $plugin_file, wpagent_plugin_mutator_state( $plugin_file ) );
		}
		if ( is_wp_error( $result ) ) {
			return wpagent_plugin_mutator_recovery_required( 'activation', $plugin_file, wpagent_plugin_mutator_state( $plugin_file ) );
		}
		$fresh = wpagent_plugin_mutator_state( $plugin_file );
		if ( ! is_array( $fresh ) || ! $fresh['active'] || ! hash_equals( $current['version'], $fresh['version'] ) || ! wpagent_core_mutator_lock_is_owned( 'plugin', $plugin_file, $lock ) ) {
			return wpagent_plugin_mutator_recovery_required( 'activation', $plugin_file, $fresh );
		}
		return array( 'plugin_file' => $plugin_file, 'active' => true, 'version' => $fresh['version'], 'verified' => true );
	} finally {
		wpagent_core_mutator_release_lock( 'plugin', $plugin_file, $lock );
	}
}
