<?php
/** Manual-only ACF value change request with validation and recovery context. */

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

function wpagent_ability_update_acf_field(): array {
	return array(
		'label' => __( 'ACF-Feldänderung prüfen', 'wp-agent-companion' ),
		'description' => __( 'Nimmt keine Remote-Änderung an ACF-Feldwerten vor, weil ACF keine atomare Compare-and-Write-Operation gegen parallele Bearbeitungen bereitstellt. Prüfe und speichere die Änderung im WordPress-Backend.', 'wp-agent-companion' ),
		'category' => 'wp-agent',
		'input_schema' => array(
			'type' => 'object',
			'properties' => array(
				'post_id' => array( 'type' => 'integer', 'minimum' => 1 ),
				'object_type' => array( 'type' => 'string', 'enum' => array( 'post', 'user', 'term', 'comment', 'options' ), 'maxLength' => 8 ),
				'object_id' => array( 'type' => array( 'integer', 'string' ), 'maxLength' => 120 ),
				'field_key' => array( 'type' => 'string', 'pattern' => '^field_[A-Za-z0-9_-]{1,120}$', 'maxLength' => 127 ),
				'expected_hash' => array( 'type' => 'string', 'pattern' => '^[a-f0-9]{64}$', 'maxLength' => 64 ),
				'value' => wpagent_acf_value_json_envelope_schema(),
			),
			'required' => array( 'field_key', 'expected_hash', 'value' ),
			'maxProperties' => 6,
			'additionalProperties' => false,
		),
		'output_schema' => array( 'type' => 'object', 'properties' => array(), 'additionalProperties' => false ),
		'execute_callback' => 'wpagent_exec_update_acf_field',
		'permission_callback' => 'wpagent_perm_acf_values',
		'meta' => array( 'show_in_rest' => true, 'annotations' => array( 'readonly' => false, 'destructive' => false, 'idempotent' => false, 'manual_only' => true ) ),
	);
}

/** @return WP_Error */
function wpagent_acf_value_manual_write_only(): WP_Error {
	return new WP_Error(
		'wpagent_acf_value_manual_write_required',
		__( 'ACF stellt für Feldwerte keine nachweisbar atomare Compare-and-Write-Operation bereit. WPAgently ändert diesen Wert deshalb nicht. Speichere die Änderung im WordPress-Backend und lies den Feldwert anschließend neu.', 'wp-agent-companion' ),
		array( 'status' => 409, 'manual_only' => true )
	);
}

/** @return WP_Error */
function wpagent_exec_update_acf_field( $input ) {
	// The REST/MCP schema rejects unbounded or malformed envelopes before this
	// callback. Do not parse the value, read ACF state, or take an application
	// lock here: none provides a provider-atomic conditional mutation.
	return wpagent_acf_value_manual_write_only();
}

/** @param array{exists:bool,raw:mixed,side_effects:array<string,mixed>,state_hash:string} $snapshot @return true|WP_Error */
function wpagent_acf_value_rollback( array $context, array $field, array $snapshot ) {
	try {
		if ( $snapshot['exists'] ) update_field( (string) $field['key'], $snapshot['raw'], $context['acf_object_id'] );
		else delete_field( (string) $field['key'], $context['acf_object_id'] );
		wpagent_acf_value_restore_side_effects( $context, $snapshot['side_effects'] );
	} catch ( Throwable $error ) {
		// ACF and third-party hooks can throw after persistence. Verify below.
	}
	$fresh = wpagent_acf_value_state( $context, $field );
	if ( ! wpagent_acf_value_side_effect_state_is_safe( $fresh['side_effects'] ) || ! hash_equals( $snapshot['state_hash'], $fresh['state_hash'] ) ) {
		return new WP_Error( 'wpagent_acf_value_rollback_unverified', __( 'Der vorherige ACF-Feldwert konnte nicht verifiziert wiederhergestellt werden. Prüfe das Zielobjekt sofort manuell.', 'wp-agent-companion' ), array( 'status' => 500, 'rollback_verified' => false ) );
	}
	return true;
}

/** @param array{exists:bool,raw:mixed,side_effects:array<string,mixed>,state_hash:string} $snapshot @return WP_Error */
function wpagent_acf_value_fail_after_rollback( array $context, array $field, array $snapshot ): WP_Error {
	$rollback = wpagent_acf_value_rollback( $context, $field, $snapshot );
	if ( is_wp_error( $rollback ) ) return $rollback;
	return new WP_Error( 'wpagent_acf_value_unverified', __( 'ACF hat den neuen Feldwert nicht exakt bestätigt. Der vorherige Zustand wurde verifiziert wiederhergestellt.', 'wp-agent-companion' ), array( 'status' => 502, 'rollback_verified' => true ) );
}

/** Compare ACF raw values while accounting for key normalization in compound fields. */
function wpagent_acf_values_match( $expected, $actual, ?array $field = null ): bool {
	if ( is_array( $expected ) || is_array( $actual ) ) {
		if ( is_array( $field ) ) $actual = wpagent_acf_normalize_actual_shape_for_compare( $expected, $actual, $field );
		return wp_json_encode( wpagent_acf_normalize_for_compare( $actual ) ) === wp_json_encode( wpagent_acf_normalize_for_compare( $expected ) );
	}
	return (string) $actual === (string) $expected;
}

/** @return mixed */
function wpagent_acf_normalize_actual_shape_for_compare( $expected, $actual, array $field ) {
	if ( ! is_array( $expected ) || ! is_array( $actual ) ) return $actual;
	$type = (string) ( $field['type'] ?? '' );
	if ( 'group' === $type ) return wpagent_acf_normalize_compound_row_for_compare( $expected, $actual, $field['sub_fields'] ?? array() );
	if ( 'repeater' === $type && array_is_list( $expected ) && array_is_list( $actual ) && count( $expected ) === count( $actual ) ) {
		$result = array(); foreach ( $expected as $index => $row ) $result[] = wpagent_acf_normalize_compound_row_for_compare( $row, $actual[ $index ], $field['sub_fields'] ?? array() ); return $result;
	}
	if ( 'flexible_content' === $type && array_is_list( $expected ) && array_is_list( $actual ) && count( $expected ) === count( $actual ) ) {
		$result = array();
		foreach ( $expected as $index => $row ) {
			$name = is_array( $row ) ? (string) ( $row['acf_fc_layout'] ?? '' ) : '';
			$layout = wpagent_acf_value_layout( $field, $name );
			$result[] = $layout ? wpagent_acf_normalize_compound_row_for_compare( $row, $actual[ $index ], $layout['sub_fields'] ?? array() ) : $actual[ $index ];
		}
		return $result;
	}
	return $actual;
}

/** @return mixed */
function wpagent_acf_normalize_compound_row_for_compare( $expected, $actual, $sub_fields ) {
	if ( ! is_array( $expected ) || ! is_array( $actual ) ) return $actual;
	$result = array();
	foreach ( $expected as $name => $value ) {
		if ( 'acf_fc_layout' === $name ) { $result[ $name ] = $actual[ $name ] ?? null; continue; }
		$child = null;
		foreach ( is_array( $sub_fields ) ? $sub_fields : array() as $candidate ) if ( is_array( $candidate ) && ( $name === (string) ( $candidate['name'] ?? '' ) || $name === (string) ( $candidate['key'] ?? '' ) ) ) { $child = $candidate; break; }
		if ( ! $child ) continue;
		$actual_key = array_key_exists( (string) $child['key'], $actual ) ? (string) $child['key'] : (string) $child['name'];
		if ( array_key_exists( $actual_key, $actual ) ) $result[ (string) $child['name'] ] = wpagent_acf_normalize_actual_shape_for_compare( $value, $actual[ $actual_key ], $child );
	}
	return $result;
}

/** @return mixed */
function wpagent_acf_normalize_for_compare( $value ) {
	if ( is_object( $value ) ) $value = get_object_vars( $value );
	if ( is_array( $value ) ) {
		$result = array(); foreach ( $value as $key => $item ) $result[ $key ] = wpagent_acf_normalize_for_compare( $item );
		if ( ! array_is_list( $result ) ) ksort( $result, SORT_STRING );
		return $result;
	}
	return is_scalar( $value ) || null === $value ? (string) $value : $value;
}
