<?php
/**
 * Ability: wp-agent/switch-theme
 *
 * Wechselt das aktive Theme über switch_theme(). Zwei WordPress-Kern-
 * Fallstricke werden vorab abgefangen, statt switch_theme() blind
 * aufzurufen:
 *
 * 1. wp_get_theme( $stylesheet )->exists() ist die im Auftrag geforderte
 *    Mindestprüfung, fängt aber nur den Fall "Ordner/Theme existiert gar
 *    nicht" ab (Fehlercode theme_not_found). Ein Theme kann jedoch
 *    existieren und trotzdem kaputt sein (fehlende style.css, nicht lesbar,
 *    Child-Theme ohne vorhandenes Parent-Theme, pausiert nach einem
 *    früheren Fatal) -- das meldet WP_Theme::errors() zusätzlich, exists()
 *    dagegen nicht. Ein Wechsel auf so ein Theme würde das Frontend sofort
 *    brechen (genau das Risiko aus dem Auftrag), deshalb wird zusätzlich
 *    errors() geprüft.
 * 2. switch_theme() ruft intern validate_theme_requirements() auf und bei
 *    nicht erfüllten WP-/PHP-Mindestanforderungen (Requires at
 *    least/Requires PHP im Theme-Header) direkt wp_die() auf, statt einen
 *    WP_Error zurückzugeben. wp_die() ist in einem JSON-REST-Kontext ein
 *    härterer, schlechter diagnostizierbarer Fehlschlag als eine saubere
 *    Fehlerantwort. Diese Ability führt dieselbe Prüfung deshalb selbst
 *    vorab aus und ruft switch_theme() bei einem Fehlschlag gar nicht erst
 *    auf.
 *
 * @package WPAgentCompanion
 */

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

/** @return array{template:string,stylesheet:string} */
function wpagent_switch_theme_state(): array {
	return array(
		'template'   => (string) get_template(),
		'stylesheet' => (string) get_stylesheet(),
	);
}

function wpagent_switch_theme_state_matches( array $left, array $right ): bool {
	return (string) ( $left['template'] ?? '' ) === (string) ( $right['template'] ?? '' )
		&& (string) ( $left['stylesheet'] ?? '' ) === (string) ( $right['stylesheet'] ?? '' );
}

function wpagent_ability_switch_theme(): array {
	return array(
		'label'               => __( 'Theme wechseln', 'wp-agent-companion' ),
		'description'         => __( 'Wechselt das aktive Theme unter einem Site-Lock. Lehnt unbekannte, fehlerhafte oder inkompatible Themes ab und verlangt bei einem nicht eindeutig verifizierbaren Hook-Ausgang eine manuelle Recovery.', 'wp-agent-companion' ),
		'category'            => 'wp-agent',
		'input_schema'        => array(
			'type'                 => 'object',
			'properties'           => array(
				'stylesheet' => array(
					'type'        => 'string',
					'maxLength'   => 255,
					'description' => __( 'Ordnername des Themes (Stylesheet), z. B. "twentytwentyfive".', 'wp-agent-companion' ),
				),
				'expected_stylesheet' => array( 'type' => 'string', 'maxLength' => 255 ),
			),
			'required'             => array( 'stylesheet', 'expected_stylesheet' ),
			'additionalProperties' => false,
		),
		'output_schema'       => array(
			'type'       => 'object',
			'properties' => array(
				'stylesheet'          => array( 'type' => 'string' ),
				'previous_stylesheet' => array( 'type' => 'string' ),
				'verified'            => array( 'type' => 'boolean' ),
			),
			'required'             => array( 'stylesheet', 'previous_stylesheet', 'verified' ),
			'additionalProperties' => false,
		),
		'execute_callback'    => 'wpagent_exec_switch_theme',
		'permission_callback' => 'wpagent_perm_switch_themes',
		'meta'                => array(
			'show_in_rest' => true,
			'annotations'  => array(
				'readonly'    => false,
				'destructive' => false,
				'idempotent'  => false,
			),
		),
	);
}

/**
 * @param mixed $input
 * @return array|WP_Error
 */
function wpagent_exec_switch_theme( $input ) {
	$input      = is_array( $input ) ? $input : array();
	$stylesheet = is_string( $input['stylesheet'] ?? null ) ? $input['stylesheet'] : '';

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

	$theme = wp_get_theme( $stylesheet );
	if ( ! $theme->exists() ) {
		return new WP_Error( 'wpagent_no_theme', __( 'Theme nicht gefunden.', 'wp-agent-companion' ), array( 'status' => 404 ) );
	}

	// Siehe Datei-Kommentar Punkt 1: existiert, aber trotzdem kaputt.
	$theme_errors = $theme->errors();
	if ( is_wp_error( $theme_errors ) ) {
		return new WP_Error(
			'wpagent_broken_theme',
			sprintf(
				/* translators: 1: Theme-Stylesheet, 2: Fehlermeldung. */
				__( 'Theme "%1$s" ist fehlerhaft und wird nicht aktiviert: %2$s', 'wp-agent-companion' ),
				$stylesheet,
				$theme_errors->get_error_message()
			),
			array( 'status' => 400 )
		);
	}

	// Siehe Datei-Kommentar Punkt 2: WP-/PHP-Mindestanforderungen vorab
	// prüfen, statt switch_theme() intern wp_die() auslösen zu lassen.
	$requirements = validate_theme_requirements( $stylesheet );
	if ( is_wp_error( $requirements ) ) {
		return new WP_Error( 'wpagent_theme_requirements', $requirements->get_error_message(), array( 'status' => 400 ) );
	}

	$lock = wpagent_core_mutator_acquire_lock( 'theme', 'active' );
	if ( is_wp_error( $lock ) ) {
		return $lock;
	}
	try {
		return wpagent_exec_switch_theme_locked( $stylesheet, $theme, $input, $lock );
	} finally {
		wpagent_core_mutator_release_lock( 'theme', 'active', $lock );
	}
}

/** @return array|WP_Error */
function wpagent_exec_switch_theme_locked( string $stylesheet, WP_Theme $theme, array $input, string $lock ) {
	$previous            = wpagent_switch_theme_state();
	$previous_stylesheet = $previous['stylesheet'];
	if ( ! current_user_can( 'switch_themes' ) ) {
		return new WP_Error( 'wpagent_forbidden', __( 'Die Berechtigung änderte sich während der Sperrübernahme.', 'wp-agent-companion' ), array( 'status' => 403 ) );
	}
	if ( ! is_string( $input['expected_stylesheet'] ?? null ) || ! hash_equals( (string) $input['expected_stylesheet'], $previous_stylesheet ) ) {
		return new WP_Error( 'wpagent_theme_conflict', __( 'Das aktive Theme wurde seit dem Lesen verändert. Lies die Theme-Liste neu.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	$target              = array(
		'template'   => (string) $theme->get_template(),
		'stylesheet' => $stylesheet,
	);
	if ( wpagent_switch_theme_state_matches( $previous, $target ) ) {
		if ( ! wpagent_core_mutator_lock_is_owned( 'theme', 'active', $lock ) ) {
			return wpagent_core_mutator_recovery_required( 'wpagent_switch_theme_recovery_required', __( 'Der Theme-Lock ging verloren. Prüfe das aktive Theme erneut.', 'wp-agent-companion' ), wpagent_core_mutator_hash( wpagent_switch_theme_state() ) );
		}
		return array(
			'stylesheet'          => $stylesheet,
			'previous_stylesheet' => $previous_stylesheet,
			'verified'            => true,
		);
	}

	if ( ! wpagent_core_mutator_refresh_lock( 'theme', 'active', $lock ) ) {
		return new WP_Error( 'wpagent_theme_busy', __( 'Die Theme-Sperre ging vor dem Wechsel verloren.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	if ( ! current_user_can( 'switch_themes' ) ) {
		return new WP_Error( 'wpagent_forbidden', __( 'Die Berechtigung ging unmittelbar vor dem Theme-Wechsel verloren.', 'wp-agent-companion' ), array( 'status' => 403 ) );
	}
	try {
		switch_theme( $stylesheet );
	} catch ( \Throwable ) {
		return wpagent_core_mutator_recovery_required( 'wpagent_switch_theme_recovery_required', __( 'Der Theme-Wechsel führte ausführbaren Fremdcode aus und endete nicht eindeutig. Prüfe das aktive Theme manuell.', 'wp-agent-companion' ), wpagent_core_mutator_hash( wpagent_switch_theme_state() ) );
	}

	$fresh_state = wpagent_switch_theme_state();
	if ( ! wpagent_switch_theme_state_matches( $fresh_state, $target ) || ! wpagent_core_mutator_lock_is_owned( 'theme', 'active', $lock ) ) {
		return wpagent_core_mutator_recovery_required( 'wpagent_switch_theme_recovery_required', __( 'Der Theme-Wechsel konnte nach der Ausführung nicht eindeutig bestätigt werden. Prüfe das aktive Theme manuell.', 'wp-agent-companion' ), wpagent_core_mutator_hash( $fresh_state ) );
	}

	return array(
		'stylesheet'          => $fresh_state['stylesheet'],
		'previous_stylesheet' => $previous_stylesheet,
		'verified'            => true,
	);
}
