<?php
/** Verified Gravity Forms entry updates, status changes, and deletion. */

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

/** @return array<string,mixed> */
function wpagent_gravity_entry_write_output_schema(): array {
	return array(
		'type'                 => 'object',
		'additionalProperties' => false,
		'properties'           => array(
			'entry'    => wpagent_gravity_entry_output_schema(),
			'verified' => array( 'type' => 'boolean' ),
		),
	);
}

/** @return array<string,mixed> */
function wpagent_gravity_entry_expected_hash_schema(): array {
	return array( 'type' => 'string', 'pattern' => '^[a-f0-9]{64}$' );
}

/** @param mixed $input @return string|WP_Error */
function wpagent_gravity_entry_expected_hash( $input ) {
	$expected_hash = is_array( $input ) && is_string( $input['expected_hash'] ?? null ) ? $input['expected_hash'] : '';
	if ( ! preg_match( '/^[a-f0-9]{64}$/D', $expected_hash ) ) {
		return new WP_Error( 'wpagent_gravity_entry_bad_hash', __( 'expected_hash muss ein SHA-256-Hash des vollständig gelesenen Eintragszustands sein.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	return $expected_hash;
}

/** @return WP_Error */
function wpagent_gravity_entry_conflict( array $entry ): WP_Error {
	return new WP_Error( 'wpagent_gravity_entry_conflict', __( 'Der Gravity-Forms-Eintrag wurde seit dem Lesen verändert. Lies ihn neu und wiederhole den Vorgang.', 'wp-agent-companion' ), array( 'status' => 409, 'current_hash' => wpagent_gravity_entry_state_hash( $entry ) ) );
}

/** @return WP_Error */
function wpagent_gravity_entry_recovery_required( int $entry_id, string $message, array $data = array() ): WP_Error {
	$data = array_merge( array( 'status' => 500, 'recovery_required' => true, 'rollback_verified' => false ), $data );
	if ( $entry_id > 0 && is_callable( array( 'GFAPI', 'get_entry' ) ) ) {
		try {
			$current = GFAPI::get_entry( $entry_id );
			if ( is_array( $current ) ) {
				$data['current_hash'] = wpagent_gravity_entry_state_hash( $current );
			}
		} catch ( Throwable $error ) {
			// A recovery response must remain available when the provider read fails.
		}
	}
	return new WP_Error( 'wpagent_gravity_entry_recovery_required', $message, $data );
}

/** @return WP_Error */
function wpagent_gravity_entry_lock_lost_before_write(): WP_Error {
	return new WP_Error( 'wpagent_gravity_entry_lock_lost', __( 'Die eintragsbezogene Gravity-Forms-Sperre ging vor dem Schreiben verloren. Es wurde nichts geändert.', 'wp-agent-companion' ), array( 'status' => 409 ) );
}

/** @return WP_Error */
function wpagent_gravity_entry_lock_lost_after_write( int $entry_id ): WP_Error {
	return wpagent_gravity_entry_recovery_required( $entry_id, __( 'Die eintragsbezogene Gravity-Forms-Sperre ging nach Beginn des Schreibvorgangs verloren. Wiederhole den Vorgang nicht automatisch und prüfe den Eintragszustand manuell.', 'wp-agent-companion' ), array( 'lock_lost' => true ) );
}

/** @return array<string,mixed>|WP_Error */
function wpagent_gravity_entry_checkpoint( int $entry_id, string $lock, string $expected_hash, bool $has_prior_writes ) {
	if ( ! wpagent_gravity_refresh_entry_lock( $entry_id, $lock ) ) {
		return $has_prior_writes
			? wpagent_gravity_entry_lock_lost_after_write( $entry_id )
			: wpagent_gravity_entry_lock_lost_before_write();
	}
	try {
		$current = GFAPI::get_entry( $entry_id );
	} catch ( Throwable $error ) {
		return $has_prior_writes
			? wpagent_gravity_entry_recovery_required( $entry_id, __( 'Der Gravity-Forms-Eintrag konnte während eines mehrteiligen Schreibvorgangs nicht erneut gelesen werden. Wiederhole ihn nicht automatisch.', 'wp-agent-companion' ) )
			: new WP_Error( 'wpagent_gravity_entry_not_found', __( 'Gravity-Forms-Eintrag nicht gefunden.', 'wp-agent-companion' ), array( 'status' => 404 ) );
	}
	if ( ! wpagent_gravity_entry_lock_is_owned( $entry_id, $lock ) ) {
		return $has_prior_writes
			? wpagent_gravity_entry_lock_lost_after_write( $entry_id )
			: wpagent_gravity_entry_lock_lost_before_write();
	}
	if ( is_wp_error( $current ) || ! is_array( $current ) ) {
		return $has_prior_writes
			? wpagent_gravity_entry_recovery_required( $entry_id, __( 'Der Gravity-Forms-Eintrag ist während eines mehrteiligen Schreibvorgangs nicht mehr lesbar. Wiederhole ihn nicht automatisch.', 'wp-agent-companion' ) )
			: new WP_Error( 'wpagent_gravity_entry_not_found', __( 'Gravity-Forms-Eintrag nicht gefunden.', 'wp-agent-companion' ), array( 'status' => 404 ) );
	}
	if ( ! hash_equals( $expected_hash, wpagent_gravity_entry_state_hash( $current ) ) ) {
		return $has_prior_writes
			? wpagent_gravity_entry_recovery_required( $entry_id, __( 'Der Gravity-Forms-Eintrag wurde während eines mehrteiligen Schreibvorgangs extern verändert. Es erfolgt kein automatisches Rollback.', 'wp-agent-companion' ) )
			: wpagent_gravity_entry_conflict( $current );
	}
	return $current;
}

/** @param array<string,mixed> $expected @return array<string,mixed>|WP_Error */
function wpagent_gravity_entry_post_write_readback( int $entry_id, string $lock, array $expected ) {
	if ( ! wpagent_gravity_entry_lock_is_owned( $entry_id, $lock ) ) {
		return wpagent_gravity_entry_lock_lost_after_write( $entry_id );
	}
	try {
		$current = GFAPI::get_entry( $entry_id );
	} catch ( Throwable $error ) {
		return wpagent_gravity_entry_recovery_required( $entry_id, __( 'Die Gravity-Forms-Eintragsänderung konnte nicht zurückgelesen werden. Wiederhole sie nicht automatisch.', 'wp-agent-companion' ) );
	}
	if ( ! wpagent_gravity_entry_lock_is_owned( $entry_id, $lock ) ) {
		return wpagent_gravity_entry_lock_lost_after_write( $entry_id );
	}
	if ( is_wp_error( $current ) || ! is_array( $current ) || ! wpagent_gravity_subset_matches( $expected, $current ) ) {
		return wpagent_gravity_entry_recovery_required( $entry_id, __( 'Die Gravity-Forms-Eintragsänderung konnte nicht vollständig verifiziert werden. Es erfolgt kein automatisches Rollback.', 'wp-agent-companion' ) );
	}
	return $current;
}

/** @return WP_Error */
function wpagent_gravity_entry_write_failure( int $entry_id, string $lock, string $previous_hash, bool $has_prior_writes, WP_Error $failure ): WP_Error {
	if ( ! wpagent_gravity_entry_lock_is_owned( $entry_id, $lock ) ) {
		return wpagent_gravity_entry_lock_lost_after_write( $entry_id );
	}
	try {
		$current = GFAPI::get_entry( $entry_id );
	} catch ( Throwable $error ) {
		return wpagent_gravity_entry_recovery_required( $entry_id, __( 'Ein fehlgeschlagener Gravity-Forms-Schreibvorgang hinterließ einen nicht lesbaren Zustand. Wiederhole ihn nicht automatisch.', 'wp-agent-companion' ) );
	}
	if ( ! wpagent_gravity_entry_lock_is_owned( $entry_id, $lock ) || is_wp_error( $current ) || ! is_array( $current ) ) {
		return wpagent_gravity_entry_recovery_required( $entry_id, __( 'Ein fehlgeschlagener Gravity-Forms-Schreibvorgang hinterließ einen unklaren Zustand. Wiederhole ihn nicht automatisch.', 'wp-agent-companion' ) );
	}
	if ( ! $has_prior_writes && hash_equals( $previous_hash, wpagent_gravity_entry_state_hash( $current ) ) ) {
		return $failure;
	}
	return wpagent_gravity_entry_recovery_required( $entry_id, __( 'Ein fehlgeschlagener Gravity-Forms-Schreibvorgang könnte eine Teiländerung hinterlassen haben. Es erfolgt kein automatisches Rollback.', 'wp-agent-companion' ) );
}

/** @param mixed $input @return array|WP_Error */
function wpagent_exec_update_gravity_entry( $input ) {
	if ( ! wpagent_gravity_entries_available( array( 'get_entry', 'get_field', 'update_entry_property', 'update_entry_field' ) ) ) {
		return wpagent_gravity_entries_missing_error();
	}
	$input         = is_array( $input ) ? $input : array();
	$expected_hash = wpagent_gravity_entry_expected_hash( $input );
	if ( is_wp_error( $expected_hash ) ) {
		return $expected_hash;
	}
	$entry = wpagent_gravity_get_authorized_entry( $input, 'gravityforms_edit_entries' );
	if ( is_wp_error( $entry ) ) {
		return $entry;
	}
	$properties = array();
	foreach ( array( 'is_read', 'is_starred' ) as $key ) {
		if ( array_key_exists( $key, $input ) ) {
			$properties[ $key ] = (bool) $input[ $key ];
		}
	}
	$field_updates = is_array( $input['field_updates'] ?? null ) ? $input['field_updates'] : array();
	if ( count( $field_updates ) > 100 ) {
		return new WP_Error( 'wpagent_gravity_entry_too_many_fields', __( 'Pro Aufruf können höchstens 100 Eintragsfelder geändert werden.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	if ( ! $properties && ! $field_updates ) {
		return new WP_Error( 'wpagent_no_fields', __( 'Keine zu aktualisierende Eintragseigenschaft übergeben.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	$seen_fields = array();
	foreach ( $field_updates as $update ) {
		$field_id = is_array( $update ) ? (string) ( $update['field_id'] ?? '' ) : '';
		if ( ! is_array( $update ) || ! array_key_exists( 'value', $update ) || ! ( null === $update['value'] || is_scalar( $update['value'] ) ) || ! preg_match( '/^[1-9]\d*(?:\.\d+)?$/', $field_id ) || isset( $seen_fields[ $field_id ] ) ) {
			return new WP_Error( 'wpagent_gravity_entry_bad_field', __( 'Jede field_id muss eine eindeutige vorhandene numerische Feld- oder Input-ID sein.', 'wp-agent-companion' ), array( 'status' => 400 ) );
		}
		try {
			$field = GFAPI::get_field( absint( $entry['form_id'] ?? 0 ), $field_id );
		} catch ( Throwable $error ) {
			$field = false;
		}
		if ( is_wp_error( $field ) || false === $field ) {
			return new WP_Error( 'wpagent_gravity_entry_bad_field', __( 'Jede field_id muss eine eindeutige vorhandene numerische Feld- oder Input-ID sein.', 'wp-agent-companion' ), array( 'status' => 400 ) );
		}
		$seen_fields[ $field_id ] = true;
	}

	$entry_id = absint( $entry['id'] ?? 0 );
	$lock     = wpagent_gravity_acquire_entry_lock( $entry_id );
	if ( is_wp_error( $lock ) ) {
		return $lock;
	}
	try {
		$fresh = wpagent_gravity_entry_checkpoint( $entry_id, $lock, $expected_hash, false );
		if ( is_wp_error( $fresh ) ) {
			return $fresh;
		}
		$current_hash    = wpagent_gravity_entry_state_hash( $fresh );
		$has_prior_writes = false;
		foreach ( $properties as $key => $value ) {
			$fresh = wpagent_gravity_entry_checkpoint( $entry_id, $lock, $current_hash, $has_prior_writes );
			if ( is_wp_error( $fresh ) ) {
				return $fresh;
			}
			try {
				$result = GFAPI::update_entry_property( $entry_id, $key, $value );
			} catch ( Throwable $error ) {
				$result = false;
			}
			if ( is_wp_error( $result ) || true !== $result ) {
				return wpagent_gravity_entry_write_failure( $entry_id, $lock, $current_hash, $has_prior_writes, new WP_Error( 'wpagent_gravity_entry_update_failed', __( 'Der Gravity-Forms-Eintrag konnte nicht vollständig aktualisiert werden.', 'wp-agent-companion' ), array( 'status' => 500 ) ) );
			}
			$fresh = wpagent_gravity_entry_post_write_readback( $entry_id, $lock, array( $key => $value ) );
			if ( is_wp_error( $fresh ) ) {
				return $fresh;
			}
			$current_hash     = wpagent_gravity_entry_state_hash( $fresh );
			$has_prior_writes = true;
		}
		foreach ( $field_updates as $update ) {
			$field_id = (string) $update['field_id'];
			$value    = $update['value'] ?? null;
			$fresh = wpagent_gravity_entry_checkpoint( $entry_id, $lock, $current_hash, $has_prior_writes );
			if ( is_wp_error( $fresh ) ) {
				return $fresh;
			}
			try {
				$result = GFAPI::update_entry_field( $entry_id, $field_id, $value );
			} catch ( Throwable $error ) {
				$result = false;
			}
			if ( is_wp_error( $result ) || true !== $result ) {
				return wpagent_gravity_entry_write_failure( $entry_id, $lock, $current_hash, $has_prior_writes, new WP_Error( 'wpagent_gravity_entry_update_failed', __( 'Der Gravity-Forms-Eintrag konnte nicht vollständig aktualisiert werden.', 'wp-agent-companion' ), array( 'status' => 500 ) ) );
			}
			$fresh = wpagent_gravity_entry_post_write_readback( $entry_id, $lock, array( $field_id => $value ) );
			if ( is_wp_error( $fresh ) ) {
				return $fresh;
			}
			$current_hash     = wpagent_gravity_entry_state_hash( $fresh );
			$has_prior_writes = true;
		}
		return array( 'entry' => wpagent_gravity_entry_payload( $fresh, true ), 'verified' => true );
	} finally {
		wpagent_gravity_release_entry_lock( $entry_id, $lock );
	}
}

function wpagent_ability_update_gravity_entry(): array {
	$value_schema = array( 'type' => array( 'string', 'integer', 'number', 'boolean', 'null' ) );
	return array(
		'label'               => __( 'Gravity-Forms-Eintrag aktualisieren', 'wp-agent-companion' ),
		'description'         => __( 'Aktualisiert Lesestatus, Markierung oder vorhandene Feldwerte mit eintragsbezogenem Zustandsvergleich und Sperre. Unklare Teiländerungen werden nicht automatisch überschrieben.', 'wp-agent-companion' ),
		'category'            => 'wp-agent',
		'input_schema'        => array(
			'type'                 => 'object',
			'additionalProperties' => false,
			'properties'           => array(
				'entry_id'              => array( 'type' => 'integer', 'minimum' => 1 ),
				'expected_hash'          => wpagent_gravity_entry_expected_hash_schema(),
				'is_read'               => array( 'type' => 'boolean' ),
				'is_starred'            => array( 'type' => 'boolean' ),
				'field_updates'         => array(
					'type'     => 'array',
					'minItems' => 1,
					'maxItems' => 100,
					'items'    => array(
						'type'                 => 'object',
						'additionalProperties' => false,
						'properties'           => array(
							'field_id' => array( 'type' => 'string', 'pattern' => '^[1-9]\\d*(?:\\.\\d+)?$' ),
							'value'    => $value_schema,
						),
						'required'             => array( 'field_id', 'value' ),
					),
				),
			),
			'required'             => array( 'entry_id', 'expected_hash' ),
		),
		'output_schema'       => wpagent_gravity_entry_write_output_schema(),
		'execute_callback'    => 'wpagent_exec_update_gravity_entry',
		'permission_callback' => 'wpagent_perm_gravity_entries_edit',
		'meta'                => array( 'show_in_rest' => true, 'annotations' => array( 'readonly' => false, 'destructive' => false, 'idempotent' => false ) ),
	);
}

function wpagent_ability_set_gravity_entry_status(): array {
	return array(
		'label'               => __( 'Gravity-Forms-Eintragsstatus setzen', 'wp-agent-companion' ),
		'description'         => __( 'Setzt einen Gravity-Forms-Eintrag mit eintragsbezogenem Zustandsvergleich und Sperre auf aktiv, Spam oder Papierkorb. Unklare Ergebnisse werden nicht automatisch überschrieben.', 'wp-agent-companion' ),
		'category'            => 'wp-agent',
		'input_schema'        => array(
			'type'                 => 'object',
			'additionalProperties' => false,
			'properties'           => array(
				'entry_id'              => array( 'type' => 'integer', 'minimum' => 1 ),
				'status'                => array( 'type' => 'string', 'enum' => array( 'active', 'spam', 'trash' ) ),
				'expected_hash'          => wpagent_gravity_entry_expected_hash_schema(),
			),
			'required'             => array( 'entry_id', 'status', 'expected_hash' ),
		),
		'output_schema'       => wpagent_gravity_entry_write_output_schema(),
		'execute_callback'    => 'wpagent_exec_set_gravity_entry_status',
		'permission_callback' => 'wpagent_perm_gravity_entries_edit',
		'meta'                => array( 'show_in_rest' => true, 'annotations' => array( 'readonly' => false, 'destructive' => true, 'idempotent' => false ) ),
	);
}

/** @param mixed $input @return array|WP_Error */
function wpagent_exec_set_gravity_entry_status( $input ) {
	if ( ! wpagent_gravity_entries_available( array( 'get_entry', 'update_entry_property' ) ) ) {
		return wpagent_gravity_entries_missing_error();
	}
	$input         = is_array( $input ) ? $input : array();
	$expected_hash = wpagent_gravity_entry_expected_hash( $input );
	if ( is_wp_error( $expected_hash ) ) {
		return $expected_hash;
	}
	$entry = wpagent_gravity_get_authorized_entry( $input, 'gravityforms_edit_entries' );
	if ( is_wp_error( $entry ) ) {
		return $entry;
	}
	$status = (string) ( $input['status'] ?? '' );
	if ( ! in_array( $status, array( 'active', 'spam', 'trash' ), true ) ) {
		return new WP_Error( 'wpagent_gravity_entry_bad_status', __( 'status muss active, spam oder trash sein.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	$entry_id = absint( $entry['id'] ?? 0 );
	$lock     = wpagent_gravity_acquire_entry_lock( $entry_id );
	if ( is_wp_error( $lock ) ) {
		return $lock;
	}
	try {
		$fresh = wpagent_gravity_entry_checkpoint( $entry_id, $lock, $expected_hash, false );
		if ( is_wp_error( $fresh ) ) {
			return $fresh;
		}
		try {
			$result = GFAPI::update_entry_property( $entry_id, 'status', $status );
		} catch ( Throwable $error ) {
			$result = false;
		}
		if ( is_wp_error( $result ) || true !== $result ) {
			return wpagent_gravity_entry_write_failure( $entry_id, $lock, $expected_hash, false, new WP_Error( 'wpagent_gravity_entry_status_failed', __( 'Der Gravity-Forms-Eintragsstatus konnte nicht geändert werden.', 'wp-agent-companion' ), array( 'status' => 500 ) ) );
		}
		$readback = wpagent_gravity_entry_post_write_readback( $entry_id, $lock, array( 'status' => $status ) );
		if ( is_wp_error( $readback ) ) {
			return $readback;
		}
		return array( 'entry' => wpagent_gravity_entry_payload( $readback, true ), 'verified' => true );
	} finally {
		wpagent_gravity_release_entry_lock( $entry_id, $lock );
	}
}

function wpagent_ability_delete_gravity_entry(): array {
	return array(
		'label'               => __( 'Gravity-Forms-Eintrag dauerhaft löschen', 'wp-agent-companion' ),
		'description'         => __( 'Das permanente Löschen von Gravity-Forms-Einträgen ist gesperrt, bis der Anbieter eine atomare Compare-and-Delete-Primitive bereitstellt. confirm=true bleibt zwingend erforderlich.', 'wp-agent-companion' ),
		'category'            => 'wp-agent',
		'input_schema'        => array(
			'type'                 => 'object',
			'additionalProperties' => false,
			'properties'           => array(
				'entry_id'              => array( 'type' => 'integer', 'minimum' => 1 ),
				'confirm'               => array( 'type' => 'boolean' ),
				'expected_date_updated' => array( 'type' => 'string', 'maxLength' => 32 ),
			),
			'required'             => array( 'entry_id', 'confirm' ),
		),
		'output_schema'       => array(
			'type'                 => 'object',
			'additionalProperties' => false,
			'properties'           => array( 'entry_id' => array( 'type' => 'integer' ), 'deleted' => array( 'type' => 'boolean' ) ),
		),
		'execute_callback'    => 'wpagent_exec_delete_gravity_entry',
		'permission_callback' => 'wpagent_perm_gravity_entries_delete',
		'meta'                => array( 'show_in_rest' => true, 'annotations' => array( 'readonly' => false, 'destructive' => true, 'idempotent' => false, 'manual_only' => true ) ),
	);
}

/** @return WP_Error */
function wpagent_gravity_entry_atomic_delete_unavailable(): WP_Error {
	return new WP_Error( 'wpagent_gravity_entry_atomic_delete_unavailable', __( 'Gravity Forms stellt keine atomare Compare-and-Delete-Primitive für Einträge bereit. Das permanente Löschen bleibt deshalb ohne Datenmutation gesperrt.', 'wp-agent-companion' ), array( 'status' => 409 ) );
}

/** @param mixed $input @return array|WP_Error */
function wpagent_exec_delete_gravity_entry( $input ) {
	$input = is_array( $input ) ? $input : array();
	if ( true !== ( $input['confirm'] ?? false ) ) {
		return new WP_Error( 'wpagent_confirmation_required', __( 'Dauerhaftes Löschen erfordert confirm=true.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	return wpagent_gravity_entry_atomic_delete_unavailable();
}
