<?php
/**
 * Ability: wp-agent/assign-menu-location
 *
 * Weist ein Menü einer vom aktiven Theme registrierten Position (nav menu
 * location) zu. Positionen sind KEINE Datenbank-Entität: sie kommen aus dem
 * Laufzeit-Array $_wp_registered_nav_menus, das nur ein Theme (oder ein
 * Must-Use-Plugin) über register_nav_menu()/register_nav_menus() befüllt,
 * typischerweise im 'after_setup_theme'-Hook. Die eigentliche Zuordnung
 * Position -> Menü-ID liegt dagegen im Theme-Mod 'nav_menu_locations' (ein
 * assoziatives Array, per get_theme_mod()/set_theme_mod() gelesen/geschrieben)
 * und übersteht damit auch einen Theme-Wechsel nicht automatisch.
 *
 * Permission-Callback wpagent_perm_edit_theme_options ist in create-menu.php
 * definiert (siehe dortiger Kommentar); diese Datei referenziert sie nur.
 *
 * @package WPAgentCompanion
 */

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

function wpagent_ability_assign_menu_location(): array {
	return array(
		'label'               => __( 'Menü einer Position zuweisen', 'wp-agent-companion' ),
		'description'         => __( 'Weist ein Menü einer vom aktiven Theme registrierten Position zu (z. B. "primary"). Die Anfrage bindet das aktive Theme und den vollständigen Theme-Mod-Zustand, schreibt per Compare-and-Swap und verifiziert den exakten Read-back.', 'wp-agent-companion' ),
		'category'            => 'wp-agent',
		'input_schema'        => array(
			'type'       => 'object',
			'properties' => array(
				'menu_id'  => array( 'type' => 'integer', 'minimum' => 1 ),
				'location' => array(
					'type'        => 'string',
					'minLength'   => 1,
					'maxLength'   => 64,
					'pattern'     => '^[A-Za-z0-9_-]+$',
					'description' => __( 'Muss eine vom aktiven Theme registrierte Position sein (siehe get_registered_nav_menus()).', 'wp-agent-companion' ),
				),
				'expected_state_hash' => array( 'type' => 'string', 'pattern' => '^[a-f0-9]{64}$' ),
			),
			'required'   => array( 'menu_id', 'location', 'expected_state_hash' ),
			'additionalProperties' => false,
		),
		'output_schema'       => array(
			'type'       => 'object',
			'properties' => array(
				'location'         => array( 'type' => 'string' ),
				'menu_id'          => array( 'type' => 'integer' ),
				'previous_menu_id' => array( 'type' => 'integer' ),
				'state_hash'       => array( 'type' => 'string', 'pattern' => '^[a-f0-9]{64}$' ),
				'verified'         => array( 'type' => 'boolean' ),
			),
			'required'              => array( 'location', 'menu_id', 'previous_menu_id', 'state_hash', 'verified' ),
			'additionalProperties'  => false,
		),
		'execute_callback'    => 'wpagent_exec_assign_menu_location',
		'permission_callback' => 'wpagent_perm_edit_theme_options',
		'meta'                => array(
			'show_in_rest' => true,
			'annotations'  => array(
				'readonly'    => false,
				'destructive' => false,
				// Ein reiner Set-Vorgang auf eine einzelne assoziative
				// Array-Position: wiederholte Aufrufe mit demselben Input
				// konvergieren immer auf denselben Endzustand.
				'idempotent'  => false,
			),
		),
	);
}

/** @param array{identity:array{template:string,template_version:string,stylesheet:string,stylesheet_version:string},option_name:string,row:array{raw:string,autoload:string}|null,mods:array<string,mixed>,locations:array<string,mixed>,hash:string}|null $state */
function wpagent_menu_location_conflict( ?array $state = null ): WP_Error {
	$data = array( 'status' => 409 );
	if ( is_array( $state ) && isset( $state['hash'] ) ) {
		$data['current_state_hash'] = $state['hash'];
	}
	return new WP_Error( 'wpagent_menu_location_conflict', __( 'Die Menüpositionen oder das aktive Theme wurden seit dem gelesenen Zustand verändert.', 'wp-agent-companion' ), $data );
}

/** @param array{identity:array{template:string,template_version:string,stylesheet:string,stylesheet_version:string},option_name:string,row:array{raw:string,autoload:string}|null,mods:array<string,mixed>,locations:array<string,mixed>,hash:string}|null $state */
function wpagent_menu_location_recovery_required( ?array $state = null ): WP_Error {
	$data = array( 'status' => 409, 'recovery_required' => true );
	if ( is_array( $state ) && isset( $state['hash'] ) ) {
		$data['current_state_hash'] = $state['hash'];
	}
	return new WP_Error( 'wpagent_menu_location_recovery_required', __( 'Die Menüpositionsänderung könnte teilweise gespeichert worden sein. Prüfe den aktuellen Theme-Zustand manuell, bevor du erneut schreibst.', 'wp-agent-companion' ), $data );
}

/**
 * @param mixed $input
 * @return array|WP_Error
 */
function wpagent_exec_assign_menu_location( $input ) {
	$input    = is_array( $input ) ? $input : array();
	$menu_id  = absint( $input['menu_id'] ?? 0 );
	$location = trim( (string) ( $input['location'] ?? '' ) );
	$expected = (string) ( $input['expected_state_hash'] ?? '' );

	if ( '' === $location || strlen( $location ) > 64 || 1 !== preg_match( '/^[A-Za-z0-9_-]+$/D', $location ) ) {
		return new WP_Error( 'wpagent_missing_location', __( 'location fehlt.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	if ( $menu_id <= 0 || 1 !== preg_match( '/^[a-f0-9]{64}$/D', $expected ) ) {
		return new WP_Error( 'wpagent_menu_location_invalid_input', __( 'menu_id und expected_state_hash müssen aus einem aktuellen Lesevorgang stammen.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}

	$initial = wpagent_core_mutator_menu_location_state();
	if ( is_wp_error( $initial ) ) {
		return $initial;
	}
	if ( ! hash_equals( $initial['hash'], $expected ) ) {
		return wpagent_menu_location_conflict( $initial );
	}
	$lock = wpagent_core_mutator_acquire_lock( 'menu-locations', $initial['option_name'] );
	if ( is_wp_error( $lock ) ) {
		return $lock;
	}
	try {
		if ( ! current_user_can( 'edit_theme_options' ) ) {
			return new WP_Error( 'wpagent_forbidden', __( 'Keine Berechtigung.', 'wp-agent-companion' ), array( 'status' => 403 ) );
		}
		$current = wpagent_core_mutator_menu_location_state();
		if ( is_wp_error( $current ) ) {
			return $current;
		}
		if ( ! hash_equals( $current['hash'], $expected ) ) {
			return wpagent_menu_location_conflict( $current );
		}
		$registered = get_registered_nav_menus();
		if ( ! array_key_exists( $location, $registered ) ) {
			return new WP_Error( 'wpagent_bad_location', __( 'Die angeforderte Menü-Position ist im aktiven Theme nicht registriert.', 'wp-agent-companion' ), array( 'status' => 400 ) );
		}
		if ( ! wp_get_nav_menu_object( $menu_id ) ) {
			return new WP_Error( 'wpagent_no_menu', __( 'Menü nicht gefunden.', 'wp-agent-companion' ), array( 'status' => 404 ) );
		}
		$previous = isset( $current['locations'][ $location ] ) ? absint( $current['locations'][ $location ] ) : 0;
		if ( $previous === $menu_id ) {
			if ( ! wpagent_core_mutator_refresh_lock( 'menu-locations', $current['option_name'], $lock ) ) {
				return new WP_Error( 'wpagent_core_mutator_locked', __( 'Die Menüpositionssperre wurde verloren.', 'wp-agent-companion' ), array( 'status' => 409 ) );
			}
			return array( 'location' => $location, 'menu_id' => $menu_id, 'previous_menu_id' => $previous, 'state_hash' => $current['hash'], 'verified' => true );
		}

		$candidate_mods                       = $current['mods'];
		$candidate_locations                  = $current['locations'];
		$candidate_locations[ $location ]     = $menu_id;
		$candidate_mods['nav_menu_locations'] = $candidate_locations;
		if ( ! wpagent_core_mutator_refresh_lock( 'menu-locations', $current['option_name'], $lock ) || ! current_user_can( 'edit_theme_options' ) ) {
			return new WP_Error( 'wpagent_core_mutator_locked', __( 'Die Menüpositionssperre oder Berechtigung wurde vor dem Schreiben verloren.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		}
		$candidate_row = wpagent_core_mutator_replace_theme_mods_if_current( $current, $candidate_mods );
		if ( false === $candidate_row ) {
			$latest = wpagent_core_mutator_menu_location_state();
			return is_wp_error( $latest ) ? $latest : wpagent_menu_location_conflict( $latest );
		}
		if ( ! wpagent_core_mutator_refresh_lock( 'menu-locations', $current['option_name'], $lock ) || ! current_user_can( 'edit_theme_options' ) ) {
			return wpagent_menu_location_recovery_required();
		}
		$fresh = wpagent_core_mutator_menu_location_state();
		if ( is_wp_error( $fresh ) ) {
			return wpagent_menu_location_recovery_required();
		}
		$verified = $fresh['identity'] === $current['identity']
			&& is_array( $fresh['row'] )
			&& hash_equals( $candidate_row['raw'], $fresh['row']['raw'] )
			&& hash_equals( $candidate_row['autoload'], $fresh['row']['autoload'] )
			&& $menu_id === absint( $fresh['locations'][ $location ] ?? 0 );
		if ( ! $verified || ! wpagent_core_mutator_refresh_lock( 'menu-locations', $current['option_name'], $lock ) || ! current_user_can( 'edit_theme_options' ) ) {
			return wpagent_menu_location_recovery_required( $fresh );
		}
		return array( 'location' => $location, 'menu_id' => $menu_id, 'previous_menu_id' => $previous, 'state_hash' => $fresh['hash'], 'verified' => true );
	} finally {
		wpagent_core_mutator_release_lock( 'menu-locations', $initial['option_name'], $lock );
	}
}
