<?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 neuen Eintrag hinzu: benutzerdefinierten Link (object=custom mit url) oder Verweis auf Seite/Beitrag/Kategorie (object=page/post/category mit object_id). Optional als Untereintrag über parent (ID eines bestehenden Eintrags im selben Menü). Verifiziert per Re-Read über wp_get_nav_menu_items.', 'wp-agent-companion' ),
		'category'            => 'wp-agent',
		'input_schema'        => array(
			'type'       => 'object',
			'properties' => array(
				'menu_id'   => array( 'type' => 'integer' ),
				'title'     => array(
					'type'        => 'string',
					'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',
					'description' => __( 'Pflicht, wenn object page, post oder category ist: die ID der Seite/des Beitrags/der Kategorie.', 'wp-agent-companion' ),
				),
				'url'       => array(
					'type'        => 'string',
					'description' => __( 'Pflicht, wenn object=custom ist: die Ziel-URL des Links.', 'wp-agent-companion' ),
				),
				'parent'    => array(
					'type'        => 'integer',
					'description' => __( 'Optional: ID eines bestehenden Menüeintrags im selben Menü, unter dem dieser Eintrag als Untereintrag erscheint.', 'wp-agent-companion' ),
				),
			),
			'required'   => array( 'menu_id', 'title', 'object' ),
		),
		'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' ),
			),
		),
		'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,
			),
		),
	);
}

/**
 * @param mixed $input
 * @return array|WP_Error
 */
function wpagent_exec_add_menu_item( $input ) {
	$input   = is_array( $input ) ? $input : array();
	$menu_id = (int) ( $input['menu_id'] ?? 0 );
	$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 = trim( (string) ( $input['title'] ?? '' ) );
	if ( '' === $title ) {
		return new WP_Error( 'wpagent_missing_title', __( 'title fehlt.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}

	$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 = trim( (string) ( $input['url'] ?? '' ) );
		if ( '' === $url ) {
			return new WP_Error( 'wpagent_missing_url', __( 'url fehlt für object=custom.', '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 {
		$object_id = (int) ( $input['object_id'] ?? 0 );
		if ( $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 )
			);
		}

		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'] && 0 !== (int) $input['parent'] ) {
		$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;
	}

	$item_id = wp_update_nav_menu_item( $menu_id, 0, $args );
	if ( is_wp_error( $item_id ) ) {
		// Sollte dank der Validierungen oben nicht mehr erreichbar sein; als
		// Sicherheitsnetz trotzdem sauber als 500 melden statt eine
		// status-lose WP_Error unbehandelt durchzureichen.
		return new WP_Error( 'wpagent_add_menu_item_failed', $item_id->get_error_message(), array( 'status' => 500 ) );
	}
	$item_id = (int) $item_id;

	$found = null;
	$items = wp_get_nav_menu_items( $menu_id );
	if ( is_array( $items ) ) {
		foreach ( $items as $candidate ) {
			if ( (int) $candidate->ID === $item_id ) {
				$found = $candidate;
				break;
			}
		}
	}
	$verified = ( null !== $found && $title === $found->title );

	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'  => $verified,
	);
}
