<?php
/**
 * Ability: wp-agent/create-term
 *
 * Legt einen neuen Term in einer Taxonomie an (z. B. eine Kategorie oder ein
 * Schlagwort). parent ist nur bei hierarchischen Taxonomien erlaubt
 * (is_taxonomy_hierarchical()); bei nicht-hierarchischen Taxonomien wie
 * post_tag würde WordPress einen gesetzten parent-Wert sonst einfach
 * ignorieren, diese Ability lehnt ihn stattdessen explizit mit einer klaren
 * Fehlermeldung ab, statt den Aufrufer über einen still verworfenen Wert im
 * Unklaren zu lassen.
 *
 * Nach dem Anlegen liest die Ability den Term über get_term() zurück und
 * meldet pro übergebenem Feld, ob es tatsächlich persistiert wurde (analog
 * zum set-seo-meta-Muster).
 *
 * @package WPAgentCompanion
 */

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

function wpagent_ability_create_term(): array {
	return array(
		'label'               => __( 'Term anlegen', 'wp-agent-companion' ),
		'description'         => __( 'Legt einen neuen Term (z. B. Kategorie oder Schlagwort) in einer Taxonomie an. parent ist nur bei hierarchischen Taxonomien erlaubt. Verifiziert per Re-Read über get_term().', 'wp-agent-companion' ),
		'category'            => 'wp-agent',
		'input_schema'        => array(
			'type'                 => 'object',
			'properties'           => array(
				'taxonomy'    => array(
					'type'        => 'string', 'maxLength' => 32, 'pattern' => '^[a-z0-9_-]{1,32}$',
					'description' => __( 'Muss eine registrierte Taxonomie sein (z. B. category, post_tag).', 'wp-agent-companion' ),
				),
				'name'        => array( 'type' => 'string', 'minLength' => 1, 'maxLength' => 200 ),
				'slug'        => array( 'type' => 'string', 'minLength' => 1, 'maxLength' => 200 ),
				'parent'      => array(
					'type'        => 'integer', 'minimum' => 0, 'maximum' => 2147483647,
					'description' => __( 'Nur bei hierarchischen Taxonomien erlaubt (z. B. category).', 'wp-agent-companion' ),
				),
				'description' => array( 'type' => 'string', 'maxLength' => 10000 ),
			),
			'required'   => array( 'taxonomy', 'name' ),
			'additionalProperties' => false,
		),
		'output_schema'       => array(
			'type'                 => 'object',
			'properties'           => array(
				'term_id'  => array( 'type' => 'integer', 'minimum' => 1, 'maximum' => 2147483647 ),
				'taxonomy' => array( 'type' => 'string', 'maxLength' => 32 ),
				'name'     => array( 'type' => 'string', 'maxLength' => 200 ),
				'slug'     => array( 'type' => 'string', 'maxLength' => 200 ),
				'parent'   => array( 'type' => 'integer', 'minimum' => 0, 'maximum' => 2147483647 ),
				'written'  => array(
					'type'                 => 'object',
					'properties'           => array(
						'name'        => array( 'type' => 'boolean' ),
						'slug'        => array( 'type' => 'boolean' ),
						'parent'      => array( 'type' => 'boolean' ),
						'description' => array( 'type' => 'boolean' ),
					),
					'additionalProperties' => false,
				),
				'verified' => array( 'type' => 'boolean' ),
			),
			'required'             => array( 'term_id', 'taxonomy', 'name', 'slug', 'parent', 'written', 'verified' ),
			'additionalProperties' => false,
		),
		'execute_callback'    => 'wpagent_exec_create_term',
		'permission_callback' => 'wpagent_perm_edit_posts',
		'meta'                => array(
			'show_in_rest' => true,
			'annotations'  => array(
				'readonly'    => false,
				'destructive' => false,
				// wp_insert_term() lehnt einen doppelten Namen innerhalb derselben
				// Taxonomie mit einem WP_Error ab (term_exists), statt bei einem
				// Re-Run einen zweiten Term anzulegen: der Zustand konvergiert.
				'idempotent'  => false,
			),
		),
	);
}

/**
 * @param mixed $input
 * @return array|WP_Error
 */
function wpagent_exec_create_term( $input ) {
	$input    = is_array( $input ) ? $input : array();
	$taxonomy = wpagent_core_mutator_bounded_text( $input['taxonomy'] ?? null, 32 );
	$name     = wpagent_core_mutator_bounded_text( $input['name'] ?? null, 200 );

	if ( is_wp_error( $taxonomy ) || 1 !== preg_match( '/^[a-z0-9_-]{1,32}$/D', $taxonomy ) || ! taxonomy_exists( $taxonomy ) ) {
		return new WP_Error( 'wpagent_bad_taxonomy', __( 'Unbekannte oder ungültige Taxonomie.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	if ( 'product_shipping_class' === $taxonomy ) {
		return new WP_Error(
			'wpagent_wc_shipping_class_requires_specialized_create',
			__( 'WooCommerce-Versandklassen müssen über die spezielle Versandklassen-Ability angelegt werden. Sie stellt eindeutige Slugs und Read-back-Verifikation sicher und verweist unklare Zustände ohne weitere Mutation zur manuellen Prüfung.', 'wp-agent-companion' ),
			array( 'status' => 409 )
		);
	}
	if ( is_wp_error( $name ) ) {
		return new WP_Error( 'wpagent_missing_name', __( 'name fehlt.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}

	// Feingranulare Capability der konkreten Taxonomie (get_taxonomy()->cap)
	// statt der pauschalen edit_posts-Schranke der permission_callback oben.
	// Fail-closed analog zu wpagent_post_type_cap() in helpers.php: fehlt die
	// Taxonomie oder ist die Capability leer, wird do_not_allow verwendet statt
	// eine Capability zu erraten.
	$tax_object = get_taxonomy( $taxonomy );
	$manage_cap = ( $tax_object && isset( $tax_object->cap->manage_terms ) && '' !== (string) $tax_object->cap->manage_terms )
		? (string) $tax_object->cap->manage_terms
		: 'do_not_allow';
	if ( ! current_user_can( $manage_cap ) ) {
		return new WP_Error( 'wpagent_forbidden', __( 'Keine Berechtigung, Terme in dieser Taxonomie anzulegen.', 'wp-agent-companion' ), array( 'status' => 403 ) );
	}

	$hierarchical = is_taxonomy_hierarchical( $taxonomy );
	$args         = array();
	$expect       = array( 'name' => $name );

	if ( array_key_exists( 'slug', $input ) ) {
		$slug = wpagent_core_mutator_bounded_text( $input['slug'], 200 );
		if ( is_wp_error( $slug ) || sanitize_title( $slug ) !== $slug ) {
			return new WP_Error( 'wpagent_bad_term_slug', __( 'slug muss ein kanonischer WordPress-Slug sein.', 'wp-agent-companion' ), array( 'status' => 400 ) );
		}
		$args['slug']   = $slug;
		$expect['slug'] = $slug;
	}

	if ( array_key_exists( 'parent', $input ) ) {
		$parent = wpagent_core_schema_input_integer( $input['parent'], 0, 2147483647, 'parent' );
		if ( is_wp_error( $parent ) ) {
			return $parent;
		}
		if ( ! $hierarchical && $parent > 0 ) {
			return new WP_Error(
				'wpagent_not_hierarchical',
				/* translators: %s: taxonomy name. */
				sprintf( __( 'Taxonomie "%s" ist nicht hierarchisch; parent ist nicht erlaubt.', 'wp-agent-companion' ), $taxonomy ),
				array( 'status' => 400 )
			);
		}
		if ( $parent > 0 && ! get_term( $parent, $taxonomy ) ) {
			return new WP_Error( 'wpagent_bad_parent', __( 'Eltern-Term nicht gefunden.', 'wp-agent-companion' ), array( 'status' => 400 ) );
		}
		$args['parent']   = $parent;
		$expect['parent'] = $parent;
	}

	if ( array_key_exists( 'description', $input ) ) {
		$description = wpagent_core_mutator_bounded_text( $input['description'], 10000, true );
		if ( is_wp_error( $description ) ) {
			return $description;
		}
		$args['description']   = $description;
		$expect['description'] = $args['description'];
	}

	$res = wp_insert_term( $name, $taxonomy, $args );
	if ( is_wp_error( $res ) ) {
		return $res;
	}
	$term_id = (int) $res['term_id'];

	$fresh = get_term( $term_id, $taxonomy );
	if ( ! $fresh || is_wp_error( $fresh ) ) {
		return wpagent_term_create_recovery_required( $term_id, __( 'Term wurde angelegt, konnte aber nicht zurückgelesen werden. Prüfe ihn manuell.', 'wp-agent-companion' ) );
	}

	$written  = array();
	foreach ( $expect as $field => $value ) {
		$actual = match ( $field ) {
			'name'        => $fresh->name,
			'slug'        => $fresh->slug,
			'parent'      => (int) $fresh->parent,
			'description' => $fresh->description,
			default       => null,
		};
		$ok                = ( (string) $actual === (string) $value );
		$written[ $field ] = $ok;
		if ( ! $ok ) {
			return wpagent_term_create_recovery_required( $term_id, __( 'Der neu angelegte Term weicht beim Rücklesen vom angeforderten Zustand ab. Er bleibt zur manuellen Prüfung erhalten.', 'wp-agent-companion' ) );
		}
	}
	$output = array(
		'taxonomy' => wpagent_core_schema_output_text( $fresh->taxonomy, 32, 'taxonomy' ),
		'name'     => wpagent_core_schema_output_text( $fresh->name, 200, 'name' ),
		'slug'     => wpagent_core_schema_output_text( $fresh->slug, 200, 'slug' ),
	);
	foreach ( $output as $value ) {
		if ( is_wp_error( $value ) ) {
			return wpagent_term_create_recovery_required( $term_id, __( 'Der neu angelegte Term überschreitet eine veröffentlichte Ausgabegrenze. Er bleibt zur manuellen Prüfung erhalten.', 'wp-agent-companion' ) );
		}
	}

	return array(
		'term_id'  => $term_id,
		'taxonomy' => $output['taxonomy'],
		'name'     => $output['name'],
		'slug'     => $output['slug'],
		'parent'   => (int) $fresh->parent,
		'written'  => $written,
		'verified' => true,
	);
}

/** @return WP_Error */
function wpagent_term_create_recovery_required( int $term_id, string $message ): WP_Error {
	return new WP_Error(
		'wpagent_term_create_recovery_required',
		$message,
		array( 'status' => 500, 'term_id' => $term_id, 'recovery_required' => true, 'rollback_verified' => false )
	);
}
