<?php
/** Gravity Forms lifecycle writes with CAS, per-form locks and recovery. */

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

/** @return array<string,mixed> */
function wpagent_gravity_write_output_schema(): array {
	return wpagent_forms_public_object_schema( array( 'form' => wpagent_gravity_form_output_schema(), 'verified' => array( 'type' => 'boolean' ) ), array( 'form', 'verified' ) );
}

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

/** @return WP_Error */
function wpagent_gravity_invalid_write_input(): WP_Error {
	return new WP_Error( 'wpagent_gravity_invalid_input', __( 'Die Gravity-Forms-Eingabe überschreitet die zulässigen Grenzen oder hat einen ungültigen Typ.', 'wp-agent-companion' ), array( 'status' => 400 ) );
}

function wpagent_gravity_string_length( string $value ): int {
	return function_exists( 'mb_strlen' ) ? mb_strlen( $value, 'UTF-8' ) : strlen( $value );
}

/** @param array<string,mixed> $input @return array<string,mixed>|WP_Error */
function wpagent_gravity_changes( array $input, bool $creating = false ) {
	foreach ( array( 'title' => 255, 'description' => 10000, 'submit_label' => 500 ) as $key => $limit ) {
		if ( array_key_exists( $key, $input ) && ( ! is_string( $input[ $key ] ) || wpagent_gravity_string_length( $input[ $key ] ) > $limit ) ) {
			return wpagent_gravity_invalid_write_input();
		}
	}
	foreach ( array( 'fields' => 200, 'notifications' => 100, 'confirmations' => 100 ) as $key => $limit ) {
		if ( array_key_exists( $key, $input ) && ( ! is_array( $input[ $key ] ) || count( $input[ $key ] ) > $limit ) ) {
			return wpagent_gravity_invalid_write_input();
		}
	}
	if ( isset( $input['fields'] ) && ! array_is_list( $input['fields'] ) ) {
		return wpagent_gravity_invalid_write_input();
	}
	$changes = array();
	foreach ( array( 'title', 'description', 'fields', 'notifications', 'confirmations' ) as $key ) {
		if ( array_key_exists( $key, $input ) ) {
			$changes[ $key ] = $input[ $key ];
		}
	}
	if ( array_key_exists( 'submit_label', $input ) ) {
		$submit_label = trim( $input['submit_label'] );
		if ( '' === $submit_label ) {
			return new WP_Error( 'wpagent_gravity_submit_label_required', __( 'Die Beschriftung der Absende-Schaltfläche darf nicht leer sein.', 'wp-agent-companion' ), array( 'status' => 400 ) );
		}
		$changes['button'] = array( 'type' => 'text', 'text' => sanitize_text_field( $submit_label ), 'imageUrl' => '' );
	}
	if ( $creating && '' === trim( (string) ( $changes['title'] ?? '' ) ) ) {
		return new WP_Error( 'wpagent_gravity_title_required', __( 'Ein nicht leerer Formulartitel ist erforderlich.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	if ( array_key_exists( 'title', $changes ) && '' === trim( (string) $changes['title'] ) ) {
		return new WP_Error( 'wpagent_gravity_title_required', __( 'Der Formulartitel darf nicht leer sein.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	if ( $creating && ! isset( $changes['fields'] ) ) {
		$changes['fields'] = array();
	}
	if ( array_key_exists( 'settings', $input ) && ! is_array( $input['settings'] ) ) {
		return wpagent_gravity_invalid_write_input();
	}
	$settings = $input['settings'] ?? array();
	if ( count( $settings ) > count( wpagent_gravity_setting_keys() ) ) {
		return wpagent_gravity_invalid_write_input();
	}
	$unknown_settings = array_diff( array_keys( $settings ), wpagent_gravity_setting_keys() );
	if ( ! empty( $unknown_settings ) ) {
		return new WP_Error( 'wpagent_gravity_unknown_setting', __( 'Mindestens eine Gravity-Forms-Einstellung wird nicht unterstützt.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	foreach ( wpagent_gravity_setting_keys() as $key ) {
		if ( array_key_exists( $key, $settings ) ) {
			$changes[ $key ] = $settings[ $key ];
		}
	}
	return $changes;
}

/** @param mixed $input @return string|WP_Error */
function wpagent_gravity_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_bad_hash', __( 'expected_hash muss ein SHA-256-Hash des vollständig gelesenen Formularzustands sein.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	return $expected_hash;
}

/** @param array<string,mixed> $expected @return array{form:array<string,mixed>,verified:bool}|WP_Error */
function wpagent_gravity_readback( int $form_id, array $expected ) {
	if ( ! is_callable( array( 'GFAPI', 'get_form' ) ) ) {
		return wpagent_gravity_missing_error();
	}
	try {
		$fresh = GFAPI::get_form( $form_id );
	} catch ( Throwable $error ) {
		$fresh = false;
	}
	if ( ! is_array( $fresh ) ) {
		return new WP_Error( 'wpagent_gravity_readback_failed', __( 'Das Gravity-Forms-Formular wurde gespeichert, konnte aber nicht erneut gelesen werden.', 'wp-agent-companion' ), array( 'status' => 500 ) );
	}
	$remaining_expected = $expected;
	$verified           = true;
	foreach ( array( 'fields', 'notifications', 'confirmations' ) as $collection ) {
		if ( ! array_key_exists( $collection, $remaining_expected ) ) {
			continue;
		}
		if ( ! wpagent_gravity_collection_matches( $remaining_expected[ $collection ], $fresh[ $collection ] ?? null ) ) {
			$verified = false;
			break;
		}
		unset( $remaining_expected[ $collection ] );
	}
	if ( $verified ) {
		$verified = wpagent_gravity_subset_matches( $remaining_expected, $fresh );
	}
	$payload = wpagent_gravity_form_payload( $fresh );
	return is_wp_error( $payload ) ? $payload : array( 'form' => $payload, 'verified' => $verified );
}

/** @return WP_Error */
function wpagent_gravity_recovery_required( int $form_id, string $message, array $data = array() ): WP_Error {
	$data = array_merge( array( 'status' => 500, 'rollback_verified' => false, 'recovery_required' => true ), $data );
	if ( $form_id > 0 ) {
		try {
			$current = GFAPI::get_form( $form_id );
			if ( is_array( $current ) ) {
				$data['current_hash'] = wpagent_gravity_form_state_hash( $current );
			}
		} catch ( Throwable $error ) {
			// The safe recovery response intentionally omits an unreadable state.
		}
	}
	return new WP_Error( 'wpagent_gravity_recovery_required', $message, $data );
}

/** @return WP_Error */
function wpagent_gravity_lock_lost_before_write(): WP_Error {
	return new WP_Error( 'wpagent_gravity_lock_lost', __( 'Die formularbezogene 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_lock_lost_after_write( int $form_id ): WP_Error {
	return wpagent_gravity_recovery_required( $form_id, __( 'Die formularbezogene Gravity-Forms-Sperre ging nach Beginn des Schreibvorgangs verloren. Wiederhole den Vorgang nicht automatisch und prüfe den Formularzustand manuell.', 'wp-agent-companion' ), array( 'lock_lost' => true ) );
}

/** @return WP_Error */
function wpagent_gravity_cleanup_unverified_form( int $form_id, WP_Error $failure ): WP_Error {
	return wpagent_gravity_recovery_required( $form_id, __( 'Das nicht vollständig verifizierte Gravity-Forms-Formular wird nicht automatisch entfernt, weil Gravity Forms keine atomare Compare-and-Delete-Primitive bereitstellt. Prüfe den Formularzustand manuell.', 'wp-agent-companion' ), array( 'form_id' => $form_id, 'cause' => $failure->get_error_code() ) );
}

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

/** @return WP_Error */
function wpagent_gravity_manual_update_required( int $form_id, string $current_hash ): WP_Error {
	return new WP_Error( 'wpagent_gravity_manual_update_required', __( 'Gravity Forms stellt keine atomare Compare-and-Save-Primitive für Formulare bereit. Die öffentliche Aktualisierung bleibt deshalb ohne Datenmutation gesperrt.', 'wp-agent-companion' ), array( 'status' => 409, 'form_id' => $form_id, 'current_hash' => $current_hash, 'manual_only' => true ) );
}

function wpagent_ability_create_gravity_form(): array {
	return array(
		'label' => __( 'Gravity-Forms-Formular erstellen', 'wp-agent-companion' ),
		'description' => __( 'Erstellt ein Gravity-Forms-Formular über GFAPI::add_form() und fordert bei einem nicht verifizierbaren Ergebnis eine manuelle Zustandsprüfung an.', 'wp-agent-companion' ),
		'category' => 'wp-agent',
		'input_schema' => array( 'type' => 'object', 'additionalProperties' => false, 'properties' => wpagent_gravity_write_properties_schema(), 'required' => array( 'title' ) ),
		'output_schema' => wpagent_gravity_write_output_schema(),
		'execute_callback' => 'wpagent_exec_create_gravity_form',
		'permission_callback' => 'wpagent_perm_gravity_create',
		'meta' => array( 'show_in_rest' => true, 'annotations' => array( 'readonly' => false, 'destructive' => false, 'idempotent' => false ) ),
	);
}

/** @param mixed $input @return array|WP_Error */
function wpagent_exec_create_gravity_form( $input ) {
	if ( ! wpagent_gravity_available() || ! is_callable( array( 'GFAPI', 'add_form' ) ) ) {
		return wpagent_gravity_missing_error();
	}
	if ( ! current_user_can( 'gravityforms_create_form' ) ) {
		return new WP_Error( 'wpagent_forbidden', __( 'Keine Berechtigung zum Erstellen von Gravity-Forms-Formularen.', 'wp-agent-companion' ), array( 'status' => 403 ) );
	}
	$changes = wpagent_gravity_changes( is_array( $input ) ? $input : array(), true );
	if ( is_wp_error( $changes ) ) {
		return $changes;
	}
	try {
		$form_id = GFAPI::add_form( $changes );
	} catch ( Throwable $error ) {
		return wpagent_gravity_recovery_required( 0, __( 'Gravity Forms lieferte beim Erstellen keinen verlässlichen Zustand. Wiederhole den Vorgang nicht automatisch und prüfe die Formularliste manuell.', 'wp-agent-companion' ) );
	}
	if ( is_wp_error( $form_id ) || absint( $form_id ) <= 0 ) {
		return wpagent_gravity_recovery_required( 0, __( 'Gravity Forms lieferte beim Erstellen keine verlässliche Formular-ID. Wiederhole den Vorgang nicht automatisch und prüfe die Formularliste manuell.', 'wp-agent-companion' ) );
	}
	$form_id  = absint( $form_id );
	$readback = wpagent_gravity_readback( $form_id, $changes );
	if ( ! is_wp_error( $readback ) && true === $readback['verified'] ) {
		return $readback;
	}
	$failure = is_wp_error( $readback )
		? $readback
		: new WP_Error( 'wpagent_gravity_create_unverified', __( 'Das erstellte Gravity-Forms-Formular wich vom erwarteten Zustand ab.', 'wp-agent-companion' ), array( 'status' => 500 ) );
	return wpagent_gravity_cleanup_unverified_form( $form_id, $failure );
}

function wpagent_ability_update_gravity_form(): array {
	return array(
		'label' => __( 'Gravity-Forms-Formular aktualisieren', 'wp-agent-companion' ),
		'description' => __( 'Prüft dokumentierte Eigenschaften mit vollständigem Zustandsvergleich und formularbezogener Sperre. Ohne atomare Compare-and-Save-Primitive bleibt die Aktualisierung ohne Datenmutation gesperrt.', 'wp-agent-companion' ),
		'category' => 'wp-agent',
		'input_schema' => array( 'type' => 'object', 'additionalProperties' => false, 'properties' => array_merge( array( 'form_id' => array( 'type' => 'integer', 'minimum' => 1 ), 'expected_hash' => wpagent_gravity_expected_hash_schema() ), wpagent_gravity_write_properties_schema() ), 'required' => array( 'form_id', 'expected_hash' ) ),
		'output_schema' => wpagent_gravity_write_output_schema(),
		'execute_callback' => 'wpagent_exec_update_gravity_form',
		'permission_callback' => 'wpagent_perm_gravity_edit',
		'meta' => array( 'show_in_rest' => true, 'annotations' => array( 'readonly' => false, 'destructive' => false, 'idempotent' => false, 'manual_only' => true ) ),
	);
}

/** @param mixed $input @return array|WP_Error */
function wpagent_exec_update_gravity_form( $input ) {
	if ( ! wpagent_gravity_available() ) {
		return wpagent_gravity_missing_error();
	}
	$input         = is_array( $input ) ? $input : array();
	$expected_hash = wpagent_gravity_expected_hash( $input );
	if ( is_wp_error( $expected_hash ) ) {
		return $expected_hash;
	}
	$changes = wpagent_gravity_changes( $input );
	if ( is_wp_error( $changes ) ) {
		return $changes;
	}
	if ( empty( $changes ) ) {
		return new WP_Error( 'wpagent_no_fields', __( 'Keine zu aktualisierende Formulareigenschaft übergeben.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	$form_id = absint( $input['form_id'] ?? 0 );
	$lock    = wpagent_gravity_acquire_form_lock( $form_id );
	if ( is_wp_error( $lock ) ) {
		return $lock;
	}
	try {
		$form = wpagent_gravity_get_authorized_form( $input, 'gravityforms_edit_forms' );
		if ( is_wp_error( $form ) ) {
			return $form;
		}
		$previous_hash = wpagent_gravity_form_state_hash( $form );
		if ( ! hash_equals( $previous_hash, $expected_hash ) ) {
			return new WP_Error( 'wpagent_gravity_conflict', __( 'Das Gravity-Forms-Formular wurde seit dem Lesen verändert. Lies es neu und wiederhole den Vorgang.', 'wp-agent-companion' ), array( 'status' => 409, 'current_hash' => $previous_hash ) );
		}
		if ( ! wpagent_gravity_refresh_form_lock( $form_id, $lock ) || ! wpagent_gravity_form_lock_is_owned( $form_id, $lock ) ) {
			return wpagent_gravity_lock_lost_before_write();
		}
		return wpagent_gravity_manual_update_required( $form_id, $previous_hash );
	} finally {
		wpagent_gravity_release_form_lock( $form_id, $lock );
	}
}

/** @param array<string,mixed> $source @return array<string,mixed> */
function wpagent_gravity_duplicate_expected( array $source, string $title ): array {
	$expected = array();
	foreach ( array( 'description', 'fields', 'button', 'notifications', 'confirmations' ) as $key ) {
		if ( array_key_exists( $key, $source ) ) {
			$expected[ $key ] = $source[ $key ];
		}
	}
	foreach ( wpagent_gravity_setting_keys() as $key ) {
		if ( array_key_exists( $key, $source ) ) {
			$expected[ $key ] = $source[ $key ];
		}
	}
	if ( '' !== $title ) {
		$expected['title'] = $title;
	}
	return $expected;
}

function wpagent_ability_duplicate_gravity_form(): array {
	return array(
		'label' => __( 'Gravity-Forms-Formular duplizieren', 'wp-agent-companion' ),
		'description' => __( 'Dupliziert ein Gravity-Forms-Formular mit Zustandsvergleich und formularbezogener Sperre. Nicht verifizierbare Kopien bleiben zur sicheren manuellen Prüfung erhalten.', 'wp-agent-companion' ),
		'category' => 'wp-agent',
		'input_schema' => array( 'type' => 'object', 'additionalProperties' => false, 'properties' => array( 'form_id' => array( 'type' => 'integer', 'minimum' => 1 ), 'expected_hash' => wpagent_gravity_expected_hash_schema(), 'title' => array( 'type' => 'string', 'minLength' => 1, 'maxLength' => 255 ) ), 'required' => array( 'form_id', 'expected_hash' ) ),
		'output_schema' => wpagent_gravity_write_output_schema(),
		'execute_callback' => 'wpagent_exec_duplicate_gravity_form',
		'permission_callback' => 'wpagent_perm_gravity_duplicate',
		'meta' => array( 'show_in_rest' => true, 'annotations' => array( 'readonly' => false, 'destructive' => false, 'idempotent' => false ) ),
	);
}

/** @param mixed $input @return array|WP_Error */
function wpagent_exec_duplicate_gravity_form( $input ) {
	$input         = is_array( $input ) ? $input : array();
	$expected_hash = wpagent_gravity_expected_hash( $input );
	if ( is_wp_error( $expected_hash ) ) {
		return $expected_hash;
	}
	$form_id = absint( $input['form_id'] ?? 0 );
	$lock    = wpagent_gravity_acquire_form_lock( $form_id );
	if ( is_wp_error( $lock ) ) {
		return $lock;
	}
	try {
		$source = wpagent_gravity_get_authorized_form( $input, 'gravityforms_edit_forms' );
		if ( is_wp_error( $source ) ) {
			return $source;
		}
		if ( ! current_user_can( 'gravityforms_create_form' ) ) {
			return new WP_Error( 'wpagent_forbidden', __( 'Keine Berechtigung zum Duplizieren von Gravity-Forms-Formularen.', 'wp-agent-companion' ), array( 'status' => 403 ) );
		}
		$source_hash = wpagent_gravity_form_state_hash( $source );
		if ( ! hash_equals( $source_hash, $expected_hash ) ) {
			return new WP_Error( 'wpagent_gravity_conflict', __( 'Das Gravity-Forms-Formular wurde seit dem Lesen verändert. Lies es neu und wiederhole den Vorgang.', 'wp-agent-companion' ), array( 'status' => 409, 'current_hash' => $source_hash ) );
		}
		$title = trim( (string) ( $input['title'] ?? '' ) );
		if ( '' !== $title && ( ! is_string( $input['title'] ?? null ) || wpagent_gravity_string_length( $title ) > 255 ) ) {
			return wpagent_gravity_invalid_write_input();
		}
		if ( ! is_callable( array( 'GFAPI', 'duplicate_form' ) ) || ( '' !== $title && ! is_callable( array( 'GFAPI', 'update_form_property' ) ) ) ) {
			return wpagent_gravity_missing_error();
		}
		if ( ! wpagent_gravity_refresh_form_lock( $form_id, $lock ) ) {
			return wpagent_gravity_lock_lost_before_write();
		}
		try {
			$copy_id = GFAPI::duplicate_form( $form_id );
		} catch ( Throwable $error ) {
			return wpagent_gravity_recovery_required( $form_id, __( 'Gravity Forms lieferte beim Duplizieren keinen verlässlichen Zustand. Wiederhole den Vorgang nicht automatisch und prüfe die Formularliste manuell.', 'wp-agent-companion' ) );
		}
		if ( is_wp_error( $copy_id ) || absint( $copy_id ) <= 0 || $form_id === absint( $copy_id ) ) {
			return wpagent_gravity_recovery_required( $form_id, __( 'Gravity Forms lieferte beim Duplizieren keine sichere Kopien-ID. Wiederhole den Vorgang nicht automatisch und prüfe die Formularliste manuell.', 'wp-agent-companion' ) );
		}
		$copy_id = absint( $copy_id );
		if ( ! wpagent_gravity_form_lock_is_owned( $form_id, $lock ) ) {
			return wpagent_gravity_lock_lost_after_write( $form_id );
		}
		if ( '' !== $title ) {
			if ( ! wpagent_gravity_refresh_form_lock( $form_id, $lock ) ) {
				return wpagent_gravity_lock_lost_after_write( $form_id );
			}
			try {
				$renamed = GFAPI::update_form_property( $copy_id, 'title', $title );
			} catch ( Throwable $error ) {
				$renamed = false;
			}
			if ( is_wp_error( $renamed ) || true !== $renamed ) {
				if ( ! wpagent_gravity_form_lock_is_owned( $form_id, $lock ) ) {
					return wpagent_gravity_lock_lost_after_write( $form_id );
				}
				return wpagent_gravity_cleanup_unverified_form( $copy_id, new WP_Error( 'wpagent_gravity_copy_rename_failed', __( 'Die Formularkopie konnte nicht sicher benannt werden.', 'wp-agent-companion' ), array( 'status' => 500 ) ) );
			}
		}
		$readback = wpagent_gravity_readback( $copy_id, wpagent_gravity_duplicate_expected( $source, $title ) );
		if ( ! wpagent_gravity_form_lock_is_owned( $form_id, $lock ) ) {
			return wpagent_gravity_lock_lost_after_write( $form_id );
		}
		if ( is_wp_error( $readback ) || true !== $readback['verified'] || $copy_id !== absint( $readback['form']['id'] ?? 0 ) ) {
			$failure = is_wp_error( $readback )
				? $readback
				: new WP_Error( 'wpagent_gravity_duplicate_unverified', __( 'Die Gravity-Forms-Kopie wich vom erwarteten Zustand ab.', 'wp-agent-companion' ), array( 'status' => 500 ) );
			return wpagent_gravity_cleanup_unverified_form( $copy_id, $failure );
		}
		return $readback;
	} finally {
		wpagent_gravity_release_form_lock( $form_id, $lock );
	}
}

function wpagent_ability_delete_gravity_form(): array {
	return array(
		'label' => __( 'Gravity-Forms-Formular dauerhaft löschen', 'wp-agent-companion' ),
		'description' => __( 'Das permanente Löschen von Gravity-Forms-Formularen 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( 'form_id' => array( 'type' => 'integer', 'minimum' => 1 ), 'expected_hash' => wpagent_gravity_expected_hash_schema(), 'confirm' => array( 'type' => 'boolean' ) ), 'required' => array( 'form_id', 'expected_hash', 'confirm' ) ),
		'output_schema' => wpagent_forms_public_object_schema( array( 'form_id' => array( 'type' => 'integer', 'minimum' => 1 ), 'deleted' => array( 'type' => 'boolean' ) ), array( 'form_id', 'deleted' ) ),
		'execute_callback' => 'wpagent_exec_delete_gravity_form',
		'permission_callback' => 'wpagent_perm_gravity_delete',
		'meta' => array( 'show_in_rest' => true, 'annotations' => array( 'readonly' => false, 'destructive' => true, 'idempotent' => false, 'manual_only' => true ) ),
	);
}

/** @param mixed $input @return array|WP_Error */
function wpagent_exec_delete_gravity_form( $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_atomic_delete_unavailable();
}
