<?php
/**
 * Ability: wp-agent/add-menu-item
 *
 * Fügt einem bestehenden Menü einen neuen Eintrag hinzu: entweder einen
 * benutzerdefinierten Link (object=custom, benötigt url) oder einen Verweis auf
 * ein bestehendes Objekt (object=page/post/category, benötigt object_id).
 * Ruft dafür wp_update_nav_menu_item( $menu_id, 0, $args ) auf; die 0 als
 * zweites Argument ist bei diesem Kern-Aufruf kein Platzhalter, sondern legt
 * bei jedem Aufruf einen NEUEN Eintrag an (es gibt keinen item_id-Parameter zum
 * Aktualisieren eines bestehenden Eintrags, siehe Datei-Kommentar unten zur
 * Idempotenz-Einstufung).
 *
 * 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_add_menu_item(): array {
	return array(
		'label'               => __( 'Menüeintrag hinzufügen', 'wp-agent-companion' ),
		'description'         => __( 'Fügt einem bestehenden Menü einen Eintrag hinzu. Die Anfrage bindet den vollständigen aktuellen Menü-Zustand, prüft die Sperre nach dem nativen Schreibvorgang und kompensiert nur einen unveränderten eigenen Eintragskandidaten.', 'wp-agent-companion' ),
		'category'            => 'wp-agent',
		'input_schema'        => array(
			'type'       => 'object',
			'properties' => array(
				'menu_id'   => array( 'type' => 'integer', 'minimum' => 1 ),
				'title'     => array(
					'type'        => 'string',
					'minLength'   => 1,
					'maxLength'   => 200,
					'description' => __( 'Sichtbarer Text des Menüeintrags.', 'wp-agent-companion' ),
				),
				'object'    => array(
					'type'        => 'string',
					'enum'        => array( 'custom', 'page', 'post', 'category' ),
					'description' => __( 'custom = benutzerdefinierter Link (benötigt url). page/post/category = Verweis auf ein bestehendes Objekt (benötigt object_id).', 'wp-agent-companion' ),
				),
				'object_id' => array(
					'type'        => 'integer',
					'minimum'     => 1,
					'description' => __( 'Pflicht, wenn object page, post oder category ist: die ID der Seite/des Beitrags/der Kategorie.', 'wp-agent-companion' ),
				),
				'url'       => array(
					'type'        => 'string',
					'minLength'   => 1,
					'maxLength'   => 2048,
					'description' => __( 'Pflicht, wenn object=custom ist: die Ziel-URL des Links.', 'wp-agent-companion' ),
				),
				'parent'    => array(
					'type'        => 'integer',
					'minimum'     => 1,
					'description' => __( 'Optional: ID eines bestehenden Menüeintrags im selben Menü, unter dem dieser Eintrag als Untereintrag erscheint.', 'wp-agent-companion' ),
				),
				'expected_hash' => array( 'type' => 'string', 'pattern' => '^[a-f0-9]{64}$' ),
			),
			'required'              => array( 'menu_id', 'title', 'object', 'expected_hash' ),
			'additionalProperties'  => false,
		),
		'output_schema'       => array(
			'type'       => 'object',
			'properties' => array(
				'item_id'   => array( 'type' => 'integer' ),
				'menu_id'   => array( 'type' => 'integer' ),
				'title'     => array( 'type' => 'string' ),
				'type'      => array( 'type' => 'string' ),
				'object'    => array( 'type' => 'string' ),
				'object_id' => array( 'type' => 'integer' ),
				'url'       => array( 'type' => 'string' ),
				'parent'    => array( 'type' => 'integer' ),
				'verified'  => array( 'type' => 'boolean' ),
			),
			'required'              => array( 'item_id', 'menu_id', 'title', 'type', 'object', 'object_id', 'url', 'parent', 'verified' ),
			'additionalProperties'  => false,
		),
		'execute_callback'    => 'wpagent_exec_add_menu_item',
		'permission_callback' => 'wpagent_perm_edit_theme_options',
		'meta'                => array(
			'show_in_rest' => true,
			'annotations'  => array(
				'readonly'    => false,
				'destructive' => false,
				// wp_update_nav_menu_item( $menu_id, 0, ... ) legt bei JEDEM
				// Aufruf einen neuen Eintrag an; es gibt keinen Abgleichs-Schlüssel
				// (kein item_id zum Update), über den ein Retry mit identischem
				// Input erkennen könnte, dass der Eintrag schon existiert. Ein
				// zweiter Aufruf mit denselben Werten erzeugt also einen echten,
				// zusätzlichen Eintrag, nicht nur denselben Zustand erneut.
				'idempotent'  => false,
			),
		),
	);
}

/** @return WP_Error */
function wpagent_menu_item_recovery_required( int $item_id = 0, string $current_hash = '' ): WP_Error {
	$data = array( 'status' => 409, 'recovery_required' => true, 'rollback_verified' => false );
	if ( $item_id > 0 ) {
		$data['item_id'] = $item_id;
	}
	if ( '' !== $current_hash ) {
		$data['current_hash'] = $current_hash;
	}
	return new WP_Error( 'wpagent_menu_item_recovery_required', __( 'Der Menüeintrag konnte nicht vollständig eigentumsgeprüft verifiziert oder entfernt werden. Wiederhole den Vorgang nicht automatisch und prüfe das Menü manuell.', 'wp-agent-companion' ), $data );
}

/** @return WP_Error */
function wpagent_menu_item_unverified(): WP_Error {
	return new WP_Error( 'wpagent_menu_item_unverified', __( 'Der Menüeintrag wich nach dem Speichern vom erwarteten Zustand ab und wurde wieder entfernt.', 'wp-agent-companion' ), array( 'status' => 500, 'rollback_verified' => true, 'recovery_required' => false ) );
}

/**
 * @param mixed $input
 * @return array|WP_Error
 */
function wpagent_exec_add_menu_item( $input ) {
	$input   = is_array( $input ) ? $input : array();
	if ( ! wpagent_core_mutator_is_strict_integer( $input['menu_id'] ?? null ) || (int) $input['menu_id'] < 1 ) {
		return new WP_Error( 'wpagent_no_menu', __( 'Menü nicht gefunden.', 'wp-agent-companion' ), array( 'status' => 404 ) );
	}
	$menu_id = (int) $input['menu_id'];
	$menu    = wp_get_nav_menu_object( $menu_id );

	if ( ! $menu ) {
		return new WP_Error( 'wpagent_no_menu', __( 'Menü nicht gefunden.', 'wp-agent-companion' ), array( 'status' => 404 ) );
	}

	$title = wpagent_core_mutator_bounded_text( $input['title'] ?? null, 200 );
	if ( is_wp_error( $title ) || '' === trim( (string) $title ) ) {
		return new WP_Error( 'wpagent_missing_title', __( 'title fehlt.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	$title = trim( $title );
	$expected_hash = wpagent_core_mutator_expected_hash( $input );
	if ( is_wp_error( $expected_hash ) ) {
		return $expected_hash;
	}
	$initial = wpagent_core_mutator_menu_state( $menu_id );
	if ( ! is_array( $initial ) ) {
		return new WP_Error( 'wpagent_no_menu', __( 'Menü nicht gefunden.', 'wp-agent-companion' ), array( 'status' => 404 ) );
	}
	if ( ! $initial['bounded'] ) {
		return new WP_Error( 'wpagent_menu_item_limit', __( 'Menüs mit mehr als 500 Einträgen werden nicht automatisiert verändert.', 'wp-agent-companion' ), array( 'status' => 413, 'current_hash' => $initial['hash'] ) );
	}
	if ( ! hash_equals( $initial['hash'], $expected_hash ) ) {
		return new WP_Error( 'wpagent_menu_item_conflict', __( 'Das Menü wurde seit dem Lesen verändert.', 'wp-agent-companion' ), array( 'status' => 409, 'current_hash' => $initial['hash'] ) );
	}
	$lock = wpagent_core_mutator_acquire_lock( 'menu', (string) $menu_id );
	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 ) );
		}
		$locked_before = wpagent_core_mutator_menu_state( $menu_id );
		if ( ! is_array( $locked_before ) ) {
			return new WP_Error( 'wpagent_no_menu', __( 'Menü nicht gefunden.', 'wp-agent-companion' ), array( 'status' => 404 ) );
		}
		if ( ! $locked_before['bounded'] || ! hash_equals( $locked_before['hash'], $expected_hash ) ) {
			return new WP_Error( 'wpagent_menu_item_conflict', __( 'Das Menü wurde während der Sperrübernahme verändert.', 'wp-agent-companion' ), array( 'status' => 409, 'current_hash' => $locked_before['hash'] ) );
		}

	$object           = (string) ( $input['object'] ?? '' );
	$allowed_objects  = array( 'custom', 'page', 'post', 'category' );
	if ( ! in_array( $object, $allowed_objects, true ) ) {
		return new WP_Error(
			'wpagent_bad_object',
			/* translators: %s: submitted menu object type. */
			sprintf( __( 'Ungültiger Wert für object: "%s" (erlaubt: custom, page, post, category).', 'wp-agent-companion' ), $object ),
			array( 'status' => 400 )
		);
	}

	$args = array(
		'menu-item-title'  => $title,
		// Ohne dieses Flag legt der WordPress-Kern neue Einträge als draft an;
		// wp_get_nav_menu_items() liest per Default aber nur post_status=publish,
		// die eigene Re-Read-Verifikation unten würde den frisch angelegten
		// Eintrag sonst nie finden.
		'menu-item-status' => 'publish',
	);

	if ( 'custom' === $object ) {
		$url = wpagent_core_mutator_bounded_text( $input['url'] ?? null, 2048 );
		if ( is_wp_error( $url ) || '' === trim( (string) $url ) ) {
			return new WP_Error( 'wpagent_missing_url', __( 'url fehlt für object=custom.', 'wp-agent-companion' ), array( 'status' => 400 ) );
		}
		$url = esc_url_raw( trim( $url ), array( 'http', 'https', 'mailto', 'tel' ) );
		if ( '' === $url ) {
			return new WP_Error( 'wpagent_bad_url', __( 'url muss eine sichere http(s)-, mailto- oder tel-URL sein.', 'wp-agent-companion' ), array( 'status' => 400 ) );
		}
		$args['menu-item-type']   = 'custom';
		$args['menu-item-url']    = esc_url_raw( $url );
		$args['menu-item-object'] = 'custom';
	} else {
		if ( ! wpagent_core_mutator_is_strict_integer( $input['object_id'] ?? null ) || (int) $input['object_id'] <= 0 ) {
			return new WP_Error(
				'wpagent_missing_object_id',
				/* translators: %s: submitted menu object type. */
				sprintf( __( 'object_id fehlt für object=%s.', 'wp-agent-companion' ), $object ),
				array( 'status' => 400 )
			);
		}
		$object_id = (int) $input['object_id'];

		if ( 'category' === $object ) {
			$term = get_term( $object_id, 'category' );
			if ( ! $term || is_wp_error( $term ) ) {
				return new WP_Error( 'wpagent_bad_object_id', __( 'Kategorie nicht gefunden.', 'wp-agent-companion' ), array( 'status' => 400 ) );
			}
			$args['menu-item-type']   = 'taxonomy';
			$args['menu-item-object'] = 'category';
		} else {
			// object ist an dieser Stelle 'page' oder 'post'.
			$target = get_post( $object_id );
			if ( ! $target || $object !== $target->post_type ) {
				return new WP_Error(
					'wpagent_bad_object_id',
					/* translators: 1: menu object type, 2: object ID. */
					sprintf( __( '%1$s mit ID %2$d nicht gefunden.', 'wp-agent-companion' ), $object, $object_id ),
					array( 'status' => 400 )
				);
			}
			$args['menu-item-type']   = 'post_type';
			$args['menu-item-object'] = $object;
		}

		$args['menu-item-object-id'] = $object_id;
	}

	if ( array_key_exists( 'parent', $input ) && null !== $input['parent'] ) {
		if ( ! wpagent_core_mutator_is_strict_integer( $input['parent'] ) || (int) $input['parent'] <= 0 ) {
			return new WP_Error( 'wpagent_bad_parent', __( 'parent muss eine positive Menüeintrags-ID sein.', 'wp-agent-companion' ), array( 'status' => 400 ) );
		}
		$parent = (int) $input['parent'];
		// Sowohl prüfen, dass der Eltern-Eintrag überhaupt ein Menüeintrag ist,
		// als auch, dass er im SELBEN Menü liegt: wp_update_nav_menu_item()
		// selbst prüft weder das eine noch das andere und würde sonst klaglos
		// einen über Menü-Grenzen hinweg verschachtelten (und in der Menü-UI
		// nie erreichbaren) Eintrag anlegen.
		if ( ! is_nav_menu_item( $parent ) || ! is_object_in_term( $parent, 'nav_menu', (int) $menu->term_id ) ) {
			return new WP_Error( 'wpagent_bad_parent', __( 'parent ist kein Menüeintrag in diesem Menü.', 'wp-agent-companion' ), array( 'status' => 400 ) );
		}
		$args['menu-item-parent-id'] = $parent;
	}

		if ( ! wpagent_core_mutator_refresh_lock( 'menu', (string) $menu_id, $lock ) || ! wpagent_core_mutator_lock_is_owned( 'menu', (string) $menu_id, $lock ) || ! current_user_can( 'edit_theme_options' ) ) {
			return new WP_Error( 'wpagent_menu_item_locked', __( 'Die Menü-Sperre oder Berechtigung ging vor dem Schreiben verloren.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		}
		$item_id = wp_update_nav_menu_item( $menu_id, 0, $args );
	if ( is_wp_error( $item_id ) ) {
		return wpagent_menu_item_recovery_required();
	}
	$item_id = (int) $item_id;
	$created_candidate = $item_id > 0 ? wpagent_core_mutator_created_post_candidate( $item_id, 'nav_menu_item', get_current_user_id() ) : null;

	$found = null;
	$items = wp_get_nav_menu_items( $menu_id );
	if ( is_array( $items ) ) {
		foreach ( $items as $menu_item ) {
			if ( (int) $menu_item->ID === $item_id ) {
				$found = $menu_item;
				break;
			}
		}
	}
		$after = wpagent_core_mutator_menu_state( $menu_id );
		$verified = is_array( $created_candidate ) && null !== $found && $title === $found->title
			&& $args['menu-item-type'] === $found->type
			&& $args['menu-item-object'] === $found->object
			&& (int) ( $args['menu-item-parent-id'] ?? 0 ) === (int) $found->menu_item_parent
			&& ( 'custom' !== $object || hash_equals( (string) $args['menu-item-url'], (string) $found->url ) )
			&& ( 'custom' === $object || (int) $args['menu-item-object-id'] === (int) $found->object_id )
			&& is_array( $after ) && $after['bounded']
			&& wpagent_core_mutator_refresh_lock( 'menu', (string) $menu_id, $lock )
			&& wpagent_core_mutator_lock_is_owned( 'menu', (string) $menu_id, $lock )
			&& current_user_can( 'edit_theme_options' );
		if ( ! $verified ) {
			if ( wpagent_core_mutator_delete_created_post_if_current( $created_candidate, 'nav_menu_item', get_current_user_id() ) ) {
				return wpagent_menu_item_unverified();
			}
			return wpagent_menu_item_recovery_required( $item_id, is_array( $after ) ? $after['hash'] : '' );
		}

		return array(
		'item_id'   => $item_id,
		'menu_id'   => $menu_id,
		'title'     => $found ? $found->title : $title,
		'type'      => $args['menu-item-type'],
		'object'    => $args['menu-item-object'],
		// Bei object=custom setzt der Kern menu-item-object-id automatisch auf
		// die eigene Eintrags-ID (siehe wp_update_nav_menu_item()); für die
		// anderen Zweige ist es die übergebene object_id.
		'object_id' => isset( $args['menu-item-object-id'] ) ? (int) $args['menu-item-object-id'] : $item_id,
		'url'       => $found ? (string) $found->url : (string) ( $args['menu-item-url'] ?? '' ),
		'parent'    => isset( $args['menu-item-parent-id'] ) ? (int) $args['menu-item-parent-id'] : 0,
		'verified'  => true,
	);
	} finally {
		wpagent_core_mutator_release_lock( 'menu', (string) $menu_id, $lock );
	}
}
