<?php
/**
 * Ability: wp-agent/create-user
 *
 * Legt einen neuen Nutzer über die offizielle wp_insert_user()-API an
 * (Username-, E-Mail- und Rollen-Validierung inklusive Eindeutigkeitsprüfung
 * übernimmt der WordPress-Kern selbst; ein WP_Error von wp_insert_user()
 * wird unverändert durchgereicht, analog zum wp_update_post()-Muster in
 * update-post.php). Ist das Passwort-Feld leer, wird serverseitig ein
 * starkes Passwort erzeugt und EINMALIG im Ergebnis zurückgegeben
 * (generated_password); bei nachfolgenden Leseoperationen (wp-agent/get-user)
 * taucht es nie wieder auf.
 *
 * Dasselbe administrator-Gate wie bei wp-agent/set-user-role: Rollen mit der
 * Capability manage_options dürfen nur mit dem expliziten Input-Flag
 * i_grant_admin=true angelegt werden, sonst 403. Die Erkennungslogik ist
 * bewusst hier lokal dupliziert statt wpagent_role_grants_admin_capability()
 * aus set-user-role.php wiederzuverwenden: beide Dateien sollen unabhängig
 * voneinander ladbar bleiben und keine Reihenfolge-Abhängigkeit zwischen
 * neuen Ability-Dateien einführen. Die Integrationsphase sollte diese
 * kleine Dopplung nach helpers.php heben, sobald die Datei dort angefasst
 * werden darf.
 *
 * @package WPAgentCompanion
 */

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

function wpagent_ability_create_user(): array {
	return array(
		'label'               => __( 'Nutzer anlegen', 'wp-agent-companion' ),
		'description'         => __( 'Legt einen neuen WordPress-Nutzer mit Username, E-Mail und Rolle an. Ohne Passwort wird eines serverseitig generiert und einmalig im Ergebnis zurückgegeben. Der Vorgang prüft Sperrbesitz und den vollständigen frisch angelegten Kandidaten, bevor er Erfolg meldet.', 'wp-agent-companion' ),
		'category'            => 'wp-agent',
		'input_schema'        => array(
			'type'       => 'object',
			'properties' => array(
				'username'      => array( 'type' => 'string', 'minLength' => 1, 'maxLength' => 60 ),
				'email'         => array( 'type' => 'string', 'format' => 'email', 'maxLength' => 100 ),
				'role'          => array(
					'type'        => 'string',
					'minLength'   => 1,
					'maxLength'   => 64,
					'description' => __( 'Muss eine registrierte Rolle sein (wp_roles()).', 'wp-agent-companion' ),
				),
				'password'      => array(
					'type'        => 'string',
					'minLength'   => 12,
					'maxLength'   => 256,
					'description' => __( 'Optional. Fehlt es, wird serverseitig ein starkes Passwort generiert und einmalig zurückgegeben.', 'wp-agent-companion' ),
				),
				'i_grant_admin' => array(
					'type'        => 'boolean',
					'default'     => false,
					'description' => __( 'Pflicht (true), wenn die Zielrolle die Capability manage_options besitzt (z. B. administrator).', 'wp-agent-companion' ),
				),
			),
			'required'              => array( 'username', 'email', 'role' ),
			'additionalProperties'  => false,
		),
		'output_schema'       => array(
			'type'       => 'object',
			'properties' => array(
				'user_id'             => array( 'type' => 'integer' ),
				'username'            => array( 'type' => 'string' ),
				'email'               => array( 'type' => 'string' ),
				'role'                => array( 'type' => 'string' ),
				'password_generated'  => array( 'type' => 'boolean' ),
				'generated_password'  => array(
					'type'        => 'string',
					'description' => __( 'Nur vorhanden, wenn password_generated true ist. Wird nie erneut ausgeliefert.', 'wp-agent-companion' ),
				),
				'verified'            => array( 'type' => 'boolean' ),
			),
			'required'              => array( 'user_id', 'username', 'email', 'role', 'password_generated', 'verified' ),
			'additionalProperties'  => false,
		),
		'execute_callback'    => 'wpagent_exec_create_user',
		'permission_callback' => 'wpagent_perm_create_users',
		'meta'                => array(
			'show_in_rest' => true,
			'annotations'  => array(
				'readonly'    => false,
				'destructive' => false,
				// Ein zweiter Aufruf mit demselben username schlägt am
				// WordPress-Kern (existing_user_login) fehl statt denselben
				// Endzustand ruhig erneut zu melden; daher bewusst false statt
				// des sonst in diesem Kern-Plugin üblichen true.
				'idempotent'  => false,
			),
		),
	);
}

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

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

/**
 * Permission callback: der Aufrufer muss create_users besitzen. Diese
 * Capability ist in einer Standardinstallation Administratoren vorbehalten;
 * der als Editor eingerichtete Companion-Bot besitzt sie nie.
 *
 * @param mixed $input Unused ability input.
 * @return bool
 */
function wpagent_perm_create_users( $input = null ): bool {
	return current_user_can( 'create_users' );
}

/**
 * Prüft, ob eine Rolle Administrator-äquivalente Rechte trägt. Siehe
 * Datei-Kommentar oben zur bewussten lokalen Dopplung gegenüber
 * set-user-role.php.
 *
 * @param string $role
 * @return bool
 */
function wpagent_new_user_role_grants_admin_capability( string $role ): bool {
	if ( 'administrator' === $role ) {
		return true;
	}
	$role_object = get_role( $role );
	if ( ! $role_object instanceof WP_Role ) {
		return false;
	}
	return ! empty( $role_object->capabilities['manage_options'] );
}

/**
 * @param mixed $input
 * @return array|WP_Error
 */
function wpagent_exec_create_user( $input ) {
	$input = is_array( $input ) ? $input : array();

	if ( ! current_user_can( 'create_users' ) ) {
		return new WP_Error( 'wpagent_forbidden', __( 'Keine Berechtigung.', 'wp-agent-companion' ), array( 'status' => 403 ) );
	}

	$username = wpagent_core_mutator_bounded_text( $input['username'] ?? null, 60 );
	$email    = wpagent_core_mutator_bounded_text( $input['email'] ?? null, 100 );
	$role     = wpagent_core_mutator_bounded_text( $input['role'] ?? null, 64 );
	if ( is_wp_error( $username ) || '' === trim( (string) $username ) ) {
		return new WP_Error( 'wpagent_bad_username', __( 'username fehlt.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	$username = trim( $username );
	if ( $username !== sanitize_user( $username, true ) ) {
		return new WP_Error( 'wpagent_bad_username', __( 'username enthält ungültige Zeichen.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	if ( is_wp_error( $email ) || ! is_email( trim( (string) $email ) ) ) {
		return new WP_Error( 'wpagent_bad_email', __( 'email fehlt.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	$email = trim( $email );
	if ( is_wp_error( $role ) || '' === trim( (string) $role ) || ! wp_roles()->is_role( $role ) ) {
		return new WP_Error(
			'wpagent_bad_role',
			/* translators: %s: submitted WordPress role. */
			sprintf( __( 'Ungültige Rolle "%s".', 'wp-agent-companion' ), $role ),
			array( 'status' => 400 )
		);
	}

	if ( array_key_exists( 'i_grant_admin', $input ) && ! is_bool( $input['i_grant_admin'] ) ) {
		return new WP_Error( 'wpagent_bad_admin_grant', __( 'i_grant_admin muss ein Boolean sein.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	$i_grant_admin = true === ( $input['i_grant_admin'] ?? false );
	if ( wpagent_new_user_role_grants_admin_capability( $role ) && ! $i_grant_admin ) {
		return new WP_Error(
			'wpagent_admin_grant_required',
			__( 'Diese Rolle besitzt Administrator-Rechte (manage_options). Setze i_grant_admin=true, um das explizit zu bestätigen.', 'wp-agent-companion' ),
			array( 'status' => 403 )
		);
	}

	$password_generated = false;
	$password            = $input['password'] ?? null;
	if ( null !== $password && ( ! is_string( $password ) || is_wp_error( wpagent_core_mutator_bounded_text( $password, 256 ) ) || strlen( $password ) < 12 ) ) {
		return new WP_Error( 'wpagent_bad_password', __( 'password muss 12 bis 256 Zeichen lang sein und darf keine Steuerzeichen enthalten.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	if ( null === $password ) {
		$password            = wp_generate_password( 24, true, true );
		$password_generated = true;
	}
	$lock_identity = hash( 'sha256', "create-user\0" . $username );
	$lock          = wpagent_core_mutator_acquire_lock( 'user-create', $lock_identity );
	if ( is_wp_error( $lock ) ) {
		return $lock;
	}
	try {
		if ( ! current_user_can( 'create_users' ) ) {
			return new WP_Error( 'wpagent_forbidden', __( 'Keine Berechtigung.', 'wp-agent-companion' ), array( 'status' => 403 ) );
		}
		if ( ! wpagent_core_mutator_refresh_lock( 'user-create', $lock_identity, $lock ) || ! wpagent_core_mutator_lock_is_owned( 'user-create', $lock_identity, $lock ) || ! current_user_can( 'create_users' ) ) {
			return new WP_Error( 'wpagent_user_create_locked', __( 'Die Erstellungs-Sperre oder Berechtigung ging vor dem Anlegen verloren.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		}
		$user_id = wp_insert_user(
			array(
				'user_login' => $username,
				'user_email' => $email,
				'user_pass'  => $password,
				'role'       => $role,
			)
		);
		if ( is_wp_error( $user_id ) ) {
			return $user_id;
		}
		$user_id   = (int) $user_id;
		$fresh     = get_userdata( $user_id );
		$candidate = $user_id > 0 ? wpagent_core_mutator_created_user_candidate( $user_id ) : null;
		$verified  = is_array( $candidate ) && $fresh instanceof WP_User
			&& $fresh->user_login === $username
			&& $fresh->user_email === $email
			&& array( $role ) === array_values( $fresh->roles )
			&& wpagent_core_mutator_refresh_lock( 'user-create', $lock_identity, $lock )
			&& wpagent_core_mutator_lock_is_owned( 'user-create', $lock_identity, $lock )
			&& current_user_can( 'create_users' );
		if ( ! $verified ) {
			if ( wpagent_core_mutator_delete_created_user_if_current( $candidate ) ) {
				return wpagent_user_create_unverified();
			}
			return wpagent_user_create_recovery_required( $user_id );
		}

		$result = array(
			'user_id'            => $user_id,
			'username'           => $fresh->user_login,
			'email'              => $fresh->user_email,
			'role'               => $role,
			'password_generated' => $password_generated,
			'verified'           => true,
		);
		if ( $password_generated ) {
			$result['generated_password'] = $password;
		}
		return $result;
	} finally {
		wpagent_core_mutator_release_lock( 'user-create', $lock_identity, $lock );
	}
}
